From 2f3854578aa0eac8fb84b54ab93a317b2e283999 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Fri, 5 Jul 2024 10:43:31 +0100 Subject: [PATCH 001/103] appsec: Fix spurious warning --- appsec/src/extension/commands_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appsec/src/extension/commands_helpers.c b/appsec/src/extension/commands_helpers.c index 6efe3089c4..0db2a7dede 100644 --- a/appsec/src/extension/commands_helpers.c +++ b/appsec/src/extension/commands_helpers.c @@ -175,7 +175,7 @@ static dd_result _dd_command_exec(dd_conn *nonnull conn, bool check_cred, return dd_error; } if (res != dd_success && res != dd_should_block && - res != dd_should_redirect) { + res != dd_should_redirect && res != dd_should_record) { mlog(dd_log_warning, "Processing for command %.*s failed: %s", NAME_L, dd_result_to_string(res)); return res; From b9f97987e5332d61c5538b7ef0cc3fae3d6a36c5 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 27 Jun 2024 11:02:54 +0100 Subject: [PATCH 002/103] Fix appsec build of the tracer --- appsec/cmake/ddtrace.cmake | 42 ++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/appsec/cmake/ddtrace.cmake b/appsec/cmake/ddtrace.cmake index 8b4b7f87a6..5658a5ec30 100644 --- a/appsec/cmake/ddtrace.cmake +++ b/appsec/cmake/ddtrace.cmake @@ -18,13 +18,39 @@ add_custom_target(libdatadog_stamp BYPRODUCT ${LIBDATADOG_STAMP_FILE} ) -set(EXPORTS_FILE "${CMAKE_BINARY_DIR}/exports.version") +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +set(EXPORTS_FILE "${CMAKE_BINARY_DIR}/ddtrace_exports.version") add_custom_target(ddtrace_exports COMMAND bash -c "{ echo -e '{\\nglobal:'; sed 's/$/;/' '${CMAKE_SOURCE_DIR}'/../ddtrace.sym; echo -e 'local:\\n*;\\n};'; } > '${EXPORTS_FILE}'" BYPRODUCT ${EXPORTS_FILE} DEPENDS ${CMAKE_SOURCE_DIR}/../ddtrace.sym VERBATIM ) +elseif(APPLE) +set(EXPORTS_FILE "${CMAKE_BINARY_DIR}/ddtrace_exports.sym") +add_custom_target(ddtrace_exports + COMMAND bash -c "sed 's/^/_/' '${CMAKE_SOURCE_DIR}'/../ddtrace.sym > '${EXPORTS_FILE}'" + BYPRODUCT ${EXPORTS_FILE} + DEPENDS ${CMAKE_SOURCE_DIR}/../ddtrace.sym + VERBATIM +) +endif() + +file(READ "${CMAKE_SOURCE_DIR}/../VERSION" VERSION_CONTENTS) +string(STRIP "${VERSION_CONTENTS}" PHP_DDTRACE_VERSION) +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/gen_ddtrace/ext") +set(VERSION_H_PATH "${CMAKE_BINARY_DIR}/gen_ddtrace/ext/version.h") + +add_custom_command( + OUTPUT "${VERSION_H_PATH}" + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --switch= --green "Updating version.h" + COMMAND ${CMAKE_COMMAND} -E remove -f "${VERSION_H_PATH}" + COMMAND ${CMAKE_COMMAND} -E touch "${VERSION_H_PATH}" + COMMAND printf "\\#ifndef PHP_DDTRACE_VERSION\\\\n\\#define PHP_DDTRACE_VERSION \"%s\"\\\\n\\#endif" "'\"${PHP_DDTRACE_VERSION}\"'" >> "${VERSION_H_PATH}" + DEPENDS "${CMAKE_SOURCE_DIR}/../VERSION" + COMMENT "Generating version.h" +) +add_custom_target(update_version_h ALL DEPENDS "${VERSION_H_PATH}") ExternalProject_Add(components_rs_proj PREFIX ${CMAKE_BINARY_DIR}/components_rs @@ -99,8 +125,15 @@ set_target_properties(ddtrace PROPERTIES OUTPUT_NAME ddtrace DEBUG_POSTFIX "" PREFIX "") -target_compile_options(ddtrace PRIVATE -fms-extensions) -target_link_options(ddtrace PRIVATE "-Wl,--version-script=${EXPORTS_FILE}") +target_compile_options(ddtrace PRIVATE -fms-extensions -Wno-microsoft-anon-tag) +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_compile_definitions(ddtrace PRIVATE _GNU_SOURCE) + target_link_options(ddtrace PRIVATE "-Wl,--version-script=${EXPORTS_FILE}") +elseif(APPLE) + target_link_options(ddtrace PRIVATE "-exported_symbols_list" "${EXPORTS_FILE}") +else() + message(FATAL_ERROR "Only Linux and Apple supported") +endif() target_link_libraries(ddtrace PRIVATE PhpConfig components_rs ${CURL_LIBRARIES}) if(CURL_DEFINITIONS) target_compile_definitions(ddtrace PRIVATE ${CURL_DEFINITIONS}) @@ -114,7 +147,8 @@ target_include_directories(ddtrace PRIVATE ${CMAKE_SOURCE_DIR}/../ext ${CMAKE_SOURCE_DIR}/../ext/vendor ${CMAKE_SOURCE_DIR}/../ext/vendor/mt19937 + ${CMAKE_BINARY_DIR}/gen_ddtrace ) -add_dependencies(ddtrace ddtrace_exports) +add_dependencies(ddtrace ddtrace_exports update_version_h) patch_away_libc(ddtrace) From c29505367fdbd2e38e0fc1a9e0dacc3e4465e51a Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 27 Jun 2024 11:04:30 +0100 Subject: [PATCH 003/103] Improve AppSec integration tests * Support aarch64 * Show the log of the sidecar * Update dependencies * Compile rust code in the tracer in debug mode * Fix build failing when gradle finds broken symlinks * Add PHP debug images (for troubleshooting) The images are now multiarch. See https://registry.hub.docker.com/r/datadog/dd-appsec-php-ci/tags --- appsec/tests/integration/build.gradle | 57 +++++++++++++------ appsec/tests/integration/gradle/images.gradle | 46 +++++++++++++-- .../src/docker/apache2-fpm/entrypoint.sh | 8 ++- .../src/docker/apache2-mod/entrypoint.sh | 2 +- .../src/docker/nginx-fpm/entrypoint.sh | 8 ++- .../integration/src/docker/php/Dockerfile | 1 + .../src/docker/php/Dockerfile-php-deps | 1 + .../src/docker/toolchain/CHECKSUMS | 1 + .../src/docker/toolchain/Dockerfile | 8 +-- .../integration/src/docker/toolchain/Makefile | 4 +- .../src/docker/toolchain/Toolchain.cmake | 17 ++++-- .../appsec/php/docker/AppSecContainer.groovy | 3 + 12 files changed, 121 insertions(+), 35 deletions(-) diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index fd20f3d6d7..3c508ba1d9 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -1,3 +1,6 @@ +import java.nio.file.Files +import java.nio.file.Path + plugins { id 'groovy' id 'application' @@ -9,18 +12,19 @@ repositories { } dependencies { - implementation 'ch.qos.logback:logback-classic:1.4.7' + implementation 'ch.qos.logback:logback-classic:1.5.6' implementation 'org.slf4j:jul-to-slf4j:1.7.36' - implementation 'org.apache.groovy:groovy:4.0.16' - implementation 'com.google.guava:guava:32.1.3-jre' - implementation 'org.msgpack:msgpack-core:0.9.6' - implementation 'io.javalin:javalin:5.4.2' + implementation 'org.apache.groovy:groovy:4.0.21' + implementation 'org.apache.groovy:groovy-json:4.0.21' + implementation 'com.google.guava:guava:33.2.1-jre' + implementation 'org.msgpack:msgpack-core:0.9.8' + implementation 'io.javalin:javalin:6.1.4' - implementation platform('org.testcontainers:testcontainers-bom:1.16.2') + implementation platform('org.testcontainers:testcontainers-bom:1.19.8') implementation "org.testcontainers:junit-jupiter" - implementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2' - implementation 'com.flipkart.zjsonpatch:zjsonpatch:0.4.13' + implementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' + implementation 'com.flipkart.zjsonpatch:zjsonpatch:0.4.16' } test { @@ -28,12 +32,14 @@ test { tasks['test'].enabled(false) ext.testMatrix = ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'].collectMany { - [[it, 'release'], [it, 'release-zts']] + [[it, 'release'], [it, 'debug'], [it, 'release-zts']] } ext.uuid = "id -u".execute().text.trim() apply from: "$rootDir/gradle/images.gradle" +def arch = System.getProperty('os.arch') + def dockerPullTask(String tag) { String taskName = "dockerPull-${tag}" if (tasks.findByName(taskName) != null) { @@ -166,8 +172,21 @@ def buildRunInDockerTask = { Map options -> description = "${options['description']} for PHP $version $variant" def inputsSpec = options.get('inputs', [:]) - inputsSpec.get('dirs', []).each { - inputs.dir it + inputsSpec.get('dirs', []).each { dir -> + inputs.files fileTree(dir).matching { + include '**/*' + exclude { FileTreeElement element -> + Path p = element.file.toPath() + if (Files.isSymbolicLink(p)) { + // exclude if the target does not exist + if (!Files.exists(Files.readSymbolicLink(p))) { + logger.warn("Excluding broken symlink: $p") + return true + } + } + false // do not exclude + } + } } inputsSpec.get('files', []).each { inputs.file it @@ -253,7 +272,11 @@ def buildTracerTask = { String version, String variant -> needsAppsec: false, description: 'Build tracer for PHP', inputs: [ - dirs: ['../../../ext', '../../../zend_abstract_interface'], + dirs: [ + '../../../ext', + '../../../zend_abstract_interface', + '../../../libdatadog', + ], ], outputs: [ volume: 'php-tracer', @@ -263,7 +286,7 @@ def buildTracerTask = { String version, String variant -> '-e', '-c', ''' cd /project - PHPRC= make /project/tmp/build_extension/modules/ddtrace.so + PHPRC= RUST_DEBUG_BUILD=1 make /project/tmp/build_extension/modules/ddtrace.so ''' ] ) @@ -393,14 +416,14 @@ testMatrix.each { spec -> } task downloadCaches(type: Download) { - src 'https://sqreen-ci-java.s3.amazonaws.com/php-appsec-volume-caches.tar.gz' - dest 'build/php-appsec-volume-caches.tar.gz' + src "https://sqreen-ci-java.s3.amazonaws.com/php-appsec-volume-caches-${arch}.tar.gz" + dest "build/php-appsec-volume-caches-${arch}.tar.gz" overwrite false } task loadCaches(type: Exec) { description = "Load the docker caches" - inputs.file "${project.buildDir}/php-appsec-volume-caches.tar.gz" + inputs.file "${project.buildDir}/php-appsec-volume-caches-${arch}.tar.gz" commandLine 'docker', 'run', '--rm', '-v', 'php-tracer-cargo-cache:/caches/php-tracer-cargo-cache', @@ -408,7 +431,7 @@ task loadCaches(type: Exec) { '-v', "${project.buildDir}:/build", 'busybox', 'sh', '-c', - "tar -xzf /build/php-appsec-volume-caches.tar.gz -C /caches && \ + "tar -xzf /build/php-appsec-volume-caches-${arch}.tar.gz -C /caches && \ chown -R ${uuid} /caches" dependsOn downloadCaches diff --git a/appsec/tests/integration/gradle/images.gradle b/appsec/tests/integration/gradle/images.gradle index cd6b9f2ef2..f590530456 100644 --- a/appsec/tests/integration/gradle/images.gradle +++ b/appsec/tests/integration/gradle/images.gradle @@ -13,6 +13,8 @@ def phpVersions = [ '8.3': '8.3.0', ] +def arch = System.getProperty('os.arch') + def imageUpToDate = { inputs, String image -> return { long volumeTime = creationDateOf(image) @@ -163,12 +165,21 @@ task buildAll { } def buildPushTask = { String tag, requirement -> - def task = tasks.register("pushImage-${tag}", Exec) { + tasks.register("pushImage-${tag}", Exec) { String image = "$repo:$tag" + String pushedImage = image + "-$arch" description = "Push image $image" + doFirst { + def proc = ['docker', 'tag', image, pushedImage].execute() + proc.waitForOrKill(5_000) + if (proc.exitValue() != 0) { + throw new GradleException("Failed to tag image $image with arch") + } + } + dependsOn requirement - commandLine 'docker', 'push', image + commandLine 'docker', 'push', pushedImage } } def allPushTasks = [ @@ -191,9 +202,34 @@ tasks.register('pushAll') { dependsOn allPushTasks } +def buildMultiArchTask = { String tag -> + tasks.register("pushImage-${tag}-multiarch", Exec) { + String image = "$repo:$tag" + commandLine 'docker', 'buildx', 'imagetools', 'create', + '-t', image, "$image-aarch64", "$image-amd64" + } +} +def allMultiArchTasks = [ + *testMatrix.collect { spec -> + buildMultiArchTask("php-${spec[0]}-${spec[1]}") + }, + *testMatrix.collect { spec -> + buildMultiArchTask("apache2-mod-php-${spec[0]}-${spec[1]}") + }, + *testMatrix.collect { spec -> + buildMultiArchTask("apache2-fpm-php-${spec[0]}-${spec[1]}") + }, + *testMatrix.collect { spec -> + buildMultiArchTask("nginx-fpm-php-${spec[0]}-${spec[1]}") + } +] +tasks.register('pushMultiArch') { + dependsOn allMultiArchTasks +} + task saveCaches(type: Exec) { description = "Save the docker caches" - outputs.file "${project.buildDir}/php-appsec-volume-caches.tar.gz" + outputs.file "${project.buildDir}/php-appsec-volume-caches-${arch}.tar.gz" commandLine 'docker', 'run', '--rm', '-e', "UUID=${uuid}", '-v', 'php-tracer-cargo-cache:/caches/php-tracer-cargo-cache', @@ -201,7 +237,7 @@ task saveCaches(type: Exec) { '-v', "${project.buildDir}:/build", 'busybox', 'sh', '-c', - 'tar -czf /build/php-appsec-volume-caches.tar.gz \ + """tar -czf /build/php-appsec-volume-caches-${arch}.tar.gz \ -C /caches php-tracer-cargo-cache php-appsec-hunter-cache && \ - chown \$UUID /build/php-appsec-volume-caches.tar.gz' + chown \$UUID /build/php-appsec-volume-caches-${arch}.tar.gz""" } diff --git a/appsec/tests/integration/src/docker/apache2-fpm/entrypoint.sh b/appsec/tests/integration/src/docker/apache2-fpm/entrypoint.sh index ca5cb7faac..4ef543e534 100755 --- a/appsec/tests/integration/src/docker/apache2-fpm/entrypoint.sh +++ b/appsec/tests/integration/src/docker/apache2-fpm/entrypoint.sh @@ -3,7 +3,13 @@ set -x mkdir -p /tmp/logs/apache2 -LOGS_PHP=(/tmp/logs/appsec.log /tmp/logs/helper.log /tmp/logs/php_error.log /tmp/logs/php_fpm_error.log) +LOGS_PHP=( + /tmp/logs/appsec.log + /tmp/logs/helper.log + /tmp/logs/php_error.log + /tmp/logs/php_fpm_error.log + /tmp/logs/sidecar.log +) touch "${LOGS_PHP[@]}" chown www-data:www-data "${LOGS_PHP[@]}" diff --git a/appsec/tests/integration/src/docker/apache2-mod/entrypoint.sh b/appsec/tests/integration/src/docker/apache2-mod/entrypoint.sh index 81b37162c8..b3aa853937 100755 --- a/appsec/tests/integration/src/docker/apache2-mod/entrypoint.sh +++ b/appsec/tests/integration/src/docker/apache2-mod/entrypoint.sh @@ -3,7 +3,7 @@ set -x mkdir -p /tmp/logs/apache2 -LOGS_PHP=(/tmp/logs/appsec.log /tmp/logs/helper.log /tmp/logs/php_error.log) +LOGS_PHP=(/tmp/logs/appsec.log /tmp/logs/helper.log /tmp/logs/php_error.log /tmp/logs/sidecar.log) touch "${LOGS_PHP[@]}" chown www-data:www-data "${LOGS_PHP[@]}" diff --git a/appsec/tests/integration/src/docker/nginx-fpm/entrypoint.sh b/appsec/tests/integration/src/docker/nginx-fpm/entrypoint.sh index d26b3152ee..521cafb1cd 100755 --- a/appsec/tests/integration/src/docker/nginx-fpm/entrypoint.sh +++ b/appsec/tests/integration/src/docker/nginx-fpm/entrypoint.sh @@ -3,7 +3,13 @@ set -x mkdir -p /tmp/logs/nginx -LOGS_PHP=(/tmp/logs/appsec.log /tmp/logs/helper.log /tmp/logs/php_error.log /tmp/logs/php_fpm_error.log) +LOGS_PHP=( + /tmp/logs/appsec.log + /tmp/logs/helper.log + /tmp/logs/php_error.log + /tmp/logs/php_fpm_error.log + /tmp/logs/sidecar.log +) touch "${LOGS_PHP[@]}" chown www-data "${LOGS_PHP[@]}" diff --git a/appsec/tests/integration/src/docker/php/Dockerfile b/appsec/tests/integration/src/docker/php/Dockerfile index c5fe969ce2..e85c7f012b 100644 --- a/appsec/tests/integration/src/docker/php/Dockerfile +++ b/appsec/tests/integration/src/docker/php/Dockerfile @@ -12,6 +12,7 @@ RUN apt-get update && apt-get install -y \ apache2-dev \ libsqlite3-dev \ gdb \ + rust-gdb \ vim \ && rm -rf /var/lib/apt/lists/* ADD build_dev_php.sh /build/php/ diff --git a/appsec/tests/integration/src/docker/php/Dockerfile-php-deps b/appsec/tests/integration/src/docker/php/Dockerfile-php-deps index 3fd4b65888..82bbd360e7 100644 --- a/appsec/tests/integration/src/docker/php/Dockerfile-php-deps +++ b/appsec/tests/integration/src/docker/php/Dockerfile-php-deps @@ -14,6 +14,7 @@ RUN apt-get update && apt-get install -y \ netcat-openbsd \ gdb \ procps \ + python3-dev \ vim \ && rm -rf /var/lib/apt/lists/* ADD build_dev_php.sh /build/php/ diff --git a/appsec/tests/integration/src/docker/toolchain/CHECKSUMS b/appsec/tests/integration/src/docker/toolchain/CHECKSUMS index 362c2a99e1..8c7b6ff368 100644 --- a/appsec/tests/integration/src/docker/toolchain/CHECKSUMS +++ b/appsec/tests/integration/src/docker/toolchain/CHECKSUMS @@ -7,3 +7,4 @@ af5333da5b90f4a46a5184532164f4c6522e3c03a580131627c0f167ab98fb3e71b3e15518d6e224 07bf9973384151a18d5cc2892103e5f28a88c632e8e49662fde56d123632f2ed1b3710fa7a87b6b821955d0ec44160ff36f2aa4f233e389e14d628e9bf8dc764 llvm-11.1.0.src.tar.xz 5344b581bd6463d71af8c13e91792fa51f25a96a1ecbea81e42664b63d90b325aeb421dfbc8c22e187397ca08e84d9296a0c0c299ba04fa2b751d6864914bd82 musl-1.2.2.tar.gz 9591360672ba6192c606404caf70101538728a1cd5d548efcbb952f663f182bd1954d63743ffc9dd18f5c649a62a042c5e36d1ff423634dfd074f672dd1f4af9 cmake-3.28.0-linux-x86_64.tar.gz +48a20095711870b23bd5db342de0e058a7c6876bafad4c6ce9ff9bce672ca1e95ed9ac890d519b0884cd277d091575eda7e60a97cad377ee57c1e20dee25feb1 cmake-3.28.0-linux-aarch64.tar.gz diff --git a/appsec/tests/integration/src/docker/toolchain/Dockerfile b/appsec/tests/integration/src/docker/toolchain/Dockerfile index 21aff0c288..07867d0d93 100644 --- a/appsec/tests/integration/src/docker/toolchain/Dockerfile +++ b/appsec/tests/integration/src/docker/toolchain/Dockerfile @@ -6,9 +6,9 @@ RUN ln -s /bin/sed /usr/bin/sed RUN mkdir /build ADD . /build/ -RUN wget https://github.com/Kitware/CMake/releases/download/v3.28.0/cmake-3.28.0-linux-x86_64.tar.gz && \ - grep -F "cmake-3.28.0-linux-x86_64.tar.gz" ./build/CHECKSUMS | sha512sum --check && \ - tar --strip-components=1 -C /usr/local -xvzf cmake-3.28.0-linux-x86_64.tar.gz && \ - rm cmake-3.28.0-linux-x86_64.tar.gz +RUN wget https://github.com/Kitware/CMake/releases/download/v3.28.0/cmake-3.28.0-linux-$(arch | sed s/arm/aarch/).tar.gz && \ + grep -F "cmake-3.28.0-linux-$(arch | sed s/arm/aarch/).tar.gz" ./build/CHECKSUMS | sha512sum --check && \ + tar --strip-components=1 -C /usr/local -xvzf cmake-3.28.0-linux-$(arch | sed s/arm/aarch/).tar.gz && \ + rm cmake-3.28.0-linux-$(arch | sed s/arm/aarch/).tar.gz RUN cd /build && make install && make clean diff --git a/appsec/tests/integration/src/docker/toolchain/Makefile b/appsec/tests/integration/src/docker/toolchain/Makefile index 0334c4d365..e1dad3ecfd 100644 --- a/appsec/tests/integration/src/docker/toolchain/Makefile +++ b/appsec/tests/integration/src/docker/toolchain/Makefile @@ -7,7 +7,7 @@ RELTYPE := RelWithDebInfo # need to be in sync with Toolchain*.cmake files MUSL_SYSROOT := $(CURDIR)/muslsysroot -TARGET_ARCH := x86_64 +TARGET_ARCH := $(shell arch) TARGET := $(TARGET_ARCH)-none-linux-musl @@ -23,7 +23,7 @@ GCC_TOOL_PREFIX := /usr/bin/ GCC_TOOLCHAIN_SYSROOT := / .gcc-toolchain-installed: - cp -v /lib/x86_64-linux-gnu/libgcc_s.so.1 /usr/lib/gcc/x86_64-linux-gnu/10/libgcc_s.so.1 + cp -v /lib/$(TARGET_ARCH)-linux-gnu/libgcc_s.so.1 /usr/lib/gcc/$(TARGET_ARCH)-linux-gnu/10/libgcc_s.so.1 touch $@ musl-$(MUSL_VERSION).tar.gz: diff --git a/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake b/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake index 2c6397706a..04fd1fa44b 100644 --- a/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake +++ b/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake @@ -1,8 +1,17 @@ set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR x86_64) +execute_process( + COMMAND arch + OUTPUT_VARIABLE ARCHITECTURE + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if(ARCHITECTURE MATCHES "x86_64") + set(ARCH x86_64) +else() + set(ARCH aarch64) +endif() set(CMAKE_SYSROOT /build/muslsysroot) set(CMAKE_AR /usr/bin/llvm-ar-11) -set(triple x86_64-none-linux-musl) +set(triple ${ARCH}-none-linux-musl) set(CMAKE_ASM_COMPILER_TARGET ${triple}) set(CMAKE_C_COMPILER /usr/bin/clang-11) set(CMAKE_C_COMPILER_TARGET ${triple}) @@ -11,8 +20,8 @@ set(CMAKE_C_FLAGS ${c_cxx_flags}) set(CMAKE_CXX_COMPILER /usr/bin/clang++-11) set(CMAKE_CXX_COMPILER_TARGET ${triple}) set(CMAKE_CXX_FLAGS "-stdlib=libc++ -isystem${CMAKE_SYSROOT}/include/c++/v1 ${c_cxx_flags}") -set(CMAKE_EXE_LINKER_FLAGS_INIT "-v -fuse-ld=lld -static -nodefaultlibs -lc++ -lc++abi ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-x86_64.a -lunwind -lc ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-x86_64.a") -set(CMAKE_SHARED_LINKER_FLAGS_INIT "-v -fuse-ld=lld -nodefaultlibs -Wl,-Bstatic -lc++ -lc++abi ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-x86_64.a -lunwind -Wl,-Bdynamic -lc ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-x86_64.a") +set(CMAKE_EXE_LINKER_FLAGS_INIT "-v -fuse-ld=lld -static -nodefaultlibs -lc++ -lc++abi ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a -lunwind -lc ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "-v -fuse-ld=lld -nodefaultlibs -Wl,-Bstatic -lc++ -lc++abi ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a -lunwind -Wl,-Bdynamic -lc ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a") set(CMAKE_NM /usr/bin/llvm-nm-11) set(CMAKE_RANLIB /usr/bin/llvm-ranlib-11) diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy index 97ea87064b..973492d17e 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy @@ -76,6 +76,7 @@ class AppSecContainer> extends GenericContain withEnv 'DD_TRACE_DEBUG', '1' withEnv 'DD_AUTOLOAD_NO_COMPILE', 'true' // must be exactly 'true' withEnv 'DD_TRACE_GIT_METADATA_ENABLED', '0' + withEnv '_DD_DEBUG_SIDECAR_LOG_METHOD', 'file:///tmp/logs/sidecar.log' if (System.getProperty('XDEBUG') == '1') { Testcontainers.exposeHostPorts(9003) withEnv 'XDEBUG', '1' @@ -218,6 +219,8 @@ class AppSecContainer> extends GenericContain ensureVolume('php-composer-cache') addVolumeMount('php-composer-cache', '/root/.composer/cache') + addVolumeMount('php-tracer-cargo-cache', '/root/.cargo/registry') + File composerFile if (phpVersion in ['7.0', '7.1']) { composerFile = new File('build/composer-2.2.x.phar') From fd0d463d300a191e45849bc1f9d05f6819473db0 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 27 Jun 2024 11:08:23 +0100 Subject: [PATCH 004/103] Fix tracer build when using GNU sed on Mac OS --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 0e2331b25f..dd77f6aadf 100644 --- a/Makefile +++ b/Makefile @@ -45,10 +45,10 @@ M4_FILES = $(shell find m4 -name '*.m4*' | awk '{ printf "$(BUILD_DIR)/%s\n", $$ XDEBUG_SO_FILE = $(shell find $(shell php-config --extension-dir) -type f -name "xdebug*.so" -exec basename {} \; | tail -n 1) # Make 'sed -i' portable -ifeq ($(shell uname),Darwin) - SED_I = sed -i '' -else +ifeq ($(shell { sed --version 2>&1 || echo ''; } | grep GNU > /dev/null && echo GNU || true),GNU) SED_I = sed -i +else + SED_I = sed -i '' endif all: $(BUILD_DIR)/configure $(SO_FILE) From 810d9d08beeb056ebb436f1ee78e96e976d304d8 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 27 Jun 2024 11:11:09 +0100 Subject: [PATCH 005/103] Read telemetry data in AppSec integration tests --- .../appsec/php/TelemetryHelpers.groovy | 39 +++++++++++ .../appsec/php/docker/AppSecContainer.groovy | 8 +++ .../php/mock_agent/MockDatadogAgent.groovy | 8 +++ .../php/mock_agent/TelemetryHandler.groovy | 70 +++++++++++++++++++ .../test/www/base/public/flush_telemetry.php | 4 ++ 5 files changed, 129 insertions(+) create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TelemetryHandler.groovy create mode 100644 appsec/tests/integration/src/test/www/base/public/flush_telemetry.php diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy new file mode 100644 index 0000000000..493cc4b6ce --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy @@ -0,0 +1,39 @@ +package com.datadog.appsec.php + +class TelemetryHelpers { + static List filterMessages(List telemetryData, Class type) { + telemetryData.findAll { it['request_type'] == type.name } + + telemetryData.findAll { it['request_type'] == 'message-batch'} + *.payload*.findAll { it['request_type'] == 'generate-metrics' }.flatten() + .collect { type.newInstance([it['payload']] as Object[]) } + } + + static class GenerateMetrics { + static name = 'generate-metrics' + List series + + GenerateMetrics(Map m) { + series = m.series.collect { new Metric(it as Map) } + } + } + + static class Metric { + String namespace + String name + List points + List tags + boolean common + String type + long interval + + Metric(Map m) { + namespace = m.namespace + name = m.metric + points = m.points + tags = m.tags + common = m.common + type = m.type + interval = m.interval + } + } +} diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy index 973492d17e..8d5f078bf1 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy @@ -76,7 +76,11 @@ class AppSecContainer> extends GenericContain withEnv 'DD_TRACE_DEBUG', '1' withEnv 'DD_AUTOLOAD_NO_COMPILE', 'true' // must be exactly 'true' withEnv 'DD_TRACE_GIT_METADATA_ENABLED', '0' + withEnv 'DD_INSTRUMENTATION_TELEMETRY_ENABLED', '1' withEnv '_DD_DEBUG_SIDECAR_LOG_METHOD', 'file:///tmp/logs/sidecar.log' + withEnv 'DD_TELEMETRY_HEARTBEAT_INTERVAL', '10' + withEnv 'DD_TELEMETRY_EXTENDED_HEARTBEAT_INTERVAL', '10' + withEnv '_DD_SHARED_LIB_DEBUG', '1' if (System.getProperty('XDEBUG') == '1') { Testcontainers.exposeHostPorts(9003) withEnv 'XDEBUG', '1' @@ -108,6 +112,10 @@ class AppSecContainer> extends GenericContain mockDatadogAgent.drainTraces() } + List drainTelemetry(int timeoutInMs) { + mockDatadogAgent.drainTelemetry(timeoutInMs) + } + void close() { copyLogs() super.close() diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy index aa869535fb..159b16ccf0 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy @@ -25,6 +25,10 @@ class MockDatadogAgent implements Startable { this.httpServer.post('v0.4/traces', tracesHandler) this.httpServer.put('v0.4/traces', tracesHandler) this.httpServer.get('info', InfoHandler.instance) + this.httpServer.post('/telemetry/proxy/api/v2/apmtelemetry', TelemetryHandler.instance) + this.httpServer.error(404, ctx -> { + log.info("Unmatched request: ${ctx.method()} ${ctx.path()}") + }) this.httpServer.start(0) } @@ -60,4 +64,8 @@ class MockDatadogAgent implements Startable { List drainTraces() { tracesHandler.drainTraces() } + + List drainTelemetry(int timeoutInMs) { + TelemetryHandler.instance.drain(timeoutInMs) + } } diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TelemetryHandler.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TelemetryHandler.groovy new file mode 100644 index 0000000000..97fc0ab912 --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TelemetryHandler.groovy @@ -0,0 +1,70 @@ +package com.datadog.appsec.php.mock_agent + +import groovy.json.JsonSlurper +import groovy.util.logging.Slf4j +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.Handler +import org.jetbrains.annotations.NotNull + +@Singleton +@Slf4j +class TelemetryHandler implements Handler { + + private final static JsonSlurper jsonSlurper = new JsonSlurper() + final List capturedTelemetryMessages = [] + AssertionError savedError + + @Override + void handle(@NotNull Context ctx) throws Exception { + Object message + AssertionError error + try { + ctx.bodyInputStream().withCloseable { + message = readTelemetryMessage(it) + } + log.debug("Read telemetry message: ${message['request_type']}") + } catch (AssertionError e) { + log.error("Error reading traces: $e.message") + error = e + } + + // response + ctx.contentType('text/plain') + .result('') + + if (message) { + synchronized (capturedTelemetryMessages) { + capturedTelemetryMessages.add message + capturedTelemetryMessages.notify() + } + } + if (error) { + synchronized (capturedTelemetryMessages) { + savedError = error + capturedTelemetryMessages.notify() + throw new BadRequestResponse(error.message) + } + } + } + + private static Object readTelemetryMessage(InputStream is) { + jsonSlurper.parse(is) + } + + List drain(long timeoutInMs) { + synchronized (capturedTelemetryMessages) { + if (!savedError && capturedTelemetryMessages.isEmpty()) { + capturedTelemetryMessages.wait(timeoutInMs) + } + if (savedError) { + def e = savedError + savedError = null + throw e + } + def messages = [*capturedTelemetryMessages] + capturedTelemetryMessages.clear() + messages + } + } +} diff --git a/appsec/tests/integration/src/test/www/base/public/flush_telemetry.php b/appsec/tests/integration/src/test/www/base/public/flush_telemetry.php new file mode 100644 index 0000000000..52f5388e78 --- /dev/null +++ b/appsec/tests/integration/src/test/www/base/public/flush_telemetry.php @@ -0,0 +1,4 @@ + Date: Wed, 24 Jul 2024 15:13:37 +0100 Subject: [PATCH 006/103] Read remote config in integration tests --- appsec/tests/integration/build.gradle | 7 + .../appsec/php/docker/AppSecContainer.groovy | 1 + .../php/mock_agent/ConfigV07Handler.groovy | 48 +++ .../appsec/php/mock_agent/InfoHandler.groovy | 3 +- .../php/mock_agent/MockDatadogAgent.groovy | 1 + .../rem_cfg/IntegrityCheckException.groovy | 11 + .../rem_cfg/MissingContentException.groovy | 7 + .../rem_cfg/RemoteConfigRequest.java | 320 ++++++++++++++++++ .../rem_cfg/RemoteConfigResponse.java | 186 ++++++++++ .../integration/src/test/resources/gdbinit | 4 + 10 files changed, 587 insertions(+), 1 deletion(-) create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/IntegrityCheckException.groovy create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/MissingContentException.groovy create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java create mode 100644 appsec/tests/integration/src/test/resources/gdbinit diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index 3c508ba1d9..83b5b9d6d5 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -11,6 +11,13 @@ repositories { mavenCentral() } +sourceCompatibility = '11' +targetCompatibility = '11' + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + dependencies { implementation 'ch.qos.logback:logback-classic:1.5.6' implementation 'org.slf4j:jul-to-slf4j:1.7.36' diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy index 8d5f078bf1..2b3a51ab52 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy @@ -214,6 +214,7 @@ class AppSecContainer> extends GenericContain withFileSystemBind(wwwDir, '/test-resources', BindMode.READ_ONLY) withFileSystemBind('src/test/waf/recommended.json', '/etc/recommended.json', BindMode.READ_ONLY) + withFileSystemBind('src/test/resources/gdbinit', '/root/.gdbinit', BindMode.READ_ONLY) withFileSystemBind('src/test/bin/enable_extensions.sh', '/usr/local/bin/enable_extensions.sh', BindMode.READ_ONLY) addVolumeMount("php-appsec-$phpVersion-$phpVariant", '/appsec') diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy new file mode 100644 index 0000000000..af6b52c189 --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy @@ -0,0 +1,48 @@ +package com.datadog.appsec.php.mock_agent + +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigResponse +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigRequest +import com.google.common.collect.Lists +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import io.javalin.http.Context +import io.javalin.http.Handler +import org.jetbrains.annotations.NotNull + +@Slf4j +@CompileStatic +@Singleton +class ConfigV07Handler implements Handler { + RemoteConfigResponse nextResponse + final List capturedRequests = [] + + @Override + void handle(@NotNull Context context) throws Exception { + RemoteConfigRequest request = context.bodyStreamAsClass(RemoteConfigRequest) + log.debug("Received request with version ${request.client.clientState.targetsVersion}: {}", request.toString()) + synchronized (capturedRequests) { + capturedRequests.add(request) + capturedRequests.notify() + } + if (nextResponse != null) { + context.json(nextResponse) + } else { + context.json([:]) + } + } + + void setNextResponse(RemoteConfigResponse nextResponse) { + this.nextResponse = nextResponse + } + + List drain(long timeoutInMs) { + synchronized (capturedRequests) { + if (capturedRequests.isEmpty()) { + capturedRequests.wait(timeoutInMs) + } + def requests = Lists.newArrayList(capturedRequests) + capturedRequests.clear() + requests + } + } +} diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/InfoHandler.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/InfoHandler.groovy index 3ef291b295..4e953f3b8a 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/InfoHandler.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/InfoHandler.groovy @@ -15,7 +15,8 @@ class InfoHandler implements Handler { static class InfoResponse { String version = '7.49.0' List endpoints = [ - '/v0.4/traces' + '/v0.4/traces', + '/v0.7/config', ] } diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy index 159b16ccf0..3895aaf2d5 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy @@ -26,6 +26,7 @@ class MockDatadogAgent implements Startable { this.httpServer.put('v0.4/traces', tracesHandler) this.httpServer.get('info', InfoHandler.instance) this.httpServer.post('/telemetry/proxy/api/v2/apmtelemetry', TelemetryHandler.instance) + this.httpServer.post('v0.7/config', ConfigV07Handler.instance) this.httpServer.error(404, ctx -> { log.info("Unmatched request: ${ctx.method()} ${ctx.path()}") }) diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/IntegrityCheckException.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/IntegrityCheckException.groovy new file mode 100644 index 0000000000..26a7e1ef13 --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/IntegrityCheckException.groovy @@ -0,0 +1,11 @@ +package com.datadog.appsec.php.mock_agent.rem_cfg + +class IntegrityCheckException extends RuntimeException { + IntegrityCheckException(String s) { + super(s) + } + + IntegrityCheckException(String s, Exception e) { + super(s, e) + } +} diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/MissingContentException.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/MissingContentException.groovy new file mode 100644 index 0000000000..1fb28f10f2 --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/MissingContentException.groovy @@ -0,0 +1,7 @@ +package com.datadog.appsec.php.mock_agent.rem_cfg + +class MissingContentException extends RuntimeException { + MissingContentException(String s) { + super(s) + } +} diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java new file mode 100644 index 0000000000..f2db7ed5c1 --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java @@ -0,0 +1,320 @@ +package com.datadog.appsec.php.mock_agent.rem_cfg; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; +import java.util.stream.Collectors; + +public class RemoteConfigRequest { + + public static RemoteConfigRequest newRequest( + String clientId, + String runtimeId, + String tracerVersion, + Collection productNames, + String serviceName, + List extraServices, + String serviceEnv, + String serviceVersion, + List tags, + ClientInfo.ClientState clientState, + Collection cachedTargetFiles, + long capabilities) { + + ClientInfo.TracerInfo tracerInfo = + new RemoteConfigRequest.ClientInfo.TracerInfo(); + tracerInfo.runtimeId = runtimeId; + tracerInfo.tracerVersion = tracerVersion; + tracerInfo.serviceName = serviceName; + tracerInfo.extraServices = extraServices; + tracerInfo.serviceEnv = serviceEnv; + tracerInfo.serviceVersion = serviceVersion; + tracerInfo.tags = tags; + + ClientInfo clientInfo = + new RemoteConfigRequest.ClientInfo( + clientState, clientId, productNames, tracerInfo, capabilities); + + RemoteConfigRequest rcr = new RemoteConfigRequest(); + rcr.client = clientInfo; + rcr.cachedTargetFiles = cachedTargetFiles; + + return rcr; + } + + private ClientInfo client; + + @JsonProperty("cached_target_files") + private Collection cachedTargetFiles; + + public ClientInfo getClient() { + return this.client; + } + + /** Stores client information for Remote Configuration */ + public static class ClientInfo { + @JsonProperty("state") + public ClientState clientState; + + public String id; + public Collection products; + + @JsonProperty("client_tracer") + public TracerInfo tracerInfo; + + @JsonProperty("client_agent") + public AgentInfo agentInfo = null; // MUST NOT be set + + @JsonProperty("is_tracer") + public boolean isTracer = true; + + @JsonProperty("is_agent") + public Boolean isAgent = null; // MUST NOT be set; + + public byte[] capabilities; + + public ClientInfo() {} + public ClientInfo( + ClientState clientState, + String id, + Collection productNames, + TracerInfo tracerInfo, + final long capabilities) { + this.clientState = clientState; + this.id = id; + this.products = productNames; + this.tracerInfo = tracerInfo; + + // Big-endian encoding of the `long` capabilities, stripping any trailing zero bytes + // (except the first one) + final int size = Math.max(1, Long.BYTES - Long.numberOfLeadingZeros(capabilities) / 8); + this.capabilities = new byte[size]; + for (int i = size - 1; i >= 0; i--) { + this.capabilities[size - i - 1] = (byte) (capabilities >>> (i * 8)); + } + } + + public TracerInfo getTracerInfo() { + return this.tracerInfo; + } + + public static class ClientState { + @JsonProperty("root_version") + public long rootVersion = 1L; + + @JsonProperty("targets_version") + public long targetsVersion; + + @JsonProperty("config_states") + public List configStates = new ArrayList<>(); + + @JsonProperty("has_error") + public boolean hasError; + + public String error; + + @JsonProperty("backend_client_state") + public String backendClientState; + + public void setState( + long targetsVersion, + List configStates, + String error, + String backendClientState) { + this.targetsVersion = targetsVersion; + this.configStates = configStates; + this.error = error; + this.hasError = error != null && !error.isEmpty(); + this.backendClientState = backendClientState; + } + + public static class ConfigState { + public static final int APPLY_STATE_ACKNOWLEDGED = 2; + public static final int APPLY_STATE_ERROR = 3; + + private String id; + private long version; + public String product; + + @JsonProperty("apply_state") + public int applyState; + + @JsonProperty("apply_error") + public String applyError; + + public void setState(String id, long version, String product, String error) { + this.id = id; + this.version = version; + this.product = product; + this.applyState = error == null ? APPLY_STATE_ACKNOWLEDGED : APPLY_STATE_ERROR; + this.applyError = error; + } + + @Override + public String toString() { + return new StringJoiner(", ", ConfigState.class.getSimpleName() + "[", "]") + .add("id='" + id + "'") + .add("version=" + version) + .add("product='" + product + "'") + .add("applyState=" + applyState) + .add("applyError='" + applyError + "'") + .toString(); + } + } + + @Override + public String toString() { + return new StringJoiner(", ", ClientState.class.getSimpleName() + "[", "]") + .add("rootVersion=" + rootVersion) + .add("targetsVersion=" + targetsVersion) + .add("configStates=" + configStates) + .add("hasError=" + hasError) + .add("error='" + error + "'") + .add("backendClientState='" + backendClientState + "'") + .toString(); + } + } + + public static class TracerInfo { + @JsonProperty("runtime_id") + public String runtimeId; + + public String language = "java"; + + public List tags; + + @JsonProperty("tracer_version") + public String tracerVersion; + + @JsonProperty("service") + public String serviceName; + + @JsonProperty("extra_services") + public List extraServices; + + @JsonProperty("env") + public String serviceEnv; + + @JsonProperty("app_version") + public String serviceVersion; + + @Override + public String toString() { + return new StringJoiner(", ", TracerInfo.class.getSimpleName() + "[", "]") + .add("runtimeId='" + runtimeId + "'") + .add("language='" + language + "'") + .add("tags=" + tags) + .add("tracerVersion='" + tracerVersion + "'") + .add("serviceName='" + serviceName + "'") + .add("extraServices=" + extraServices) + .add("serviceEnv='" + serviceEnv + "'") + .add("serviceVersion='" + serviceVersion + "'") + .toString(); + } + } + + private class AgentInfo { + String name; + String version; + + @Override + public String toString() { + return new StringJoiner(", ", AgentInfo.class.getSimpleName() + "[", "]") + .add("name='" + name + "'") + .add("version='" + version + "'") + .toString(); + } + } + + @Override + public String toString() { + return new StringJoiner(", ", ClientInfo.class.getSimpleName() + "[", "]") + .add("clientState=" + clientState) + .add("id='" + id + "'") + .add("products=" + products) + .add("tracerInfo=" + tracerInfo) + .add("agentInfo=" + agentInfo) + .add("isTracer=" + isTracer) + .add("isAgent=" + isAgent) + .add("capabilities=" + Arrays.toString(capabilities)) + .toString(); + } + } + + public static class CachedTargetFile { + public String path; + public long length; + public List hashes; + + public CachedTargetFile() {} + public CachedTargetFile( + String path, long length, Map hashes) { + this.path = path; + this.length = length; + List hashesList = + hashes.entrySet().stream() + .map(e -> new TargetFileHash(e.getKey(), e.getValue())) + .collect(Collectors.toList()); + this.hashes = hashesList; + } + + public boolean hashesMatch(Map hashesMap) { + if (this.hashes == null) { + return false; + } + + if (hashesMap.size() != this.hashes.size()) { + return false; + } + + for (TargetFileHash tfh : hashes) { + String digest = hashesMap.get(tfh.algorithm); + if (!digest.equals(tfh.hash)) { + return false; + } + } + + return true; + } + + public class TargetFileHash { + String algorithm; + String hash; + + TargetFileHash() {} + TargetFileHash(String algorithm, String hash) { + this.algorithm = algorithm; + this.hash = hash; + } + + @Override + public String toString() { + return new StringJoiner(", ", TargetFileHash.class.getSimpleName() + "[", "]") + .add("algorithm='" + algorithm + "'") + .add("hash='" + hash + "'") + .toString(); + } + } + + @Override + public String toString() { + return new StringJoiner(", ", CachedTargetFile.class.getSimpleName() + "[", "]") + .add("path='" + path + "'") + .add("length=" + length) + .add("hashes=" + hashes) + .toString(); + } + } + + @Override + public String toString() { + return new StringJoiner(", ", RemoteConfigRequest.class.getSimpleName() + "[", "]") + .add("client=" + client) + .add("cachedTargetFiles=" + cachedTargetFiles) + .toString(); + } +} diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java new file mode 100644 index 0000000000..0c77f63dc8 --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java @@ -0,0 +1,186 @@ +package com.datadog.appsec.php.mock_agent.rem_cfg; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class RemoteConfigResponse { + @JsonProperty("client_configs") + public List clientConfigs; + + @JsonDeserialize(using = TargetsDeserializer.class) + private Targets targets; + + @JsonProperty("target_files") + public List targetFiles; + + public Targets.ConfigTarget getTarget(String configKey) { + return this.targets.targetsSigned.targets.get(configKey); + } + + public String getTargetsSignature(String keyId) { + for (Targets.Signature signature : this.targets.signatures) { + if (keyId.equals(signature.keyId)) { + return signature.signature; + } + } + + throw new IntegrityCheckException("Missing signature for key " + keyId); + } + + public Targets.TargetsSigned getTargetsSigned() { + return this.targets.targetsSigned; + } + + public byte[] getFileContents(String configKey) { + + if (targetFiles == null) { + throw new MissingContentException("No content for " + configKey); + } + + try { + for (TargetFile targetFile : this.targetFiles) { + if (!configKey.equals(targetFile.path)) { + continue; + } + + Targets.ConfigTarget configTarget = getTarget(configKey); + String hashStr; + if (configTarget == null + || configTarget.hashes == null + || (hashStr = configTarget.hashes.get("sha256")) == null) { + throw new IntegrityCheckException("No sha256 hash present for " + configKey); + } + BigInteger expectedHash = new BigInteger(hashStr, 16); + + String raw = targetFile.raw; + byte[] decode = Base64.getDecoder().decode(raw); + BigInteger gottenHash = sha256(decode); + if (!expectedHash.equals(gottenHash)) { + throw new IntegrityCheckException( + "File " + + configKey + + " does not " + + "have the expected sha256 hash: Expected " + + expectedHash.toString(16) + + ", but got " + + gottenHash.toString(16)); + } + if (decode.length != configTarget.length) { + throw new IntegrityCheckException( + "File " + + configKey + + " does not " + + "have the expected length: Expected " + + configTarget.length + + ", but got " + + decode.length); + } + + return decode; + } + } catch (IntegrityCheckException e) { + throw e; + } catch (Exception exception) { + throw new IntegrityCheckException( + "Could not get file contents from remote config, file " + configKey, exception); + } + + throw new MissingContentException("No content for " + configKey); + } + + private static BigInteger sha256(byte[] bytes) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(bytes); + return new BigInteger(1, hash); + } catch (NoSuchAlgorithmException e) { + throw new UndeclaredThrowableException(e); + } + } + + public List getClientConfigs() { + return this.clientConfigs != null ? this.clientConfigs : Collections.emptyList(); + } + + public static class Targets { + public List signatures; + + @JsonProperty("signed") + public TargetsSigned targetsSigned; + + public static class Signature { + @JsonProperty("keyid") + public String keyId; + + @JsonProperty("sig") + public String signature; + } + + public static class TargetsSigned { + @JsonProperty("_type") + public String type; + + public TargetsCustom custom; + public Instant expires; + + @JsonProperty("spec_version") + public String specVersion; + + public long version; + public Map targets; + + public static class TargetsCustom { + @JsonProperty("opaque_backend_state") + public String opaqueBackendState; + } + } + + public static class ConfigTarget { + public ConfigTargetCustom custom; + public Map hashes; + public long length; + + public static class ConfigTargetCustom { + @JsonProperty("v") + public long version; + } + } + } + + public static class TargetFile { + public String path; + public String raw; + } + + public static class TargetsDeserializer extends JsonDeserializer { + @Override + public Targets deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + String targetsJsonBase64 = node.asText(); + byte[] targetsJsonDecoded = + Base64.getDecoder().decode(targetsJsonBase64.getBytes(StandardCharsets.ISO_8859_1)); + + JsonParser defParser = jsonParser.getCodec().getFactory().createParser(targetsJsonDecoded); + + JsonDeserializer defaultDeserializer = deserializationContext.findRootValueDeserializer( + deserializationContext.constructType(Targets.class)); + return (Targets) defaultDeserializer.deserialize(defParser, deserializationContext); + } + } +} diff --git a/appsec/tests/integration/src/test/resources/gdbinit b/appsec/tests/integration/src/test/resources/gdbinit new file mode 100644 index 0000000000..aaf915b3cc --- /dev/null +++ b/appsec/tests/integration/src/test/resources/gdbinit @@ -0,0 +1,4 @@ +set verbose off +set confirm off +handle SIGPIPE nostop print pass +catch throw From c384350f082cccd804c270f267ff7d60aef9ebb3 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 27 Jun 2024 11:12:41 +0100 Subject: [PATCH 007/103] Fix headers collection tests when element order differs --- .../extension/headers_collection_01.phpt | 21 +++--- .../extension/headers_collection_02.phpt | 73 +++++++++--------- .../extension/headers_collection_03.phpt | 73 +++++++++--------- .../extension/headers_collection_04.phpt | 73 +++++++++--------- .../extension/headers_collection_05.phpt | 73 +++++++++--------- .../extension/headers_collection_06.phpt | 73 +++++++++--------- .../extension/headers_collection_07.phpt | 73 +++++++++--------- .../extension/headers_collection_08.phpt | 73 +++++++++--------- .../extension/headers_collection_09.phpt | 21 +++--- .../extension/headers_collection_10.phpt | 21 +++--- .../extension/headers_collection_11.phpt | 21 +++--- .../extension/headers_collection_12.phpt | 73 +++++++++--------- .../extension/headers_collection_13.phpt | 74 +++++++++---------- 13 files changed, 377 insertions(+), 365 deletions(-) diff --git a/appsec/tests/extension/headers_collection_01.phpt b/appsec/tests/extension/headers_collection_01.phpt index ade7dafe21..290bb5183d 100644 --- a/appsec/tests/extension/headers_collection_01.phpt +++ b/appsec/tests/extension/headers_collection_01.phpt @@ -68,32 +68,33 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(11) { + ["http.request.headers.accept"]=> + string(3) "*/*" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" ["http.request.headers.content-type"]=> string(10) "text/plain" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_02.phpt b/appsec/tests/extension/headers_collection_02.phpt index 065fb507df..67f5bb9023 100644 --- a/appsec/tests/extension/headers_collection_02.phpt +++ b/appsec/tests/extension/headers_collection_02.phpt @@ -68,60 +68,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_03.phpt b/appsec/tests/extension/headers_collection_03.phpt index d0854f6737..add14327c1 100644 --- a/appsec/tests/extension/headers_collection_03.phpt +++ b/appsec/tests/extension/headers_collection_03.phpt @@ -73,60 +73,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_04.phpt b/appsec/tests/extension/headers_collection_04.phpt index d067f60fad..bad781630a 100644 --- a/appsec/tests/extension/headers_collection_04.phpt +++ b/appsec/tests/extension/headers_collection_04.phpt @@ -73,60 +73,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_05.phpt b/appsec/tests/extension/headers_collection_05.phpt index 66da7497ec..b0164b7d87 100644 --- a/appsec/tests/extension/headers_collection_05.phpt +++ b/appsec/tests/extension/headers_collection_05.phpt @@ -73,60 +73,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_06.phpt b/appsec/tests/extension/headers_collection_06.phpt index 25a364490a..f5cfe6fe64 100644 --- a/appsec/tests/extension/headers_collection_06.phpt +++ b/appsec/tests/extension/headers_collection_06.phpt @@ -74,60 +74,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_07.phpt b/appsec/tests/extension/headers_collection_07.phpt index 9bc25a497e..373a9cd8f0 100644 --- a/appsec/tests/extension/headers_collection_07.phpt +++ b/appsec/tests/extension/headers_collection_07.phpt @@ -74,60 +74,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_08.phpt b/appsec/tests/extension/headers_collection_08.phpt index 19e060657d..e052c3b44f 100644 --- a/appsec/tests/extension/headers_collection_08.phpt +++ b/appsec/tests/extension/headers_collection_08.phpt @@ -74,60 +74,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_09.phpt b/appsec/tests/extension/headers_collection_09.phpt index b92e1d00cf..4497cbd394 100644 --- a/appsec/tests/extension/headers_collection_09.phpt +++ b/appsec/tests/extension/headers_collection_09.phpt @@ -74,32 +74,33 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(11) { + ["http.request.headers.accept"]=> + string(3) "*/*" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" ["http.request.headers.content-type"]=> string(10) "text/plain" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_10.phpt b/appsec/tests/extension/headers_collection_10.phpt index f67f0ce96e..7a80d1db88 100644 --- a/appsec/tests/extension/headers_collection_10.phpt +++ b/appsec/tests/extension/headers_collection_10.phpt @@ -74,32 +74,33 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(11) { + ["http.request.headers.accept"]=> + string(3) "*/*" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" ["http.request.headers.content-type"]=> string(10) "text/plain" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_11.phpt b/appsec/tests/extension/headers_collection_11.phpt index c722afbf22..72199d86b5 100644 --- a/appsec/tests/extension/headers_collection_11.phpt +++ b/appsec/tests/extension/headers_collection_11.phpt @@ -74,32 +74,33 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(11) { + ["http.request.headers.accept"]=> + string(3) "*/*" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" ["http.request.headers.content-type"]=> string(10) "text/plain" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_12.phpt b/appsec/tests/extension/headers_collection_12.phpt index c39eccbd1a..d93073d78e 100644 --- a/appsec/tests/extension/headers_collection_12.phpt +++ b/appsec/tests/extension/headers_collection_12.phpt @@ -74,60 +74,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } diff --git a/appsec/tests/extension/headers_collection_13.phpt b/appsec/tests/extension/headers_collection_13.phpt index f41f53ab20..d32870831d 100644 --- a/appsec/tests/extension/headers_collection_13.phpt +++ b/appsec/tests/extension/headers_collection_13.phpt @@ -66,7 +66,6 @@ rshutdown(); $helper->get_commands(); //ignore - ddtrace_rshutdown(); dd_trace_internal_fn('synchronous_flush'); @@ -74,60 +73,61 @@ $commands = $helper->get_commands(); $tags = $commands[0]['payload'][0][0]['meta']; $headers = array_filter($tags, function ($key) { return strpos($key, "http.request.headers.") === 0;}, ARRAY_FILTER_USE_KEY); +ksort($headers); var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- array(25) { + ["http.request.headers.accept"]=> + string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" - ["http.request.headers.x-forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.x-cluster-client-ip"]=> - string(7) "7.7.7.9" ["http.request.headers.cf-ray"]=> string(5) "cfray" - ["http.request.headers.user-agent"]=> - string(13) "my user agent" - ["http.request.headers.accept"]=> - string(3) "*/*" - ["http.request.headers.host"]=> - string(11) "myhost:8888" - ["http.request.headers.forwarded"]=> - string(9) "for="foo"" - ["http.request.headers.content-encoding"]=> - string(5) "utf-8" - ["http.request.headers.x-forwarded-for"]=> - string(16) "7.7.7.6,10.0.0.1" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" - ["http.request.headers.accept-language"]=> - string(5) "pt-PT" - ["http.request.headers.x-appgw-trace-id"]=> - string(12) "appgvtraceid" - ["http.request.headers.x-sigsci-tags"]=> - string(10) "sigscitags" - ["http.request.headers.true-client-ip"]=> - string(8) "7.7.7.11" - ["http.request.headers.accept-encoding"]=> - string(4) "gzip" - ["http.request.headers.x-sigsci-requestid"]=> - string(15) "sigscirequestid" - ["http.request.headers.x-client-ip"]=> - string(7) "7.7.7.7" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" - ["http.request.headers.x-real-ip"]=> - string(7) "7.7.7.8" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" ["http.request.headers.forwarded-for"]=> string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" + ["http.request.headers.user-agent"]=> + string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" + ["http.request.headers.x-appgw-trace-id"]=> + string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" - ["http.request.headers.via"]=> - string(12) "HTTP/1.1 GWA" - ["http.request.headers.content-length"]=> - string(1) "0" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" + ["http.request.headers.x-sigsci-requestid"]=> + string(15) "sigscirequestid" + ["http.request.headers.x-sigsci-tags"]=> + string(10) "sigscitags" } From 4a671f7ab2d4a3fb530197c639a95ad04a7093eb Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 27 Jun 2024 17:13:57 +0100 Subject: [PATCH 008/103] Improvements to build_dev_php.sh --- .../src/docker/php/build_dev_php.sh | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/appsec/tests/integration/src/docker/php/build_dev_php.sh b/appsec/tests/integration/src/docker/php/build_dev_php.sh index 18a1821d2d..4b81f7e18c 100755 --- a/appsec/tests/integration/src/docker/php/build_dev_php.sh +++ b/appsec/tests/integration/src/docker/php/build_dev_php.sh @@ -137,12 +137,28 @@ function build_php { options+=(--with-iconv=shared) fi + if [[ -d /opt/homebrew/opt/zlib ]]; then + options+=(--with-zlib-dir=/opt/homebrew/opt/zlib) + fi + + if [[ -f /opt/homebrew/include/gmp.h ]]; then + options+=(--with-gmp=shared,/opt/homebrew) + else + options+=(--with-gmp=shared) + fi + + if [[ -d /opt/homebrew/opt/sqlite ]]; then + options+=(--with-pdo-sqlite=shared,/opt/homebrew/opt/sqlite) + else + options+=(--with-pdo-sqlite=shared,/usr) + fi + set -x - local -r libpq_dir=$(find /opt/homebrew/Cellar/libpq -depth 1 -type d 2>/dev/null | head -1) + local -r libpq_dir=/opt/homebrew/opt/libpq if [[ -n $libpq_dir ]]; then - #export LDFLAGS="${LDFLAGS:-} -L$libpq_dir/lib" - #export CPPFLAGS="${CPPFLAGS:-} -I$libpq_dir/include" - #export PATH="$libpq_dir/bin:$PATH" + export LDFLAGS="${LDFLAGS:-} -L$libpq_dir/lib" + export CPPFLAGS="${CPPFLAGS:-} -I$libpq_dir/include" + export PATH="$libpq_dir/bin:$PATH" options+=( "--with-pgsql=shared,$libpq_dir/bin" "--with-pdo-pgsql=shared,$libpq_dir/bin") @@ -160,11 +176,10 @@ function build_php { --enable-dom=shared --enable-fileinfo=shared --enable-filter - --with-gmp=shared --enable-intl=shared --enable-mbstring=shared --enable-opcache=shared - --enable-pdo=shared + "--enable-pdo=$([[ $(uname -o) != Darwin ]] && echo shared)" --enable-phar=shared --enable-xml --enable-simplexml=shared @@ -174,7 +189,6 @@ function build_php { --enable-ctype=shared --enable-session=shared --enable-tokenizer=shared - --with-pdo-sqlite=shared,/usr --with-curl=shared) fi @@ -312,15 +326,22 @@ function install_openssl { local -r build_dir="$HOME/php/build/openssl$major" mkdir -p "$build_dir" - cd "$build_dir" + pushd "$build_dir" if [[ $major = 1.0 ]]; then cp -a "$source_dir/." "$build_dir" fi - "$source_dir/config" --prefix="$install_dir" --openssldir="$install_dir" shared zlib no-tests + if [[ $version = '1.0.2u' && $(uname -o) = 'Darwin' && $(arch) = 'arm64' ]]; then + curl -Lf https://gist.githubusercontent.com/felixbuenemann/5f4dcb30ebb3b86e1302e2ec305bac89/raw/b339a33ff072c9747df21e2558c36634dd62c195/openssl-1.0.2u-darwin-arm64.patch | patch -p1 + "./Configure" shared zlib no-tests --prefix="$install_dir" \ + --openssldir="$install_dir" darwin64-arm64-cc + make depend + else + "$source_dir/config" --prefix="$install_dir" --openssldir="$install_dir" shared zlib no-tests + fi make -j && make install_sw touch "$install_dir/.installed" - cd - + popd rm -rf "$build_dir" echo "Installed OpenSSL $version in $install_dir" @@ -462,6 +483,8 @@ if [[ -d /opt/homebrew/lib ]]; then export LDFLAGS="${LDFLAGS:-} -L/opt/homebrew/lib" export CPPFLAGS="${CPPFLAGS:-} -I/opt/homebrew/include" fi +export CXXFLAGS="${CXXFLAGS:-} -std=c++11" +export CFLAGS="${CFLAGS:-} -Wno-implicit-function-declaration" install_openssl 1.0.2u install_openssl 1.1.1w From 3b37bb4ed6af8efb503a1c32af1871bd364ec26f Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Wed, 3 Jul 2024 17:55:49 +0100 Subject: [PATCH 009/103] appsec helper: make http calls timeout asio synchronous network APIs don't support timeouts. Switch to async APIs with stackless C++20 coroutine executor. --- appsec/cmake/helper.cmake | 8 ++- appsec/src/helper/remote_config/http_api.cpp | 68 ++++++++++++++++---- appsec/tests/fuzzer/CMakeLists.txt | 7 +- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/appsec/cmake/helper.cmake b/appsec/cmake/helper.cmake index 836285b3ae..8f9e2a2f44 100644 --- a/appsec/cmake/helper.cmake +++ b/appsec/cmake/helper.cmake @@ -15,6 +15,8 @@ list(FILTER HELPER_SOURCE EXCLUDE REGEX "^.*main\.cpp$") add_library(helper_objects OBJECT ${HELPER_SOURCE}) set_target_properties(helper_objects PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED YES POSITION_INDEPENDENT_CODE 1) target_include_directories(helper_objects PUBLIC ${HELPER_INCLUDE_DIR}) target_compile_definitions(helper_objects PUBLIC SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) @@ -27,16 +29,16 @@ target_link_libraries(ddappsec-helper helper_objects) # for its PUBLIC deps try_compile(STDLIBXX_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_fslib.cpp - CXX_STANDARD 17 + CXX_STANDARD 20 CXX_STANDARD_REQUIRED TRUE) try_compile(STDLIBXX_FS_NEEDS_STDCXXFS ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_fslib.cpp - CXX_STANDARD 17 + CXX_STANDARD 20 CXX_STANDARD_REQUIRED TRUE LINK_LIBRARIES stdc++fs) try_compile(STDLIBXX_FS_NEEDS_CXXFS ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_fslib.cpp - CXX_STANDARD 17 + CXX_STANDARD 20 CXX_STANDARD_REQUIRED TRUE LINK_LIBRARIES c++fs) if(NOT STDLIBXX_FS_NO_LIB_NEEDED) diff --git a/appsec/src/helper/remote_config/http_api.cpp b/appsec/src/helper/remote_config/http_api.cpp index 9f97b123dd..af5ad69248 100644 --- a/appsec/src/helper/remote_config/http_api.cpp +++ b/appsec/src/helper/remote_config/http_api.cpp @@ -4,11 +4,18 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "http_api.hpp" +#include +#include #include #include +#include +#include #include +#include #include #include +#include +#include #include #include #include @@ -18,29 +25,37 @@ namespace http = beast::http; // from namespace net = boost::asio; // from using tcp = net::ip::tcp; // from -static const int version = 11; +namespace { +constexpr auto timeout = + std::chrono::duration_cast( + std::chrono::seconds{60}); +const int version = 11; -std::string execute_request(const std::string &host, const std::string &port, - const http::request &request) +// NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) +net::awaitable execute_request(const std::string &host, +// NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + const std::string &port, const http::request &request) { std::string result; try { - // The io_context is required for all I/O - net::io_context ioc; + auto exec = co_await net::this_coro::executor; // These objects perform our I/O - tcp::resolver resolver(ioc); - beast::tcp_stream stream(ioc); + tcp::resolver resolver(exec); + beast::tcp_stream stream(exec); // Look up the domain name - auto const results = resolver.resolve(host, port); + auto const results = + co_await resolver.async_resolve(host, port, net::use_awaitable); // Make the connection on the IP address we get from a lookup - stream.connect(results); + beast::get_lowest_layer(stream).expires_after(timeout); + co_await stream.async_connect( + results.begin(), results.end(), net::use_awaitable); // Send the HTTP request to the remote host - http::write(stream, request); + co_await http::async_write(stream, request, net::use_awaitable); // This buffer is used for reading and must be persisted beast::flat_buffer buffer; @@ -49,7 +64,7 @@ std::string execute_request(const std::string &host, const std::string &port, http::response res; // Receive the HTTP response - http::read(stream, buffer, res); + co_await http::async_read(stream, buffer, res, net::use_awaitable); // Write the message to standard out result = boost::beast::buffers_to_string(res.body().data()); @@ -75,16 +90,41 @@ std::string execute_request(const std::string &host, const std::string &port, "Connection error - " + err + " - " + e.what()); } - return result; + co_return result; } +std::string execute_request_sync(const std::string &host, + const std::string &port, const http::request &req) +{ + + net::io_context ioc; + net::awaitable client_coroutine = + execute_request(host, port, req); + + std::promise promise; + auto fut = promise.get_future(); + + net::co_spawn(ioc, std::move(client_coroutine), + [&](const std::exception_ptr &eptr, std::string body) { + if (eptr) { + promise.set_exception(eptr); + } else { + promise.set_value(std::move(body)); + } + }); + + ioc.run(); + return fut.get(); +} +} // namespace + std::string dds::remote_config::http_api::get_info() const { http::request req{http::verb::get, "/info", version}; req.set(http::field::host, host_); req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); - return execute_request(host_, port_, req); + return execute_request_sync(host_, port_, req); } std::string dds::remote_config::http_api::get_configs( @@ -103,5 +143,5 @@ std::string dds::remote_config::http_api::get_configs( req.body() = std::move(request); req.keep_alive(true); - return execute_request(host_, port_, req); + return execute_request_sync(host_, port_, req); }; diff --git a/appsec/tests/fuzzer/CMakeLists.txt b/appsec/tests/fuzzer/CMakeLists.txt index 6fd6970fd6..eacd786caf 100644 --- a/appsec/tests/fuzzer/CMakeLists.txt +++ b/appsec/tests/fuzzer/CMakeLists.txt @@ -4,8 +4,11 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSIO set_target_properties(RapidJSON::rapidjson PROPERTIES INTERFACE_COMPILE_DEFINITIONS "RAPIDJSON_HAS_STDSTRING=1") add_executable(ddappsec_helper_fuzzer ${HELPER_SOURCE} main.cpp mutators.cpp) - set_target_properties(ddappsec_helper_fuzzer PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer-no-link,address,leak -fprofile-instr-generate -fcoverage-mapping") - set_target_properties(ddappsec_helper_fuzzer PROPERTIES LINK_FLAGS "-fsanitize=fuzzer-no-link,address,leak -fprofile-instr-generate -fcoverage-mapping") + set_target_properties(ddappsec_helper_fuzzer PROPERTIES + COMPILE_FLAGS "-fsanitize=fuzzer-no-link,address,leak -fprofile-instr-generate -fcoverage-mapping" + LINK_FLAGS "-fsanitize=fuzzer-no-link,address,leak -fprofile-instr-generate -fcoverage-mapping" + CXX_STANDARD 20 + ) target_include_directories(ddappsec_helper_fuzzer PRIVATE ${HELPER_INCLUDE_DIR}) execute_process( From 0fcd5020cf2ad93f04478c67eae0bdd658a14748 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 27 Jun 2024 18:34:58 +0100 Subject: [PATCH 010/103] Provide zend_empty_string for compat --- appsec/src/extension/php_compat.c | 7 +++++++ appsec/src/extension/php_compat.h | 1 + appsec/src/extension/request_abort.c | 8 +++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/appsec/src/extension/php_compat.c b/appsec/src/extension/php_compat.c index f4f8ddce87..608b16d097 100644 --- a/appsec/src/extension/php_compat.c +++ b/appsec/src/extension/php_compat.c @@ -6,6 +6,13 @@ #include "php_compat.h" #if PHP_VERSION_ID < 70300 +static zend_string _zend_empty_string_st = { + .gc.refcount = 1, + .gc.u.v.type = IS_STRING, + .gc.u.v.flags = IS_STR_PERSISTENT | IS_STR_INTERNED, +}; +zend_string *zend_empty_string = &_zend_empty_string_st; + zend_bool zend_ini_parse_bool(zend_string *str) { if ((ZSTR_LEN(str) == 4 && strcasecmp(ZSTR_VAL(str), "true") == 0) || diff --git a/appsec/src/extension/php_compat.h b/appsec/src/extension/php_compat.h index abd3205d52..14be5aa73b 100644 --- a/appsec/src/extension/php_compat.h +++ b/appsec/src/extension/php_compat.h @@ -43,6 +43,7 @@ static zend_always_inline zend_string *zend_string_init_interned( return zend_new_interned_string(ret); # endif } +extern zend_string *zend_empty_string; #endif #if PHP_VERSION_ID < 70300 diff --git a/appsec/src/extension/request_abort.c b/appsec/src/extension/request_abort.c index 08b8744666..0f0ed71373 100644 --- a/appsec/src/extension/request_abort.c +++ b/appsec/src/extension/request_abort.c @@ -67,7 +67,6 @@ static zend_string *_content_length_zstr; static zend_string *_location_zstr; static zend_string *_content_type_html_zstr; static zend_string *_content_type_json_zstr; -static zend_string *_empty_zstr; // older versions don't have zend_empty_string static THREAD_LOCAL_ON_ZTS int _response_code = DEFAULT_BLOCKING_RESPONSE_CODE; static THREAD_LOCAL_ON_ZTS dd_response_type _response_type = DEFAULT_RESPONSE_TYPE; @@ -107,7 +106,7 @@ static zend_string *nullable _read_file_contents(const char *nonnull path) php_stream_close(fs); if (!contents) { - return _empty_zstr; + return zend_empty_string; } return contents; } @@ -600,7 +599,6 @@ void dd_request_abort_startup() zend_string_init_interned(ZEND_STRL(HTML_CONTENT_TYPE), 1); _content_type_json_zstr = zend_string_init_interned(ZEND_STRL(JSON_CONTENT_TYPE), 1); - _empty_zstr = zend_string_init_interned(&(char){0}, 0, 1); if (!get_global_DD_APPSEC_TESTING()) { return; @@ -620,7 +618,7 @@ static zend_string *nonnull _get_json_blocking_template() // * if the template file is not found, return an empty template // * if the template file is empty, return the default if (!body_error_json) { - return _empty_zstr; + return zend_empty_string; } if (ZSTR_LEN(body_error_json) == 0) { zend_string_release(body_error_json); @@ -641,7 +639,7 @@ static zend_string *nonnull _get_html_blocking_template() zend_string *nullable body_error_html = _read_file_contents(ZSTR_VAL(html_template_file)); if (!body_error_html) { - return _empty_zstr; + return zend_empty_string; } if (ZSTR_LEN(body_error_html) == 0) { zend_string_release(body_error_html); From f947f87d76d253b48b4df600a70f179b3869f3aa Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Tue, 23 Jul 2024 15:43:41 +0100 Subject: [PATCH 011/103] integration tests toolchain: musl-glibc compat --- .../integration/src/docker/toolchain/Makefile | 6 ++++- .../src/docker/toolchain/alltypes.h.diff | 25 +++++++++++++++++++ .../src/docker/toolchain/locale.h.diff | 11 ++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 appsec/tests/integration/src/docker/toolchain/alltypes.h.diff create mode 100644 appsec/tests/integration/src/docker/toolchain/locale.h.diff diff --git a/appsec/tests/integration/src/docker/toolchain/Makefile b/appsec/tests/integration/src/docker/toolchain/Makefile index e1dad3ecfd..2290cff05e 100644 --- a/appsec/tests/integration/src/docker/toolchain/Makefile +++ b/appsec/tests/integration/src/docker/toolchain/Makefile @@ -55,10 +55,14 @@ src/%/.finger: %-$(LLVM_VERSION).src.tar.xz CC=$(GCC_TOOL_PREFIX)gcc \ AR=$(GCC_TOOL_PREFIX)ar \ RANLIB=$(GCC_TOOL_PREFIX)ranlib \ - ../../src/musl/configure --prefix=$(MUSL_SYSROOT) && \ + ../../src/musl/configure --prefix=$(MUSL_SYSROOT) && \ $(MAKE) -j $(shell nproc) && \ $(MAKE) install && \ popd && \ + pushd $(MUSL_SYSROOT)/include && \ + patch -p0 < /build/locale.h.diff && \ + patch -p0 < /build/alltypes.h.diff && \ + popd && \ touch $@ VERBOSE := 1 diff --git a/appsec/tests/integration/src/docker/toolchain/alltypes.h.diff b/appsec/tests/integration/src/docker/toolchain/alltypes.h.diff new file mode 100644 index 0000000000..bd9b86ffef --- /dev/null +++ b/appsec/tests/integration/src/docker/toolchain/alltypes.h.diff @@ -0,0 +1,25 @@ +--- bits/alltypes.h 2023-11-06 11:49:18.000000000 +0000 ++++ bits/alltypes.h 2024-04-22 09:30:09.927560000 +0000 +@@ -383,12 +383,20 @@ + + + #if defined(__NEED_pthread_attr_t) && !defined(__DEFINED_pthread_attr_t) +-typedef struct { union { int __i[sizeof(long)==8?14:9]; volatile int __vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; } __u; } pthread_attr_t; ++typedef struct { union { int __i[sizeof(long)==8?14:9]; volatile int __vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; ++#ifdef __aarch64__ ++ char __glibc_compat[64]; ++#endif ++} __u; } pthread_attr_t; + #define __DEFINED_pthread_attr_t + #endif + + #if defined(__NEED_pthread_mutex_t) && !defined(__DEFINED_pthread_mutex_t) +-typedef struct { union { int __i[sizeof(long)==8?10:6]; volatile int __vi[sizeof(long)==8?10:6]; volatile void *volatile __p[sizeof(long)==8?5:6]; } __u; } pthread_mutex_t; ++typedef struct { union { int __i[sizeof(long)==8?10:6]; volatile int __vi[sizeof(long)==8?10:6]; volatile void *volatile __p[sizeof(long)==8?5:6]; ++#ifdef __aarch64__ ++ char __glibc_compat[48]; ++#endif ++} __u; } pthread_mutex_t; + #define __DEFINED_pthread_mutex_t + #endif + diff --git a/appsec/tests/integration/src/docker/toolchain/locale.h.diff b/appsec/tests/integration/src/docker/toolchain/locale.h.diff new file mode 100644 index 0000000000..36de614b57 --- /dev/null +++ b/appsec/tests/integration/src/docker/toolchain/locale.h.diff @@ -0,0 +1,11 @@ +-- locale.h ++++ locale.h +@@ -71,7 +71,7 @@ + #define LC_COLLATE_MASK (1< Date: Tue, 23 Jul 2024 15:48:11 +0100 Subject: [PATCH 012/103] Handle some clang-tidy warnings in mock helper/agent --- appsec/tests/mock_helper/mock_dd_agent.cc | 94 ++++++++++--------- appsec/tests/mock_helper/mock_dd_agent.hpp | 8 +- appsec/tests/mock_helper/mock_helper_main.cc | 66 +++++++------ appsec/tests/mock_helper/mock_helper_main.hpp | 29 +++--- 4 files changed, 112 insertions(+), 85 deletions(-) diff --git a/appsec/tests/mock_helper/mock_dd_agent.cc b/appsec/tests/mock_helper/mock_dd_agent.cc index 0a1f0b199a..6da810a1b3 100644 --- a/appsec/tests/mock_helper/mock_dd_agent.cc +++ b/appsec/tests/mock_helper/mock_dd_agent.cc @@ -14,6 +14,7 @@ #include #include +#include #include namespace ip = asio::ip; @@ -22,26 +23,29 @@ using boost::system::error_code; class HttpClient { public: - HttpClient(EchoPipe &echo_pipe, ip::tcp::socket &&sock) - : echo_pipe_{echo_pipe}, sock_{std::move(sock)} - { - } - ~HttpClient() { SPDLOG_INFO("Closing HTTP connection"); } - - void do_request(yield_context yield) - { - try { - _do_request(yield); - } catch (const std::exception &e) { - SPDLOG_WARN("Error handling HTTP request: {}", e.what()); - } catch (...) { - SPDLOG_WARN("Error handling HTTP request"); - } - SPDLOG_DEBUG("Finished request handling"); + HttpClient(EchoPipe &echo_pipe, ip::tcp::socket &&sock) + : echo_pipe_{echo_pipe}, sock_{std::move(sock)} + {} + HttpClient(const HttpClient &) = delete; + HttpClient(HttpClient &&) = delete; + HttpClient &operator=(const HttpClient &) = delete; + HttpClient &operator=(HttpClient &&) = delete; + ~HttpClient() { SPDLOG_INFO("Closing HTTP connection"); } + + void do_request(const yield_context &yield) + { + try { + _do_request(yield); + } catch (const std::exception &e) { + SPDLOG_WARN("Error handling HTTP request: {}", e.what()); + } catch (...) { + SPDLOG_WARN("Error handling HTTP request"); + } + SPDLOG_DEBUG("Finished request handling"); } private: - void _do_request(yield_context yield) + void _do_request(const yield_context& yield) { SPDLOG_DEBUG("do_request_line"); do_request_line(yield); @@ -51,6 +55,9 @@ class HttpClient { if (method_ == "PUT" && uri_ == "/v0.4/traces") { SPDLOG_DEBUG("do_traces"); do_traces(yield); + } else if (method_ == "POST" && + uri_ == "/telemetry/proxy/api/v2/apmtelemetry") { + SPDLOG_INFO("Ignoring telemetry data"); } else { SPDLOG_WARN("Don't know how to handle {} {}", method_, uri_); } @@ -61,11 +68,12 @@ class HttpClient { void do_request_line(yield_context yield) { - size_t size = asio::async_read_until( + size_t const size = asio::async_read_until( sock_, asio::dynamic_buffer(buf_), '\r', yield); SPDLOG_DEBUG("Read request line with size {}", size - 1); - std::sregex_iterator it{buf_.begin(), buf_.begin() + size, word}; + std::sregex_iterator it{ + buf_.begin(), buf_.begin() + static_cast(size), word}; if (it != itend) { method_ = it->str(); SPDLOG_DEBUG("Method: {}", method_); @@ -96,14 +104,14 @@ class HttpClient { consume_chars(yield, '\n'); } - void do_headers(yield_context yield) + void do_headers(const yield_context& yield) { while (do_single_header(yield)) {} } bool do_single_header(yield_context yield) { - size_t read = asio::async_read_until( + size_t const read = asio::async_read_until( sock_, asio::dynamic_buffer(buf_), '\r', yield); if (read == 1) { @@ -112,8 +120,9 @@ class HttpClient { } std::smatch match; std::string header; - if (std::regex_search( - buf_.cbegin(), buf_.cbegin() + read, match, header_name)) { + if (std::regex_search(buf_.cbegin(), + buf_.cbegin() + static_cast(read), match, + header_name)) { header = match.str(); } else { throw std::runtime_error{"No header name found"}; @@ -134,7 +143,7 @@ class HttpClient { return true; } - void do_traces(yield_context yield) + void do_traces(const yield_context& yield) { auto count_header = headers_.find(trace_count_header); if (count_header == headers_.end()) { @@ -151,10 +160,10 @@ class HttpClient { void do_single_trace(yield_context yield) { auto dyn_buf = asio::dynamic_buffer(buf_); - size_t read = asio::async_read_until(sock_, dyn_buf, '\r', yield); + size_t const read = asio::async_read_until(sock_, dyn_buf, '\r', yield); // read the message length - std::string_view size_str{&buf_[0], read - 1}; + std::string_view const size_str{buf_.data(), read - 1}; SPDLOG_DEBUG("Trace size as string: {}", size_str); size_t msg_size; @@ -168,7 +177,7 @@ class HttpClient { if (buf_.size() < msg_size + 2 /* for \r\n */) { buf_.reserve(msg_size + 2); - size_t missing = msg_size + 2 - buf_.size(); + size_t const missing = msg_size + 2 - buf_.size(); asio::async_read(sock_, dyn_buf.prepare(missing), yield); } @@ -210,33 +219,31 @@ class HttpClient { } template - void check_n_chars(Tuple &&tuple, std::index_sequence) + void check_n_chars(Tuple &&tuple, std::index_sequence /*seq*/) { auto check_char = [](char read, char exp) { if (read != exp) { throw std::runtime_error{ std::string{"Expected read char to be "} + - boost::lexical_cast( - static_cast(exp)) + + std::to_string(static_cast(exp)) + " but it was " + - boost::lexical_cast( - static_cast(read))}; + std::to_string(static_cast(read))}; } }; - [[maybe_unused]] int dummy[] = { - (check_char(buf_[Is], std::get(tuple)), 0)...}; + (check_char(buf_[Is], std::get(tuple)), ...); } void do_response(yield_context yield) { - static const char resp[] = "HTTP/1.1 200 OK\r\n" - "Content-Type: application/json\r\n" - "Content-Length: 40\r\n" - "\r\n" - R"({"rate_by_service":{"service:,env:":1}}")" - "\n"; + static const std::array resp{ + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json\r\n" + "Content-Length: 40\r\n" + "\r\n" + R"({"rate_by_service":{"service:,env:":1}}")" + "\n"}; asio::async_write( - sock_, asio::const_buffer(resp, sizeof(resp) - 1), yield); + sock_, asio::const_buffer(resp.data(), sizeof(resp) - 1), yield); } void consume(size_t bytes) @@ -249,8 +256,11 @@ class HttpClient { buf_.erase(0, bytes); } + // NOLINTNEXTLINE static inline const std::regex word{R"(\S+)"}; + // NOLINTNEXTLINE static inline const std::regex header_name{R"(^[^:]+(?=:))"}; + // NOLINTNEXTLINE static inline const std::sregex_iterator itend; static inline constexpr std::string_view trace_count_header{ "x-datadog-trace-count"}; @@ -286,7 +296,7 @@ void HttpServerDispatcher::start() acceptor_.listen(backlog); // synchronous; may throw SPDLOG_INFO("Listening for TCP connections (mock datadog agent)"); } -void HttpServerDispatcher::run_loop(yield_context yield) +void HttpServerDispatcher::run_loop(const yield_context& yield) { SPDLOG_INFO("Started HttpServerDisp:atcher loop"); while (!iocontext.stopped()) { diff --git a/appsec/tests/mock_helper/mock_dd_agent.hpp b/appsec/tests/mock_helper/mock_dd_agent.hpp index b60178fdd4..f6632e4683 100644 --- a/appsec/tests/mock_helper/mock_dd_agent.hpp +++ b/appsec/tests/mock_helper/mock_dd_agent.hpp @@ -11,15 +11,19 @@ #include class HttpServerDispatcher { +public: static constexpr int backlog = 1; - public: HttpServerDispatcher(EchoPipe &echo_pipe, asio::ip::port_type port); + HttpServerDispatcher(const HttpServerDispatcher&) = delete; + HttpServerDispatcher& operator=(const HttpServerDispatcher&) = delete; + HttpServerDispatcher(HttpServerDispatcher&&) = delete; + HttpServerDispatcher& operator=(HttpServerDispatcher&&) = delete; ~HttpServerDispatcher(); void start(); - void run_loop(boost::asio::yield_context yield); + void run_loop(const boost::asio::yield_context& yield); private: EchoPipe &echo_pipe_; diff --git a/appsec/tests/mock_helper/mock_helper_main.cc b/appsec/tests/mock_helper/mock_helper_main.cc index 6982260fde..49b92ffe53 100644 --- a/appsec/tests/mock_helper/mock_helper_main.cc +++ b/appsec/tests/mock_helper/mock_helper_main.cc @@ -17,11 +17,11 @@ #define RAPIDJSON_NO_SIZETYPEDEFINE namespace rapidjson { using SizeType = std::uint32_t; -} +} // namespace rapidjson #include #include -#include #include +#include #include @@ -39,13 +39,12 @@ using SizeType = std::uint32_t; #include #include -#include "mock_helper_main.hpp" #include "mock_dd_agent.hpp" +#include "mock_helper_main.hpp" #include namespace po = boost::program_options; namespace asio = boost::asio; -namespace ip = asio::ip; namespace local = asio::local; namespace posix = asio::posix; using boost::system::error_code; @@ -60,7 +59,7 @@ MsgpackToJson::MsgpackToJson(const char *buffer, size_t size) void MsgpackToJson::convert() { mpack_tree_parse(&tree_); - mpack_error_t err = mpack_tree_error(&tree_); + const mpack_error_t err = mpack_tree_error(&tree_); if (err != mpack_ok) { throw std::runtime_error{std::string{"Error parsing msgpack: "} + mpack_error_to_string(err)}; @@ -69,9 +68,11 @@ void MsgpackToJson::convert() mpack_node_t root = mpack_tree_root(&tree_); write(root); } + +// NOLINTNEXTLINE(misc-no-recursion) void MsgpackToJson::write(mpack_node_t &node) { - mpack_type_t type = mpack_node_type(node); + const mpack_type_t type = mpack_node_type(node); switch (type) { case mpack_type_nil: writer_.Null(); @@ -108,10 +109,10 @@ void MsgpackToJson::write(mpack_node_t &node) writer_.StartObject(); auto len = mpack_node_map_count(node); for (decltype(len) i = 0; i < len; i++) { - mpack_node_t key = mpack_node_map_key_at(node, i); + mpack_node_t const key = mpack_node_map_key_at(node, i); mpack_node_t value = mpack_node_map_value_at(node, i); - mpack_type_t key_type = mpack_node_type(key); + mpack_type_t const key_type = mpack_node_type(key); if (key_type != mpack_type_str) { throw std::runtime_error{ "saw nonstring map key in msgpack message"}; @@ -138,9 +139,9 @@ EchoPipe::EchoPipe() : stream_{try_fds()} } } void EchoPipe::write( - const asio::const_buffer data, asio::yield_context yield) + asio::const_buffer buff, asio::yield_context yield) { - std::array buffers{data, {"", 1}}; + std::array buffers{buff, {"", 1}}; SPDLOG_INFO("Writing to echo pipe {} + 1 bytes", buffers[0].size()); SPDLOG_DEBUG("Content: {}", std::string_view{ @@ -171,20 +172,20 @@ std::optional EchoPipe::try_single_fd(int fd) { struct ::stat statbuf = {0}; if (fstat(fd, &statbuf) == -1) { - error_code ec = {errno, boost::system::system_category()}; + error_code const ec = {errno, boost::system::system_category()}; SPDLOG_INFO("fstat() failed for fd {}: {}", fd, ec.message()); return std::nullopt; } - if (!(statbuf.st_mode & (S_IFIFO | S_IFCHR))) { + if ((statbuf.st_mode & (S_IFIFO | S_IFCHR)) == 0) { SPDLOG_INFO( "File descriptor {0} is not a FIFO or character device: {1:o}", fd, statbuf.st_mode & S_IFMT); return std::nullopt; } - int new_fd = ::dup(fd); + const int new_fd = ::dup(fd); // NOLINT(android-cloexec-dup) if (new_fd == -1) { - error_code ec = {errno, boost::system::system_category()}; + error_code const ec = {errno, boost::system::system_category()}; SPDLOG_INFO("Call to dup of fd {} failed: {}", fd, ec.message()); return std::nullopt; } @@ -200,11 +201,11 @@ struct OwningBuffer { OwningBuffer(const char *buf, std::size_t len) : buf_{buf}, len_{len} {} OwningBuffer(const OwningBuffer &) = delete; const OwningBuffer &operator=(const OwningBuffer &) = delete; - OwningBuffer(OwningBuffer &&oth) : OwningBuffer{} + OwningBuffer(OwningBuffer &&oth) noexcept : OwningBuffer{} { *this = std::move(oth); } - const OwningBuffer &operator=(OwningBuffer &&oth) + OwningBuffer &operator=(OwningBuffer &&oth) noexcept { std::free(const_cast(buf_)); buf_ = oth.buf_; @@ -231,6 +232,7 @@ class MpackWriter { std::free(data_); } + // NOLINTNEXTLINE(misc-no-recursion) MpackWriter &operator<<(const rapidjson::Value &val) { if (val.IsInt() || val.IsInt64()) { @@ -420,22 +422,22 @@ class Client { { JsonToMsgpack json_to_msgpack{doc}; json_to_msgpack.convert(); - if (json_to_msgpack.delay()) { + if (json_to_msgpack.delay() != 0U) { SPDLOG_INFO("Will wait {} seconds before sending response", json_to_msgpack.delay()); asio::steady_timer timer{iocontext}; timer.expires_after(std::chrono::seconds{json_to_msgpack.delay()}); timer.async_wait(yield); } - OwningBuffer buf = json_to_msgpack.move_buffer(); + OwningBuffer const buf = json_to_msgpack.move_buffer(); - Header h; + Header h{}; memcpy(&h.marker, "dds", 4); h.size = buf.len_; SPDLOG_INFO("Writing response; size {} (header) + {} (body)", sizeof(h), buf.len_); - std::array buffers = { + std::array const buffers = { asio::const_buffer(reinterpret_cast(&h), sizeof(h)), asio::const_buffer(buf.buf_, buf.len_), }; @@ -516,7 +518,7 @@ class Dispatcher { socklen_t len = sizeof(addr); if (::getsockname(fd, reinterpret_cast(&addr), &len) == -1) { - error_code ec = {errno, boost::system::system_category()}; + error_code const ec = {errno, boost::system::system_category()}; SPDLOG_INFO("Call to getsockname failed on socket {}: {}", fd, ec.message()); return std::nullopt; @@ -608,13 +610,19 @@ static void _fatal_signal_handler(int signum) { int main(int argc, char *argv[]) { - ::signal(SIGSEGV, _fatal_signal_handler); - ::signal(SIGABRT, _fatal_signal_handler); - auto console = spdlog::stderr_logger_mt("console"); spdlog::set_default_logger(console); spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v at %s:%!"); + auto prev = ::signal(SIGSEGV, _fatal_signal_handler); + if (prev == SIG_ERR) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) + SPDLOG_CRITICAL("Could not set signal handler for SIGSEGV"); + } + prev = ::signal(SIGABRT, _fatal_signal_handler); + if (prev == SIG_ERR) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) + SPDLOG_CRITICAL("Could not set signal handler for SIGABRT"); + } + po::options_description opt_desc{"Allowed options"}; // clang-format off opt_desc.add_options() @@ -641,11 +649,11 @@ int main(int argc, char *argv[]) } po::notify(opt_vm); - if (opt_vm.count("help")) { + if (opt_vm.count("help") != 0U) { std::cerr << opt_desc << "\n"; return 1; } - if (!opt_vm.count("response")) { + if (opt_vm.count("response") == 0U) { std::cerr << "At least one response is required\n"; return 1; } @@ -673,15 +681,15 @@ int main(int argc, char *argv[]) }); std::optional signal_lock; - if (opt_vm.count("lock")) { + if (opt_vm.count("lock") != 0U) { signal_lock.emplace(opt_vm["lock"].as()); spawn(iocontext, [&signal_lock](auto yield) { signal_lock->lock(yield); }); } asio::signal_set signals{iocontext, SIGINT, SIGTERM}; - signals.async_wait([](const error_code &err, int signal) { - SPDLOG_INFO("Got signal {}; exiting", signal); + signals.async_wait([](const error_code & /*err*/, int signal) { + SPDLOG_INFO("Got signal {}; exiting", signal); // NOLINT iocontext.stop(); }); diff --git a/appsec/tests/mock_helper/mock_helper_main.hpp b/appsec/tests/mock_helper/mock_helper_main.hpp index 65d297c60f..1a4e41b196 100644 --- a/appsec/tests/mock_helper/mock_helper_main.hpp +++ b/appsec/tests/mock_helper/mock_helper_main.hpp @@ -5,13 +5,13 @@ // Copyright 2021 Datadog, Inc. #pragma once +#include #include #include -#include -#include -#include #include +#include +#include #include #include @@ -26,20 +26,21 @@ class EchoPipe { // lifetime: till the end of the program public: EchoPipe(); - void write(const asio::const_buffer buff, asio::yield_context yield); + void write(asio::const_buffer buff, asio::yield_context yield); template void add_close_cb(Callable &&cb) { - spawn(iocontext, [this, cb = std::move(cb)](auto yield) { + spawn( + iocontext, [this, cb = std::forward(cb)](auto yield) { #ifdef __linux__ - auto wait_type = posix::stream_descriptor::wait_error; + auto wait_type = posix::stream_descriptor::wait_error; #else auto wait_type = posix::stream_descriptor::wait_read; #endif - stream_.async_wait(wait_type, yield); - SPDLOG_INFO("The echo pipe was closed"); - cb(); - }); + stream_.async_wait(wait_type, yield); + SPDLOG_INFO("The echo pipe was closed"); // NOLINT + cb(); + }); } private: @@ -59,9 +60,13 @@ class MsgpackToJson { template , void>> MsgpackToJson(const T *buffer, size_t size) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) : MsgpackToJson{reinterpret_cast(buffer), size} {} MsgpackToJson(const MsgpackToJson &) = delete; + MsgpackToJson(MsgpackToJson &&) = delete; + MsgpackToJson &operator=(const MsgpackToJson &) = delete; + MsgpackToJson &operator=(MsgpackToJson &&) = delete; ~MsgpackToJson() { mpack_tree_destroy(&tree_); } @@ -73,14 +78,14 @@ class MsgpackToJson { asio::const_buffer asio_buffer() { const char *str = buffer_.GetString(); - size_t len = buffer_.GetLength(); + const size_t len = buffer_.GetLength(); return {str, len}; } private: void write(mpack_node_t &node); - mpack_tree_t tree_; + mpack_tree_t tree_{}; rapidjson::StringBuffer buffer_; writer_t writer_{buffer_}; }; From 069c993fd96ab398ea2a305db9f1b146fa6d5ceb Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Wed, 24 Jul 2024 18:53:28 +0100 Subject: [PATCH 013/103] Fix two tidy lint problems --- appsec/src/helper/network/msgpack_helpers.cpp | 1 + appsec/src/helper/network/proto.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/appsec/src/helper/network/msgpack_helpers.cpp b/appsec/src/helper/network/msgpack_helpers.cpp index 7778c25c22..6effe74db8 100644 --- a/appsec/src/helper/network/msgpack_helpers.cpp +++ b/appsec/src/helper/network/msgpack_helpers.cpp @@ -5,6 +5,7 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "msgpack_helpers.hpp" +// NOLINTNEXTLINE(modernize-concat-nested-namespaces) namespace msgpack { MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) { namespace adaptor { diff --git a/appsec/src/helper/network/proto.cpp b/appsec/src/helper/network/proto.cpp index 9fa03d76e2..9af84f10aa 100644 --- a/appsec/src/helper/network/proto.cpp +++ b/appsec/src/helper/network/proto.cpp @@ -5,6 +5,7 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "proto.hpp" +// NOLINTNEXTLINE(modernize-concat-nested-namespaces) namespace msgpack { MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) { namespace adaptor { From a457a4f55531cb1ab459b8203a2a645181ac285e Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Tue, 23 Jul 2024 15:47:44 +0100 Subject: [PATCH 014/103] Move appsec helper to sidecar --- .circleci/continue_config.yml | 16 +- appsec/README.md | 6 +- appsec/cmake/Toolchain.aarch64.cmake | 25 - appsec/cmake/Toolchain.x86_64.cmake | 25 - appsec/cmake/extension.cmake | 1 + appsec/cmake/helper.cmake | 29 +- appsec/cmake/patchelf.cmake | 13 +- appsec/cmake/strip_libc.sh | 17 + appsec/src/extension/configuration.h | 4 +- appsec/src/extension/ddappsec.c | 10 +- appsec/src/extension/ddappsec.h | 1 + appsec/src/extension/helper_process.c | 841 ++---------------- appsec/src/extension/helper_process.h | 7 + appsec/src/extension/network.c | 74 +- appsec/src/helper/client.cpp | 1 + appsec/src/helper/config.cpp | 64 +- appsec/src/helper/config.hpp | 44 +- appsec/src/helper/engine_settings.cpp | 69 +- appsec/src/helper/main.cpp | 144 ++- appsec/src/helper/network/acceptor.cpp | 27 +- appsec/src/helper/network/acceptor.hpp | 7 +- .../helper/remote_config/client_handler.cpp | 7 +- appsec/src/helper/remote_config/http_api.cpp | 2 +- appsec/src/helper/runner.cpp | 43 +- appsec/src/helper/runner.hpp | 18 +- .../extension/helper_extra_args_parsing.phpt | 166 ---- .../extension/helper_launch_failure.phpt | 40 - .../extension/helper_launch_lock_taken.phpt | 41 - appsec/tests/extension/helper_launch_ok.phpt | 95 -- appsec/tests/fuzzer/main.cpp | 149 +++- appsec/tests/fuzzer/mutators.hpp | 3 + appsec/tests/helper/config_test.cpp | 83 +- appsec/tests/integration/build.gradle | 4 +- .../integration/src/docker/toolchain/Makefile | 20 +- .../src/docker/toolchain/Toolchain.cmake | 8 +- .../src/docker/toolchain/glibc_compat.c | 187 ++++ .../appsec/php/docker/AppSecContainer.groovy | 3 +- .../src/test/bin/enable_extensions.sh | 4 +- appsec/third_party/CMakeLists.txt | 4 + components-rs/ddtrace.h | 6 + components-rs/sidecar.rs | 55 +- datadog-setup.php | 39 +- .../installer/test_appsec_install_disabled.sh | 2 +- .../installer/test_appsec_install_enabled.sh | 2 +- ext/configuration.h | 1 + ext/ddtrace.c | 1 - ext/sidecar.c | 25 + libdatadog | 2 +- tooling/bin/generate-final-artifact.sh | 10 +- 49 files changed, 994 insertions(+), 1451 deletions(-) delete mode 100644 appsec/cmake/Toolchain.aarch64.cmake delete mode 100644 appsec/cmake/Toolchain.x86_64.cmake create mode 100755 appsec/cmake/strip_libc.sh delete mode 100644 appsec/tests/extension/helper_extra_args_parsing.phpt delete mode 100644 appsec/tests/extension/helper_launch_failure.phpt delete mode 100644 appsec/tests/extension/helper_launch_lock_taken.phpt delete mode 100644 appsec/tests/extension/helper_launch_ok.phpt create mode 100644 appsec/tests/integration/src/docker/toolchain/glibc_compat.c diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 8ef8aeda8f..6d5b659e60 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3380,23 +3380,19 @@ jobs: - <<: *STEP_CHECKOUT - <<: *STEP_ATTACH_WORKSPACE - setup_docker: - docker_image: "datadog/libddwaf:toolchain" + docker_image: "public.ecr.aws/b1o7r7e0/nginx_musl_toolchain@sha256:afbb4503fd930729e884e18074b227b2ea182dd2895e580635ca119250aa0fe2" - run: mkdir -p appsec_$(uname -m) - - run: - name: Create clang symlinks - command: | - ln -s /usr/bin/clang++-16 /usr/bin/clang++ - ln -s /usr/bin/clang-16 /usr/bin/clang - ln -s /usr/bin/clang-cpp-16 /usr/bin/clang-cpp - run: name: Build command: | git config --global --add safe.directory $(pwd)/appsec/third_party/libddwaf mkdir -p appsec/build ; cd appsec/build - cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDD_APPSEC_BUILD_EXTENSION=OFF -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../cmake/Toolchain.$(uname -m).cmake + cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDD_APPSEC_BUILD_EXTENSION=OFF \ + -DDD_APPSEC_ENABLE_PATCHELF_LIBC=ON \ + -DCMAKE_TOOLCHAIN_FILE=/sysroot/$(arch)-none-linux-musl/Toolchain.cmake make -j $(nproc) - objcopy --compress-debug-sections ddappsec-helper - cp -v ddappsec-helper ../../appsec_$(uname -m)/ddappsec-helper + objcopy --compress-debug-sections libddappsec-helper.so + cp -v libddappsec-helper.so ../../appsec_$(uname -m)/libddappsec-helper.so - run: name: Test command: | diff --git a/appsec/README.md b/appsec/README.md index 9594f9857c..d9f9d88023 100644 --- a/appsec/README.md +++ b/appsec/README.md @@ -74,15 +74,15 @@ cd build cmake .. make -j ``` -This will produce the extension, `ddappsec.so` and the helper process `ddappsec-helper`. +This will produce the extension, `ddappsec.so` and the helper library `libddappsec-helper.so`. Alternatively, to build the extension but not the helper, you can disable the helper build on the cmake step: ``` -cmake .. -DDD_APPSEC_BUILD_HELPER=OFF +cmake .. -DDD_APPSEC_BUILD_HELPER=OFF ``` Similarly, to build the helper but not the extension: ``` -cmake .. DDD_APPSEC_BUILD_EXTENSION=OFF +cmake .. DDD_APPSEC_BUILD_EXTENSION=OFF ``` #### Testing the extension diff --git a/appsec/cmake/Toolchain.aarch64.cmake b/appsec/cmake/Toolchain.aarch64.cmake deleted file mode 100644 index dfe24329ca..0000000000 --- a/appsec/cmake/Toolchain.aarch64.cmake +++ /dev/null @@ -1,25 +0,0 @@ -set(sysroot /sysroot/aarch64-none-linux-musl) -set(arch aarch64) -set(interpreter ld-musl-aarch64.so.1) -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR ${arch}) -set(CMAKE_SYSROOT ${sysroot}) -set(CMAKE_AR /usr/bin/llvm-ar-16) -set(triple ${arch}-none-linux-musl) -set(CMAKE_ASM_COMPILER_TARGET ${triple}) -set(CMAKE_C_COMPILER /usr/bin/clang-16) -set(CMAKE_C_COMPILER_TARGET ${triple}) - -set(c_cxx_flags "-Qunused-arguments -rtlib=compiler-rt -unwindlib=libunwind -static-libgcc -isystem/sysroot/aarch64-none-linux-musl/usr/include/c++/v1/") -set(CMAKE_C_FLAGS ${c_cxx_flags}) -set(CMAKE_CXX_COMPILER /usr/bin/clang++-16) -set(CMAKE_CXX_COMPILER_TARGET ${triple}) -set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${c_cxx_flags}") - -set(linker_flags "-v -fuse-ld=lld-16 -static -nodefaultlibs -lc++ -lc++abi ${sysroot}/usr/lib/libclang_rt.builtins.a -lunwind -lc ${sysroot}/usr/lib/libclang_rt.builtins.a -resource-dir ${sysroot}/usr/lib/resource_dir") -set(CMAKE_EXE_LINKER_FLAGS_INIT "${linker_flags}") -set(CMAKE_SHARED_LINKER_FLAGS_INIT ${linker_flags}) - -set(CMAKE_NM /usr/bin/llvm-nm-16) -set(CMAKE_RANLIB /usr/bin/llvm-ranlib-16) -set(CMAKE_STRIP /usr/bin/aarch64-linux-gnu-strip) # llvm-strip-11 doesn't seem to work correctly diff --git a/appsec/cmake/Toolchain.x86_64.cmake b/appsec/cmake/Toolchain.x86_64.cmake deleted file mode 100644 index 1ff15264d8..0000000000 --- a/appsec/cmake/Toolchain.x86_64.cmake +++ /dev/null @@ -1,25 +0,0 @@ -set(sysroot /sysroot/x86_64-none-linux-musl) -set(arch x86_64) -set(interpreter ld-musl-x86_64.so.1) -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR ${arch}) -set(CMAKE_SYSROOT ${sysroot}) -set(CMAKE_AR /usr/bin/llvm-ar-16) -set(triple ${arch}-none-linux-musl) -set(CMAKE_ASM_COMPILER_TARGET ${triple}) -set(CMAKE_C_COMPILER /usr/bin/clang-16) -set(CMAKE_C_COMPILER_TARGET ${triple}) - -set(c_cxx_flags "-Qunused-arguments -rtlib=compiler-rt -unwindlib=libunwind -static-libgcc -isystem/sysroot/x86_64-none-linux-musl/usr/include/c++/v1/") -set(CMAKE_C_FLAGS ${c_cxx_flags}) -set(CMAKE_CXX_COMPILER /usr/bin/clang++-16) -set(CMAKE_CXX_COMPILER_TARGET ${triple}) -set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${c_cxx_flags}") - -set(linker_flags "-v -fuse-ld=lld-16 -static -nodefaultlibs -lc++ -lc++abi ${sysroot}/usr/lib/libclang_rt.builtins.a -lunwind -lc ${sysroot}/usr/lib/libclang_rt.builtins.a -resource-dir ${sysroot}/usr/lib/resource_dir") -set(CMAKE_EXE_LINKER_FLAGS_INIT "${linker_flags}") -set(CMAKE_SHARED_LINKER_FLAGS_INIT ${linker_flags}) - -set(CMAKE_NM /usr/bin/llvm-nm-16) -set(CMAKE_RANLIB /usr/bin/llvm-ranlib-16) -set(CMAKE_STRIP /usr/bin/x86_64-linux-gnu-strip) # llvm-strip-11 doesn't seem to work correctly diff --git a/appsec/cmake/extension.cmake b/appsec/cmake/extension.cmake index 9f1c355c89..6739e9d28c 100644 --- a/appsec/cmake/extension.cmake +++ b/appsec/cmake/extension.cmake @@ -24,6 +24,7 @@ set_target_properties(extension PROPERTIES target_compile_definitions(extension PRIVATE TESTING=1 ZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -D_GNU_SOURCE) target_link_libraries(extension PRIVATE mpack PhpConfig zai) +target_include_directories(extension PRIVATE ..) macro(target_linker_flag_conditional target) # flags as argv try_compile(LINKER_HAS_FLAG "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/check.c" diff --git a/appsec/cmake/helper.cmake b/appsec/cmake/helper.cmake index 8f9e2a2f44..cb6a7d3c02 100644 --- a/appsec/cmake/helper.cmake +++ b/appsec/cmake/helper.cmake @@ -10,28 +10,49 @@ configure_file(src/helper/version.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/src/helper/ set(HELPER_SOURCE_DIR src/helper) set(HELPER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/helper) -file(GLOB_RECURSE HELPER_SOURCE ${HELPER_SOURCE_DIR}/*.cpp) +file(GLOB_RECURSE HELPER_SOURCE CONFIGURE_DEPENDS + ${HELPER_SOURCE_DIR}/*.cpp ${HELPER_SOURCE_DIR}/*.c) list(FILTER HELPER_SOURCE EXCLUDE REGEX "^.*main\.cpp$") add_library(helper_objects OBJECT ${HELPER_SOURCE}) set_target_properties(helper_objects PROPERTIES + CXX_VISIBILITY_PRESET hidden CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES POSITION_INDEPENDENT_CODE 1) target_include_directories(helper_objects PUBLIC ${HELPER_INCLUDE_DIR}) target_compile_definitions(helper_objects PUBLIC SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) +target_compile_options(helper_objects PRIVATE -ftls-model=global-dynamic) target_link_libraries(helper_objects PUBLIC libddwaf_objects pthread spdlog cpp-base64 msgpack_c RapidJSON::rapidjson Boost::system zlibstatic) -add_executable(ddappsec-helper src/helper/main.cpp - $ - $) +add_library(ddappsec-helper SHARED + src/helper/main.cpp + $ + $) target_link_libraries(ddappsec-helper helper_objects) # for its PUBLIC deps +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_compile_options(ddappsec-helper PRIVATE -ftls-model=global-dynamic) + # Bind symbols lookup of symbols defined in the library to the library itself + # also avoids relocation problems with libc++.a on linux/aarch64 + target_link_options(ddappsec-helper PRIVATE -Wl,-Bsymbolic) +endif() +set_target_properties(ddappsec-helper PROPERTIES + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED YES + POSITION_INDEPENDENT_CODE 1 + DEBUG_POSTFIX "" + SUFFIX .so +) + +patch_away_libc(ddappsec-helper) try_compile(STDLIBXX_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_fslib.cpp CXX_STANDARD 20 CXX_STANDARD_REQUIRED TRUE) try_compile(STDLIBXX_FS_NEEDS_STDCXXFS ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_fslib.cpp CXX_STANDARD 20 CXX_STANDARD_REQUIRED TRUE diff --git a/appsec/cmake/patchelf.cmake b/appsec/cmake/patchelf.cmake index 892c90f2ee..4ae6873828 100644 --- a/appsec/cmake/patchelf.cmake +++ b/appsec/cmake/patchelf.cmake @@ -1,5 +1,5 @@ function(patch_away_libc target) - if (NOT ${DD_APPSEC_ENABLE_PATCHELF_LIBC}) + if(NOT ${DD_APPSEC_ENABLE_PATCHELF_LIBC}) return() endif() @@ -8,10 +8,15 @@ function(patch_away_libc target) endif() find_program(PATCHELF patchelf) - if (PATCHELF STREQUAL "PATCHELF-NOTFOUND") + find_program(READELF readelf) + if(PATCHELF STREQUAL "PATCHELF-NOTFOUND") message(WARNING "Patchelf not found. Can't build glibc + musl binaries") else() - add_custom_command(TARGET ${target} POST_BUILD - COMMAND patchelf --remove-needed libc.so $ ${SYMBOL_FILE}) + if(READELF STREQUAL "READELF-NOTFOUND") + message(WARNING "readelf not found. Can't build glibc + musl binaries") + else() + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_SOURCE_DIR}/cmake/strip_libc.sh "${PATCHELF}" "${READELF}" $) + endif() endif() endfunction() diff --git a/appsec/cmake/strip_libc.sh b/appsec/cmake/strip_libc.sh new file mode 100755 index 0000000000..128f566b28 --- /dev/null +++ b/appsec/cmake/strip_libc.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +main() { + local patchelf=$1 + local readelf=$2 + local target=$3 + + "$patchelf" $( + "$readelf" -d "$target" 2>/dev/null | grep libc\\. | grep NEEDED | \ + awk -F'[][]' '{print "--remove-needed " $2;}' | xargs + ) \ + "$target" +} + +main "$@" diff --git a/appsec/src/extension/configuration.h b/appsec/src/extension/configuration.h index bae726683d..9fdefb0404 100644 --- a/appsec/src/extension/configuration.h +++ b/appsec/src/extension/configuration.h @@ -45,11 +45,11 @@ extern bool runtime_config_first_init; CONFIG(CUSTOM(INT), DD_APPSEC_LOG_LEVEL, "warn", .parser = dd_parse_log_level) \ SYSCFG(STRING, DD_APPSEC_LOG_FILE, "php_error_reporting") \ SYSCFG(BOOL, DD_APPSEC_HELPER_LAUNCH, "true") \ - CONFIG(STRING, DD_APPSEC_HELPER_PATH, DD_BASE("bin/ddappsec-helper")) \ + CONFIG(STRING, DD_APPSEC_HELPER_PATH, DD_BASE("bin/libddappsec-helper.so")) \ CONFIG(STRING, DD_APPSEC_HELPER_RUNTIME_PATH, "/tmp", .ini_change = dd_on_runtime_path_update) \ SYSCFG(STRING, DD_APPSEC_HELPER_LOG_FILE, "/dev/null") \ + SYSCFG(STRING, DD_APPSEC_HELPER_LOG_LEVEL, "info") \ CONFIG(CUSTOM(SET), DD_EXTRA_SERVICES, "", .parser = _parse_list) \ - CONFIG(STRING, DD_APPSEC_HELPER_EXTRA_ARGS, "") \ CONFIG(STRING, DD_SERVICE, "") \ CONFIG(STRING, DD_ENV, "") \ CONFIG(STRING, DD_VERSION, "") \ diff --git a/appsec/src/extension/ddappsec.c b/appsec/src/extension/ddappsec.c index e0f60b3bd3..7fb581ee86 100644 --- a/appsec/src/extension/ddappsec.c +++ b/appsec/src/extension/ddappsec.c @@ -241,10 +241,14 @@ static PHP_MSHUTDOWN_FUNCTION(ddappsec) return SUCCESS; } -static pthread_once_t _rinit_once_control = PTHREAD_ONCE_INIT; - static void _rinit_once() { dd_config_first_rinit(); } +void dd_appsec_rinit_once() +{ + static pthread_once_t _rinit_once_control = PTHREAD_ONCE_INIT; + pthread_once(&_rinit_once_control, _rinit_once); +} + // NOLINTNEXTLINE static PHP_RINIT_FUNCTION(ddappsec) { @@ -254,7 +258,7 @@ static PHP_RINIT_FUNCTION(ddappsec) // Safety precaution DDAPPSEC_G(during_request_shutdown) = false; - pthread_once(&_rinit_once_control, _rinit_once); + dd_appsec_rinit_once(); zai_config_rinit(); _check_enabled(); diff --git a/appsec/src/extension/ddappsec.h b/appsec/src/extension/ddappsec.h index a45381ec46..e019a2850b 100644 --- a/appsec/src/extension/ddappsec.h +++ b/appsec/src/extension/ddappsec.h @@ -53,6 +53,7 @@ extern __thread void *unspecnull ATTR_TLS_LOCAL_DYNAMIC TSRMLS_CACHE; # define DDAPPSEC_G(v) (ddappsec_globals.v) #endif +void dd_appsec_rinit_once(void); int dd_appsec_rshutdown(bool ignore_verdict); // Add a NO_CACHE version. diff --git a/appsec/src/extension/helper_process.c b/appsec/src/extension/helper_process.c index 04e914d259..d599b23a96 100644 --- a/appsec/src/extension/helper_process.c +++ b/appsec/src/extension/helper_process.c @@ -5,27 +5,10 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. // NOLINTNEXTLINE(misc-header-include-cycle) +#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #define HELPER_PROCESS_C_INCLUDES #include "configuration.h" #include "ddappsec.h" @@ -34,10 +17,7 @@ #include "logging.h" #include "network.h" #include "php_compat.h" -#include "php_helpers.h" #include "php_objects.h" -#include "string_helpers.h" -#include "version.h" typedef struct _dd_helper_mgr { dd_conn conn; @@ -45,14 +25,12 @@ typedef struct _dd_helper_mgr { struct timespec next_retry; uint16_t failed_count; bool connected_this_req; - bool launched_this_req; + pid_t pid; char *nonnull socket_path; char *nonnull lock_path; } dd_helper_mgr; -static atomic_int _launch_failure_fd_lock; - static THREAD_LOCAL_ON_ZTS dd_helper_mgr _mgr; static const double _backoff_initial = 3.0; @@ -68,31 +46,23 @@ static const int timeout_recv_subseq = 2000; #define DD_SOCK_PATH_FORMAT DD_PATH_FORMAT ".sock" #define DD_LOCK_PATH_FORMAT DD_PATH_FORMAT ".lock" -#ifndef CLOCK_MONOTONIC_COARSE -# define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC -#endif #ifdef TESTING static void _register_testing_objects(void); #endif +static void _read_settings(void); +static bool _wait_for_next_retry(void); +static void _inc_failed_counter(void); +static void _reset_retry_state(void); + void dd_helper_startup(void) { - atomic_store(&_launch_failure_fd_lock, -1); #ifdef TESTING _register_testing_objects(); #endif } -void dd_helper_shutdown(void) -{ - int failure_lock_fd = atomic_load(&_launch_failure_fd_lock); - if (failure_lock_fd != -1) { - // no need for compare and exchange, dd_helper_shutdown - // is called from MSHUTDOWN - atomic_store(&_launch_failure_fd_lock, -1); - close(failure_lock_fd); - } -} +void dd_helper_shutdown(void) {} void dd_helper_gshutdown() { @@ -100,17 +70,8 @@ void dd_helper_gshutdown() pefree(_mgr.lock_path, 1); } -void dd_helper_rshutdown() -{ - _mgr.connected_this_req = false; - _mgr.launched_this_req = false; -} +void dd_helper_rshutdown() { _mgr.connected_this_req = false; } -static bool _wait_for_next_retry(void); -static void _inc_failed_counter(void); -static void _prevent_launch_attempts(int lock_fd); -static bool /* retry */ _maybe_launch_helper(void); -static void _connection_succeeded(void); dd_conn *nullable dd_helper_mgr_acquire_conn( client_init_func nonnull init_func, void *unspecnull ctx) { @@ -121,72 +82,44 @@ dd_conn *nullable dd_helper_mgr_acquire_conn( if (_wait_for_next_retry()) { return NULL; } - zval runtime_path; - ZVAL_STR(&runtime_path, get_DD_APPSEC_HELPER_RUNTIME_PATH()); - dd_on_runtime_path_update(NULL, &runtime_path); - bool retry = false; - for (int attempt = 0;; attempt++) { - int res = - dd_conn_init(conn, _mgr.socket_path, strlen(_mgr.socket_path)); - - if (res) { - // connection failure - if (attempt == 0) { - // on first attempt, try to launch the helper - retry = _maybe_launch_helper(); - if (retry) { - continue; - } - // no retry - mlog(dd_log_warning, - "Connection to helper failed and we are not going to " - "attempt to launch it: %s", - dd_result_to_string(res)); - goto error; - } else { // attempt == 1 - // 2nd connection attempt failed - // after apparently succeeding in launching the helper - mlog(dd_log_warning, - "Connection to helper failed; we tried to launch it " - "and connect again, only to fail again: %s", - dd_result_to_string(res)); - _prevent_launch_attempts(-1); - goto error; - } - } - - // else we have a connection. Set timeouts and test it - dd_conn_set_timeout(conn, comm_type_send, timeout_send); - dd_conn_set_timeout(conn, comm_type_recv, timeout_recv_initial); - - res = init_func(conn, ctx); - if (res) { - mlog_g(dd_log_warning, "Initial exchange with helper failed; " - "abandoning the connection"); - dd_conn_destroy(conn); - if (attempt == 1) { - _prevent_launch_attempts(-1); - } - goto error; - } else { - dd_conn_set_timeout( - &_mgr.conn, comm_type_recv, timeout_recv_subseq); - } - - // else success - break; + _read_settings(); + + int res = dd_conn_init(conn, _mgr.socket_path, strlen(_mgr.socket_path)); + + if (res) { + // connection failure + mlog(dd_log_warning, "Connection to helper failed (socket: %s): %s", + _mgr.socket_path, dd_result_to_string(res)); + goto error; + } + + // else we have a connection. Set timeouts and test it + dd_conn_set_timeout(conn, comm_type_send, timeout_send); + dd_conn_set_timeout(conn, comm_type_recv, timeout_recv_initial); + + res = init_func(conn, ctx); + if (res) { + mlog_g(dd_log_warning, "Initial exchange with helper failed; " + "abandoning the connection"); + dd_conn_destroy(conn); + goto error; } - _connection_succeeded(); + dd_conn_set_timeout(&_mgr.conn, comm_type_recv, timeout_recv_subseq); mlog(dd_log_debug, "returning fresh connection"); + + _mgr.connected_this_req = true; + _reset_retry_state(); + return conn; error: _inc_failed_counter(); return NULL; } + dd_conn *nullable dd_helper_mgr_cur_conn(void) { dd_conn *conn = &_mgr.conn; @@ -226,6 +159,63 @@ bool dd_on_runtime_path_update(zval *nullable old_val, zval *nonnull new_val) return true; } +static inline ddog_CharSlice to_char_slice(zend_string *zs) +{ + return (ddog_CharSlice){.len = ZSTR_LEN(zs), .ptr = ZSTR_VAL(zs)}; +} + +static void _read_settings() +{ + if (_mgr.socket_path) { + return; + } + + dd_appsec_rinit_once(); + + zval runtime_path; + ZVAL_STR(&runtime_path, get_DD_APPSEC_HELPER_RUNTIME_PATH()); + dd_on_runtime_path_update(NULL, &runtime_path); +} + +__attribute__((visibility("default"))) void dd_appsec_maybe_enable_helper( + sidecar_enable_appsec_t nonnull enable_appsec) +{ + _read_settings(); + + ddog_CharSlice helper_path = to_char_slice(get_DD_APPSEC_HELPER_PATH()); + mlog(dd_log_debug, "Helper path is %.*s", (int)helper_path.len, + helper_path.ptr); + ddog_CharSlice socket_path = {_mgr.socket_path, strlen(_mgr.socket_path)}; + ddog_CharSlice lock_path = {_mgr.lock_path, strlen(_mgr.lock_path)}; + ddog_CharSlice log_path = + to_char_slice(get_global_DD_APPSEC_HELPER_LOG_FILE()); + ddog_CharSlice log_level = + to_char_slice(get_global_DD_APPSEC_HELPER_LOG_LEVEL()); + + enable_appsec(helper_path, socket_path, lock_path, log_path, log_level); +} + +void dd_helper_close_conn() +{ + if (!dd_conn_connected(&_mgr.conn)) { + mlog(dd_log_debug, "Not connected; nothing to do"); + return; + } + + int res = dd_conn_destroy(&_mgr.conn); + if (res == -1) { + mlog_err(dd_log_warning, "Error closing connection to helper"); + } + + /* we treat closing the connection on the request it was opened a failure + * for the purposes of the connection backoff */ + if (_mgr.connected_this_req) { + mlog(dd_log_debug, "Connection was closed on the same request as it " + "opened. Incrementing backoff counter"); + _inc_failed_counter(); + } +} + // returns true if an attempt to connectt should not be made yet static bool _wait_for_next_retry() { @@ -234,7 +224,7 @@ static bool _wait_for_next_retry() } struct timespec cur_time; - if (clock_gettime(CLOCK_MONOTONIC_COARSE, &cur_time) == -1) { + if (clock_gettime(CLOCK_MONOTONIC, &cur_time) == -1) { mlog_err(dd_log_warning, "Call to clock_gettime() failed"); return false; } @@ -249,51 +239,6 @@ static bool _wait_for_next_retry() return false; } -static void _connection_succeeded() -{ - _mgr.connected_this_req = true; - _mgr.failed_count = 0; - _mgr.next_retry = (struct timespec){0}; -} - -static int /* fd */ _acquire_lock(void); -static dd_result _launch_helper_daemon(int lock_fd); -static bool _maybe_launch_helper() -{ - if (!get_global_DD_APPSEC_HELPER_LAUNCH()) { - mlog(dd_log_debug, "Will not try to launch daemon due to ini " - "datadog.appsec.launch_helper"); - return false; - } - - int lock_fd = _acquire_lock(); - if (lock_fd == -1) { - mlog(dd_log_info, - "Could not acquire exclusive lock for launching the daemon"); - return false; - } - - dd_result res = _launch_helper_daemon(lock_fd); - if (res) { - // if this fails, we don't want this worker or any other - // also trying to start the helper, so we hold on to the lock - _prevent_launch_attempts(lock_fd); - return false; - } - - // however, if we were successful, we let go of the lock and let - // the helper keep its copy of the file descriptor. If the helper dies, - // the lock will be released and we can try launching it again - UNUSED(close(lock_fd)); - - _mgr.launched_this_req = true; - - mlog(dd_log_info, - "Apparently successful launch of the helper; will try reconnecting"); - - return true; -} - static void _inc_failed_counter() { if (_mgr.failed_count != UINT16_MAX) { @@ -302,7 +247,7 @@ static void _inc_failed_counter() mlog(dd_log_debug, "Failed counter is now at %u", _mgr.failed_count); struct timespec cur_time; - int res = clock_gettime(CLOCK_MONOTONIC_COARSE, &cur_time); + int res = clock_gettime(CLOCK_MONOTONIC, &cur_time); if (res == -1) { mlog_err(dd_log_warning, "Call to clock_gettime() failed"); _mgr.next_retry = (struct timespec){0}; @@ -317,566 +262,10 @@ static void _inc_failed_counter() _mgr.next_retry.tv_sec += (time_t)wait; } -static void _prevent_launch_attempts(int lock_fd /* -1 to acquire it */) +static void _reset_retry_state() { - if (lock_fd == -1) { - lock_fd = _acquire_lock(); - if (lock_fd == -1) { - mlog(dd_log_info, - "Could not acquire exclusive lock to prevent helper " - "launch attempts"); - return; - } - } - - mlog(dd_log_warning, "holding the exclusive lock indefinitely to " - "prevent further attempts to start the helper"); - bool success = atomic_compare_exchange_strong( - &_launch_failure_fd_lock, &(int){-1}, lock_fd); - if (!success) { - mlog(dd_log_error, "failure to set _launch_failure_fd_lock. Bug"); - UNUSED(close(lock_fd)); - } -} - -static int /* fd */ _acquire_lock() -{ - // open file descriptor - const char *lock_file = _mgr.lock_path; - int fd = open(lock_file, O_CREAT | O_NOFOLLOW | O_RDONLY, 0600); // NOLINT - if (fd == -1) { - mlog_err(dd_log_warning, "Could not open lock file %s", lock_file); - return -1; - } - - // acquire lock - int res = flock(fd, LOCK_EX | LOCK_NB); - if (res == -1) { - if (errno == EWOULDBLOCK) { - mlog_g(dd_log_info, - "The helper lock on %s is already being held; " - "could not get exclusive lock", - lock_file); - } else { - mlog_err(dd_log_warning, "Failed getting a hold of a lock on %s", - lock_file); - } - res = close(fd); - if (res == -1) { - mlog_err(dd_log_warning, "Call to close() failed"); - } - return -1; - } - - mlog_g( - dd_log_debug, "Got exclusive lock on file %s, fd is %d", lock_file, fd); - - return fd; -} - -static int /* fd */ _open_socket_for_helper(void); -static char **nullable _split_params( - const char *exe, const char *orig_params_str); -static dd_result _wait_for_intermediate_process(pid_t pid); -static void _close_file_descriptors(int log_fd, int lock_fd, int sock_fd); -static bool _reset_signals_state(int log_fd); -static ATTR_NO_RETURN void _continue_in_intermediate_process( - int log_fd, int lock_fd, int sock_fd, const char *executable, char **argv); -static dd_result _launch_helper_daemon(int lock_fd) -{ - int log_mode = O_WRONLY | O_CREAT; -#ifdef TESTING - if (get_global_DD_APPSEC_TESTING()) { - log_mode |= O_TRUNC; - } else { - log_mode |= O_APPEND; - } -#else - log_mode |= O_APPEND; -#endif - char *helperlog = ZSTR_VAL(get_global_DD_APPSEC_HELPER_LOG_FILE()); - int log_fd = open(helperlog, log_mode, 0600); // NOLINT - if (log_fd == -1) { - mlog_err( - dd_log_warning, "Could not open log file for helper %s", helperlog); - return dd_error; - } - mlog_g(dd_log_debug, "Opened helper log at %s", helperlog); - - int sock_fd = _open_socket_for_helper(); - if (sock_fd == -1) { - close(log_fd); - return dd_error; - } - - char *binary = ZSTR_VAL(get_DD_APPSEC_HELPER_PATH()); - mlog_g(dd_log_debug, "The executable to launch is %s", binary); - - char **argv; - { - char *args; - char *extra_args = ZSTR_VAL(get_DD_APPSEC_HELPER_EXTRA_ARGS()); - spprintf(&args, 0, "%s%s--lock_path - --socket_path fd:%d", extra_args, - *extra_args ? " " : "", sock_fd); - argv = _split_params(binary, args); - efree(args); - } - if (!argv) { - mlog(dd_log_error, "Could not build argument array to launch helper"); - close(log_fd); - close(sock_fd); - return dd_error; - } - if (dd_log_level() >= dd_log_debug) { - for (char **arg = argv + 1; *arg; arg++) { - mlog(dd_log_debug, " argument: %s", *arg); - } - } - - /* fork */ - pid_t pid = fork(); - if (pid != 0) { // parent; the extension - UNUSED(close(log_fd)); - UNUSED(close(sock_fd)); - - if (argv[1]) { - efree(argv[1]); - } - efree(argv); - - if (pid == -1) { - mlog_err(dd_log_warning, "Failed to fork()"); - return dd_error; - } - - mlog_g( - dd_log_info, "Forked. Pid of intermediate process is %d", (int)pid); - - return _wait_for_intermediate_process(pid); - } - - /* fallback to the intermediary process */ - _continue_in_intermediate_process(log_fd, lock_fd, sock_fd, argv[0], argv); - return dd_error; // unreachable -} - -static int /* fd */ _open_socket_for_helper() -{ - struct sockaddr_un sockaddr = {0}; - sockaddr.sun_family = AF_UNIX; - if (strlen(_mgr.socket_path) >= sizeof(sockaddr.sun_path) - 1) { - mlog(dd_log_error, - "The value of datadog.appsec.socket_path (%s) is " - "longer than the max size (%zu)", - _mgr.socket_path, sizeof(sockaddr.sun_path) - 1); - return -1; - } - // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) - strcpy(sockaddr.sun_path, _mgr.socket_path); - - int res = unlink(_mgr.socket_path); - if (res == -1 && errno != ENOENT) { - mlog_err(dd_log_warning, "Failed to unlink %s", _mgr.socket_path); - return -1; - } - - int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock_fd == -1) { - mlog_err(dd_log_warning, "Call to socket() failed"); - return -1; - } - - res = bind(sock_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - if (res == -1) { - mlog_err(dd_log_warning, "Call to bind() failed"); - UNUSED(close(sock_fd)); - return -1; - } - -#define BACKLOG 20 - res = listen(sock_fd, BACKLOG); - if (res == -1) { - mlog_err(dd_log_warning, "Call to listen() failed"); - UNUSED(close(sock_fd)); - return -1; - } - - mlog(dd_log_info, "Prepared socket for helper. fd %d", sock_fd); - - return sock_fd; -} - -/* caller should free returned array and also ret[1] if return is not null */ -// NOLINTNEXTLINE(readability-function-cognitive-complexity) -static char **nullable _split_params( - const char *executable, const char *orig_params_str) // NOLINT -{ - unsigned count = 2; // at least one for executable and one for final null - char **ret = safe_emalloc(count, sizeof *ret, 0); - - ret[0] = (char *)(uintptr_t)executable; - ret[1] = NULL; - - const char *p; // read pointer - for (p = orig_params_str; *p == ' '; p++) {} - if (*p == '\0') { - // no arguments - return ret; - } - - // we never write more than the original size of the params - char *params_buffer = emalloc(strlen(orig_params_str) + 1); // NOLINT - char *wp = params_buffer; // write pointer - char *param_start; // position of write pointer where we started writing the - // current parameter - enum { - between, - double_quoted, - single_quoted, - bare_param, - } state = between; - - bool escaped = false; - param_start = wp; - for (; *p != '\0'; p++) { - switch (state) { // NOLINT - case between: - if (*p == ' ') { - // nothing to do - } else if (*p == '"') { - state = double_quoted; - param_start = wp; - } else if (*p == '\'') { - state = single_quoted; - param_start = wp; - } else if (*p == '\\') { - state = bare_param; - escaped = true; - param_start = wp; - } else { - state = bare_param; - param_start = wp; - *wp++ = *p; - } // next is \0: nothing to do - break; - case double_quoted: - case single_quoted: - if (escaped) { - *wp++ = *p; - escaped = false; - } else if (*p == (state == double_quoted ? '"' : '\'')) { - state = bare_param; - } else if (*p == '\\') { - escaped = true; - } else { - *wp++ = *p; - } // next is \0: we will trigger failure - break; - case bare_param: { - if (escaped) { - *wp++ = *p; - escaped = false; - } else if (*p == '\\') { - escaped = true; - } else if (*p == '"') { - state = double_quoted; - } else if (*p == '\'') { - state = single_quoted; - } else if (*p == ' ') { - *wp++ = '\0'; - count++; - ret = safe_erealloc(ret, count, sizeof *ret, 0); - ret[count - 2] = param_start; - ret[count - 1] = NULL; - state = between; - } else { - *wp++ = *p; - } - break; - } - } // end switch - } // end loop - - if (escaped) { - mlog(dd_log_warning, - "datadog.appsec.helper_extra_args has an unpaired \\ at the end: " - "%s", - orig_params_str); - efree(ret); - efree(params_buffer); - return NULL; - } - if (state == single_quoted || state == double_quoted) { - mlog(dd_log_warning, - "datadog.appsec.helper_extra_args has unmatched quotes: %s", - orig_params_str); - efree(ret); - efree(params_buffer); - return NULL; - } - - if (state != between) { - *wp = '\0'; - count++; - ret = safe_erealloc(ret, count, sizeof *ret, 0); - ret[count - 2] = param_start; - ret[count - 1] = NULL; - } - - return ret; -} - -static dd_result _wait_for_intermediate_process(pid_t pid) -{ - int stat_loc; - if (waitpid(pid, &stat_loc, 0) == -1) { - mlog_err(dd_log_info, "Call to waitpid() failed"); - return dd_error; - } - if (WIFEXITED(stat_loc) && WEXITSTATUS(stat_loc) == 0) { - mlog_g(dd_log_debug, "Intermediate process terminated normally"); - } else { - if (WIFEXITED(stat_loc)) { - mlog(dd_log_warning, - "Intermediate process %d exited with exit code %d", (int)pid, - WEXITSTATUS(stat_loc)); - } else if (WIFSIGNALED(stat_loc)) { - mlog(dd_log_warning, - "Intermediate process %d was signaled. Signal %d (dump: " - "%s)", - (int)pid, WTERMSIG(stat_loc), - WCOREDUMP(stat_loc) ? "yes" : "no"); - } else if (WIFSTOPPED(stat_loc)) { - mlog(dd_log_warning, - "Intermediate process %d was stopped. Signal %d", (int)pid, - WTERMSIG(stat_loc)); - } else { - mlog_g(dd_log_warning, - "Intermediate process %d did not end normally; " - "value of stat_loc is %d", - (int)pid, stat_loc); - } - return dd_error; - } - - return dd_success; -} - -#define PREEXEC_LOG(msgf) _preexec_log(log_fd, msgf, sizeof(msgf) - 1) -static void _preexec_log(int log_fd, const char *msg, size_t msg_len) -{ - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - - struct tm tinfo; - localtime_r(&ts.tv_sec, &tinfo); - char buffer[sizeof("[2010-01-01 00:00:00.000")]; - size_t ret = strftime(buffer, sizeof(buffer), "[%F %T", &tinfo); - if (!ret) { - buffer[0] = '\0'; - } - - size_t buffer_len = strlen(buffer); -#define TEN_E6 1000000 - snprintf(&buffer[buffer_len], sizeof(buffer) - buffer_len, ".%03ld", - (long)ts.tv_nsec / TEN_E6); - UNUSED(write(log_fd, buffer, strlen(buffer))); - UNUSED(write(log_fd, ZEND_STRL("] pre-exec: "))); - UNUSED(write(log_fd, msg, msg_len)); - UNUSED(write(log_fd, "\n", 1)); -} - -#define EXIT_SIGNALS_STATE 9 -#define EXIT_GETFD 2 -#define EXIT_FD_OPEN 3 -#define EXIT_UNEXPECTED_FD 4 -#define EXIT_SIGACTION 5 -#define EXIT_SIGHUP_IGNORE 6 -#define EXIT_SETSID 7 -#define EXIT_SECOND_FORK 8 -#define EXIT_CODE_MASK 0x7F - -static ATTR_NO_RETURN void _continue_in_intermediate_process( - int log_fd, int lock_fd, int sock_fd, const char *executable, char **argv) -{ - /* we can't log with mlog anymore! */ - - /* close all file descriptors except lock_fd and sock_fd*/ - _close_file_descriptors(log_fd, lock_fd, sock_fd); - - /* set default handlers and empty signal mask */ - if (!_reset_signals_state(log_fd)) { - exit(EXIT_SIGNALS_STATE); // NOLINT(concurrency-mt-unsafe) - } - - /* check that the remaining file descriptors are valid */ - int fildes[] = {log_fd, lock_fd, sock_fd}; - for (size_t i = 0; i < ARRAY_SIZE(fildes); i++) { - if (fcntl(fildes[i], F_GETFD) == -1) { - PREEXEC_LOG("call to fcntl F_GETFD failed"); - exit(EXIT_GETFD); // NOLINT(concurrency-mt-unsafe) - } - } - - /* open stdin, stdout /dev/null and stderr as dup of log_fd (typically - * /dev/null too, generally the value of datadog.appsec.helper_log_file */ - int fd0 = open("/dev/null", O_RDWR); // NOLINT - int fd1 = dup(0); // NOLINT - int fd2 = dup2(log_fd, 2); - close(log_fd); - log_fd = fd2; // for PREEXEC_LOG macro - - if (fd0 == -1 || fd1 == -1 || fd2 == -1) { - PREEXEC_LOG("failed opening one of the standard file descriptors"); - exit(EXIT_FD_OPEN); // NOLINT(concurrency-mt-unsafe) - } - - if (fd0 != 0 || fd1 != 1 || fd2 != 2) { - PREEXEC_LOG("one of the opened files did not have the expect file " - "descriptor number"); - exit(EXIT_UNEXPECTED_FD); // NOLINT(concurrency-mt-unsafe) - } - - if (chdir("/") == -1) { - PREEXEC_LOG("could not chdir /"); - // not fatal - } - - umask(0); // can't fail - - struct sigaction sa = {.sa_handler = SIG_IGN, .sa_flags = 0}; - if (sigemptyset(&sa.sa_mask) == -1) { - exit(EXIT_SIGACTION); // NOLINT(concurrency-mt-unsafe) - } - - if (sigaction(SIGHUP, &sa, NULL) == -1) { - PREEXEC_LOG("could not make SIGHUP ignored"); - exit(EXIT_SIGHUP_IGNORE); // NOLINT(concurrency-mt-unsafe) - } - - /* fork again */ - PREEXEC_LOG("Going for second fork"); - pid_t pid = fork(); - if (pid == -1) { - PREEXEC_LOG("Second fork failed"); - exit(EXIT_SECOND_FORK); // NOLINT(concurrency-mt-unsafe) - } - if (pid != 0) { // parent - PREEXEC_LOG("Intermediate process exiting"); - // skip atexit() hooks - _Exit(0); // NOLINT(concurrency-mt-unsafe) - } - - if (setsid() == -1) { - PREEXEC_LOG("Call to setsid() failed"); - exit(EXIT_SETSID); // NOLINT(concurrency-mt-unsafe) - } - - PREEXEC_LOG("About to call execv"); - if (execv(executable, argv) == -1) { - int exit_code = (errno & EXIT_CODE_MASK) ? (errno & EXIT_CODE_MASK) : 1; - PREEXEC_LOG("execv call failed"); - exit(exit_code); // NOLINT(concurrency-mt-unsafe) - } - - __builtin_unreachable(); -} - -#define DEFAULT_BASE 10 -#define MAX_RLIMIT 1024 - -static void _close_file_descriptors(int log_fd, int lock_fd, int sock_fd) -{ - DIR *self = opendir("/proc/self/fd/"); - if (self != NULL) { - struct dirent *entry = NULL; - // NOLINTNEXTLINE(concurrency-mt-unsafe) - while ((entry = readdir(self)) != NULL) { - if (*entry->d_name == '.') { - continue; - } - - char *endptr = NULL; - int fd = (int)strtol(entry->d_name, &endptr, DEFAULT_BASE); - if (endptr != entry->d_name && *endptr == '\0' && fd != log_fd && - fd != lock_fd && fd != sock_fd) { - close(fd); - } - } - - closedir(self); - } else { - int fd_max; - struct rlimit rl; - - // Determine max number of file descriptors, default and limit to 1024 - if (getrlimit(RLIMIT_NOFILE, &rl) == -1 || rl.rlim_max > MAX_RLIMIT) { - fd_max = MAX_RLIMIT; - } else { - fd_max = (int)rl.rlim_max; - } - - for (int i = 0; i < fd_max; i++) { - if (i != log_fd && i != lock_fd && i != sock_fd) { - close(i); - } - } - } -} - -static bool _reset_signals_state(int log_fd) -{ -#ifndef SIGRTMIN -# define SIGRTMIN 32 -#endif - // ignored signals are kept ignored across exec() - // NOLINTNEXTLINE(cert-err33-c) - for (int i = 1; i < SIGRTMIN; i++) { signal(i, SIG_DFL); } - - // the signal mask is also kept across exec() - sigset_t empty_mask; - sigemptyset(&empty_mask); - - // NOLINTNEXTLINE(concurrency-mt-unsafe) - if (sigprocmask(SIG_SETMASK, &empty_mask, NULL) == -1) { - PREEXEC_LOG("Call to sigprocmask failed"); - return false; - } - - return true; -} - -void dd_helper_close_conn() -{ - if (!dd_conn_connected(&_mgr.conn)) { - mlog(dd_log_debug, "Not connected; nothing to do"); - return; - } - - int res = dd_conn_destroy(&_mgr.conn); - if (res == -1) { - mlog_err(dd_log_warning, "Error closing connection to helper"); - } - - /* we treat closing the connection on the request it was opened a failure - * for the purposes of the connection backoff */ - if (_mgr.connected_this_req) { - mlog(dd_log_debug, "Connection was closed on the same request as it " - "opened. Incrementing backoff counter"); - _inc_failed_counter(); - } - - // also, if we launched, do not try again and prevent others from trying too - if (_mgr.launched_this_req) { - int lock_fd = _acquire_lock(); - if (lock_fd == -1) { - mlog(dd_log_info, "Could not acquire exclusive lock to prevent " - "further helper launching"); - return; - } - - _prevent_launch_attempts(lock_fd); - } + _mgr.failed_count = 0; + _mgr.next_retry = (struct timespec){0}; } #ifdef TESTING @@ -893,39 +282,7 @@ static PHP_FUNCTION(datadog_appsec_testing_set_helper_path) ->name, zstr, ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME); } -static PHP_FUNCTION(datadog_appsec_testing_set_helper_extra_args) -{ - zend_string *zstr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &zstr) == FAILURE) { - RETURN_FALSE; - } - zend_alter_ini_entry( - zai_config_memoized_entries[DDAPPSEC_CONFIG_DD_APPSEC_HELPER_EXTRA_ARGS] - .ini_entries[0] - ->name, - zstr, ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME); -} -static PHP_FUNCTION(datadog_appsec_testing_get_helper_argv) -{ - array_init(return_value); - if (zend_parse_parameters_none() == FAILURE) { - return; - } - - char **argv = _split_params(ZSTR_VAL(get_DD_APPSEC_HELPER_PATH()), - ZSTR_VAL(get_DD_APPSEC_HELPER_EXTRA_ARGS())); - if (!argv) { - return; - } - - for (char **s = argv; *s; s++) { add_next_index_string(return_value, *s); } - - if (argv[1]) { - efree(argv[1]); - } - efree(argv); -} static PHP_FUNCTION(datadog_appsec_testing_is_connected_to_helper) { if (zend_parse_parameters_none() == FAILURE) { @@ -968,8 +325,6 @@ ZEND_END_ARG_INFO() static const zend_function_entry functions[] = { ZEND_RAW_FENTRY(DD_TESTING_NS "set_helper_path", PHP_FN(datadog_appsec_testing_set_helper_path), set_string_arginfo, 0) - ZEND_RAW_FENTRY(DD_TESTING_NS "set_helper_extra_args", PHP_FN(datadog_appsec_testing_set_helper_extra_args), set_string_arginfo, 0) - ZEND_RAW_FENTRY(DD_TESTING_NS "get_helper_argv", PHP_FN(datadog_appsec_testing_get_helper_argv), void_ret_array_arginfo, 0) ZEND_RAW_FENTRY(DD_TESTING_NS "is_connected_to_helper", PHP_FN(datadog_appsec_testing_is_connected_to_helper), void_ret_bool_arginfo, 0) ZEND_RAW_FENTRY(DD_TESTING_NS "backoff_status", PHP_FN(datadog_appsec_testing_backoff_status), void_ret_array_arginfo, 0) PHP_FE_END diff --git a/appsec/src/extension/helper_process.h b/appsec/src/extension/helper_process.h index 481a86f12d..750bf70f44 100644 --- a/appsec/src/extension/helper_process.h +++ b/appsec/src/extension/helper_process.h @@ -6,10 +6,17 @@ #ifndef DD_HELPER_MGR_H #define DD_HELPER_MGR_H +#include + #include "attributes.h" #include "dddefs.h" #include "network.h" +typedef typeof(&ddog_sidecar_enable_appsec) sidecar_enable_appsec_t; + +__attribute__((visibility("default"))) +void dd_appsec_maybe_enable_helper(sidecar_enable_appsec_t nonnull enable_appsec); + void dd_helper_startup(void); void dd_helper_shutdown(void); void dd_helper_gshutdown(void); diff --git a/appsec/src/extension/network.c b/appsec/src/extension/network.c index 0803a0d53b..227ad58f5b 100644 --- a/appsec/src/extension/network.c +++ b/appsec/src/extension/network.c @@ -19,6 +19,7 @@ #include #include #include +#include #define HELPER_PROCESS_C_INCLUDES #include "ddappsec.h" @@ -34,9 +35,13 @@ struct PACKED _dd_header { // NOLINT typedef struct PACKED _dd_header dd_header; -static const int CONNECT_TIMEOUT = 2500; // ms +static const int CONNECT_TIMEOUT = 2500; // ms +static const int CONNECT_RETRY_PAUSE = 100; // ms static const uint32_t MAX_RECV_MESSAGE_SIZE = 4 * 1024 * 1024; +static void _timespec_add_ms(struct timespec *ts, long num_ms); +static long _timespec_delta_ms(struct timespec *ts1, struct timespec *ts2); + int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) dd_conn *nonnull conn, const char *nonnull path, size_t path_len) { @@ -45,6 +50,7 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) return dd_error; } + // NOLINTNEXTLINE(android-cloexec-socket) int res = conn->socket = socket(AF_UNIX, SOCK_STREAM, 0); if (res == -1) { @@ -71,13 +77,27 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) } mlog(dd_log_info, "Attempting to connect to UNIX socket %s", path); + struct timespec deadline; + clock_gettime(CLOCK_MONOTONIC, &deadline); + _timespec_add_ms(&deadline, CONNECT_TIMEOUT); + +try_again: res = connect( conn->socket, (struct sockaddr *)&conn->addr, sizeof(conn->addr)); if (res == -1) { if (errno == EINPROGRESS) { struct pollfd pfds[] = { {.fd = conn->socket, .events = POLLIN | POLLOUT}}; - res = poll(pfds, 1, CONNECT_TIMEOUT); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + long time_left = _timespec_delta_ms(&deadline, &now); + if (time_left <= 0) { + dd_conn_destroy(conn); + mlog(dd_log_info, "Connection to helper timed out"); + return dd_error; + } + + res = poll(pfds, 1, (int)time_left); if (res == 0) { dd_conn_destroy(conn); mlog(dd_log_info, "Connection to helper timed out"); @@ -103,6 +123,28 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) } // else good } else { + if (errno == ENOENT) { + // the socket does not exist. Retry + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + long time_left = _timespec_delta_ms(&deadline, &now); + if (time_left <= 0) { + dd_conn_destroy(conn); + mlog(dd_log_info, "Connection to helper timed out"); + return dd_error; + } + + mlog(dd_log_debug, + "Socket does not exist. Waiting 100 ms for next retry"); + int ret = usleep(CONNECT_RETRY_PAUSE * 1000); // NOLINT + if (ret == 0) { + goto try_again; + } else { + mlog_err(dd_log_warning, + "Failed connecting to helper (usleep())"); + } + } + dd_conn_destroy(conn); mlog_err( dd_log_info, "Failed connecting to helper (connect() call)"); @@ -308,7 +350,7 @@ dd_result dd_conn_recv_cred(dd_conn *nonnull conn, char *nullable *nonnull data, setsockopt(conn->socket, SOL_SOCKET, SO_PASSCRED, &(int){0}, sizeof(int)); - if (recv_bytes <= 0) { + if (recv_bytes == 0) { mlog(dd_log_info, "No data received"); return dd_network; } @@ -423,3 +465,29 @@ dd_result dd_conn_set_timeout( return dd_success; } + +#define ONE_E3 1000 +#define ONE_E6 1000000 +#define ONE_E9 1000000000 +static void _timespec_add_ms(struct timespec *ts, long num_ms) +{ + long seconds = num_ms / ONE_E3; + long nanoseconds = (num_ms % ONE_E3) * ONE_E6; + + ts->tv_sec += seconds; + ts->tv_nsec += nanoseconds; + + if (ts->tv_nsec >= ONE_E9) { + ts->tv_sec += ts->tv_nsec / ONE_E9; + ts->tv_nsec %= ONE_E9; + } +} + +static long _timespec_delta_ms(struct timespec *ts1, struct timespec *ts2) +{ + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + long res = (ts1->tv_sec - ts2->tv_sec) * 1000; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + res += (ts1->tv_nsec - ts2->tv_nsec) / 1000000; + return res; +} diff --git a/appsec/src/helper/client.cpp b/appsec/src/helper/client.cpp index bb6497c221..078dffd224 100644 --- a/appsec/src/helper/client.cpp +++ b/appsec/src/helper/client.cpp @@ -88,6 +88,7 @@ bool handle_message(client &client, const network::base_broker &broker, auto msg = broker.recv(initial_timeout); return maybe_exec_cmd_M(client, msg); } catch (const unexpected_command &e) { + SPDLOG_DEBUG("Unexpected command: {}", e.what()); send_error = true; if (!ignore_unexpected_messages) { result = false; diff --git a/appsec/src/helper/config.cpp b/appsec/src/helper/config.cpp index 7150e016bd..921b15037a 100644 --- a/appsec/src/helper/config.cpp +++ b/appsec/src/helper/config.cpp @@ -8,63 +8,25 @@ namespace dds::config { // NOLINTNEXTLINE -config::config(int argc, char *argv[]) +config::config( + const std::function(std::string_view)> &fn) { - for (int i = 1; i < argc; ++i) { - std::string_view arg(argv[i]); - if (arg.size() < 2 || arg.substr(0, 2) != "--") { - // Not an option, weird - continue; - } - arg.remove_prefix(2); - - // Check if the option has an assignment - auto pos = arg.find('='); - if (pos != std::string::npos) { - kv_[arg.substr(0, pos)] = arg.substr(pos + 1); - continue; - } - - // Check the next argument - if ((i + 1) < argc) { - const std::string_view value(argv[i + 1]); - if (arg.size() < 2 || arg.substr(0, 2) != "--") { - // Not an option, so we assume it's a value - kv_[arg] = value; - // Skip on next iteration - ++i; - continue; - } - } - - // If the next argument is an option or this is the last argument, we - // assume it's just a modifier. - kv_[arg] = std::string_view(); - } -} - -template <> bool config::get(std::string_view key) const -{ - return kv_.find(key) != kv_.end(); -} - -template <> std::string config::get(std::string_view key) const -{ - return std::string(kv_.at(key)); -} - -template <> -std::string_view config::get(std::string_view key) const -{ - return kv_.at(key); + auto get_env = [&fn](std::string_view key) -> std::string_view { + return fn(key).value_or(defaults.at(key)); + }; + kv_[env_socket_file_path] = get_env(env_socket_file_path); + kv_[env_lock_file_path] = get_env(env_lock_file_path); + kv_[env_log_file_path] = get_env(env_log_file_path); + kv_[env_log_level] = get_env(env_log_level); } // NOLINTNEXTLINE const std::unordered_map config::defaults = { - {"lock_path", "/tmp/ddappsec.lock"}, - {"socket_path", "/tmp/ddappsec.sock"}, {"log_level", "warn"}, - {"runner_idle_timeout", "1440"} // minutes + {env_lock_file_path, "/tmp/ddappsec.lock"}, + {env_socket_file_path, "/tmp/ddappsec.sock"}, + {env_log_file_path, "/tmp/ddappsec_helper.log"}, + {env_log_level, "warn"}, }; } // namespace dds::config diff --git a/appsec/src/helper/config.hpp b/appsec/src/helper/config.hpp index a76dff5269..aa0e35edfe 100644 --- a/appsec/src/helper/config.hpp +++ b/appsec/src/helper/config.hpp @@ -6,6 +6,10 @@ #pragma once #include +#include +#include +#include +#include #include namespace dds::config { @@ -13,25 +17,43 @@ namespace dds::config { // Perhaps make this a "singleton" class config { public: - config() = default; - config(int argc, char *argv[]); // NOLINT + explicit config( + const std::function(std::string_view)> + &fn); - template T get(std::string_view key) const + [[nodiscard]] std::string_view socket_file_path() const { - return boost::lexical_cast(kv_.at(key)); + return kv_.at(env_socket_file_path); + } + + [[nodiscard]] std::string_view lock_file_path() const + { + return kv_.at(env_lock_file_path); + } + + [[nodiscard]] std::string_view log_file_path() const + { + return kv_.at(env_log_file_path); + } + + [[nodiscard]] spdlog::level::level_enum log_level() const + { + return spdlog::level::from_str(std::string{kv_.at(env_log_level)}); } protected: static const std::unordered_map defaults; std::unordered_map kv_{defaults}; -}; -template <> bool config::get(std::string_view key) const; - -template <> std::string config::get(std::string_view key) const; - -template <> -std::string_view config::get(std::string_view key) const; + static constexpr std::string_view env_socket_file_path = + "_DD_SIDECAR_APPSEC_SOCKET_FILE_PATH"; + static constexpr std::string_view env_lock_file_path = + "_DD_SIDECAR_APPSEC_LOCK_FILE_PATH"; + static constexpr std::string_view env_log_file_path = + "_DD_SIDECAR_APPSEC_LOG_FILE_PATH"; + static constexpr std::string_view env_log_level = + "_DD_SIDECAR_APPSEC_LOG_LEVEL"; +}; } // namespace dds::config diff --git a/appsec/src/helper/engine_settings.cpp b/appsec/src/helper/engine_settings.cpp index 26b4e9f6dd..8f91eaee6a 100644 --- a/appsec/src/helper/engine_settings.cpp +++ b/appsec/src/helper/engine_settings.cpp @@ -5,6 +5,7 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "engine_settings.hpp" +#include #ifdef __has_include # if __has_include() @@ -21,6 +22,33 @@ namespace std { namespace filesystem = experimental::filesystem; } // namespace std #endif + +#include + +namespace { +std::filesystem::path get_helper_path() +{ + static constexpr std::string_view lib_name{"/libddappsec-helper.so"}; + + const std::filesystem::path maps_path{"/proc/self/maps"}; + std::ifstream maps_stream{maps_path}; + std::string line; + while (std::getline(maps_stream, line)) { + if (line.find(lib_name) == std::string::npos) { + continue; + } + + auto pos = line.find_first_of('/'); + assert(pos != std::string::npos); // NOLINT + maps_stream.close(); + return std::filesystem::path{line.substr(pos)}; + } + + maps_stream.close(); + throw std::runtime_error{"libappsec-helper.so not found in maps"}; +} +} // namespace + namespace dds { const std::string &engine_settings::default_rules_file() @@ -28,25 +56,40 @@ const std::string &engine_settings::default_rules_file() struct def_rules_file { def_rules_file() { - std::error_code ec; - auto self = std::filesystem::read_symlink({"/proc/self/exe"}, ec); - if (ec) { - // should not happen on Linux - file = ""; - } else { - auto self_dir = self.parent_path(); - file = self_dir / "../etc/recommended.json"; - if (!std::filesystem::exists(file)) { - // This fallback file is set by a custom/old installer on - // appsec repository - file = self_dir / "../etc/dd-appsec/recommended.json"; + std::filesystem::path base_path; + + try { + base_path = get_helper_path().parent_path(); + } catch (const std::exception &e) { + file = ""; + + // try relative to the executable instead + try { + auto psp = std::filesystem::path{"/proc/self/exe"}; + if (std::filesystem::is_symlink(psp)) { + psp = std::filesystem::read_symlink(psp); + } + base_path = psp.parent_path(); + } catch (const std::exception &e) { + file = + ""; + return; } } + + file = base_path / "../etc/recommended.json"; + if (!std::filesystem::exists(file)) { + // This fallback file is set by a custom/old installer on + // appsec repository + file = base_path / "../etc/dd-appsec/recommended.json"; + } } std::string file; }; - static def_rules_file const drf; + static def_rules_file const drf; // NOLINT return drf.file; } } // namespace dds diff --git a/appsec/src/helper/main.cpp b/appsec/src/helper/main.cpp index 425f240f4d..7f8d9aad77 100644 --- a/appsec/src/helper/main.cpp +++ b/appsec/src/helper/main.cpp @@ -3,33 +3,37 @@ // // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + #include "config.hpp" #include "runner.hpp" #include "subscriber/waf.hpp" +#include +#include +#include +#include +#include + +extern "C" { #include #include -#include -#include +#include #include #include #include +} -std::atomic exit_signal = false; -std::atomic global_runner = nullptr; - -void signal_handler(int signum) -{ - SPDLOG_INFO("Got signal {}", signum); +namespace { +constexpr std::chrono::seconds log_flush_interval{5}; - dds::runner *runner = global_runner.load(); - runner->exit(); - exit_signal = true; -} +std::atomic interrupted; // NOLINT +std::atomic finished; // NOLINT +pthread_t thread_id; -bool ensure_unique(const dds::config::config &config) +bool ensure_unique(const std::string &lock_path) { - auto lock_path = config.get("lock_path"); - // do not acquire the lock / assume we inherited it if (lock_path == "-") { return true; @@ -37,47 +41,119 @@ bool ensure_unique(const dds::config::config &config) /** The first helper process will create and exclusively lock the file */ // NOLINTNEXTLINE - int fd = ::open(lock_path.data(), O_WRONLY | O_CREAT, 0744); + const int fd = ::open(lock_path.c_str(), O_WRONLY | O_CREAT, 0744); if (fd == -1) { + SPDLOG_INFO("Failed to open lock file: {}", lock_path); return false; } + SPDLOG_DEBUG("Opened lock file {}: fd {}", lock_path, fd); int const res = ::flock(fd, LOCK_EX | LOCK_NB); // If we fail to obtain the lock, for whichever reason, assume we can't // run for now. - return res != -1; + if (res == -1) { + SPDLOG_INFO("Failed to get exclusive lock on file {}: errno {}", + lock_path, errno); + return false; + } + return true; } -int main(int argc, char *argv[]) +int appsec_helper_main_impl() { - std::signal(SIGTERM, signal_handler); // NOLINT(cert-err33-c) - std::signal(SIGPIPE, SIG_IGN); // NOLINT - dds::config::config const config(argc, argv); + dds::config::config const config{ + [](std::string_view key) -> std::optional { + // NOLINTNEXTLINE + char const *value = std::getenv(key.data()); + if (value == nullptr) { + return std::nullopt; + } + return std::string_view{value}; + }}; + + std::shared_ptr logger; + bool stderr_fallback = false; + try { + logger = spdlog::basic_logger_mt( + "ddappsec", std::string{config.log_file_path()}, false); + } catch (std::exception &e) { + // NOLINTNEXTLINE + logger = spdlog::stderr_logger_mt("ddappsec"); + stderr_fallback = true; + } - auto logger = spdlog::stderr_color_mt("ddappsec"); spdlog::set_default_logger(logger); logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l][%t] %v"); - - auto level = spdlog::level::from_str(config.get("log_level")); + auto level = config.log_level(); spdlog::set_level(level); + spdlog::flush_on(level); + + if (stderr_fallback) { + logger->warn("Failed to open log file {}, falling back to stderr", + config.log_file_path()); + } + dds::waf::initialise_logging(level); - if (!ensure_unique(config)) { + if (!ensure_unique(std::string{config.lock_file_path()})) { logger->warn("helper launched, but not unique, exiting"); // There's another helper running - return 0; + return 1; } + auto runner = std::make_unique(config, interrupted); + SPDLOG_INFO("starting runner on new thread"); + std::thread thr{[runner = std::move(runner)]() { +#ifdef __linux__ + pthread_setname_np(pthread_self(), "appsec_helper runner"); +#elif defined(__APPLE__) + pthread_setname_np("appsec_helper runner"); +#endif + runner->run(); + + finished.store(true, std::memory_order_release); + }}; + thread_id = thr.native_handle(); + thr.detach(); + + return 0; +} +} // namespace + +extern "C" __attribute__((visibility("default"))) int +appsec_helper_main() noexcept +{ try { - SPDLOG_INFO("starting runner"); - dds::runner runner(config); - global_runner = &runner; - if (!exit_signal) { - runner.run(); - } - } catch (const std::exception &e) { - SPDLOG_ERROR("exception: {}", e.what()); + return appsec_helper_main_impl(); + } catch (std::exception &e) { + SPDLOG_ERROR("Unhandled exception: {}", e.what()); + return 2; + } catch (...) { + SPDLOG_ERROR("Unhandled exception"); + return 2; } + return 0; +} + +extern "C" __attribute__((visibility("default"))) int +appsec_helper_shutdown() noexcept +{ + interrupted.store(true, std::memory_order_release); + pthread_kill(thread_id, SIGUSR1); + // wait up to 1 second for the runner to finish + auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds{1}; + while (true) { + if (finished.load(std::memory_order_acquire)) { + SPDLOG_INFO("AppSec helper finished"); + return 0; + } + if (std::chrono::steady_clock::now() >= deadline) { + SPDLOG_WARN("Could not finish AppSec helper before deadline"); + return 1; + } + std::this_thread::sleep_for(std::chrono::milliseconds{10}); // NOLINT + } + spdlog::shutdown(); return 0; } diff --git a/appsec/src/helper/network/acceptor.cpp b/appsec/src/helper/network/acceptor.cpp index 7ae10b99e1..4bb10c3386 100644 --- a/appsec/src/helper/network/acceptor.cpp +++ b/appsec/src/helper/network/acceptor.cpp @@ -21,6 +21,7 @@ using namespace std::chrono_literals; namespace dds::network::local { acceptor::acceptor(const std::string_view &sv) + // NOLINTNEXTLINE(android-cloexec-socket) : sock_(::socket(AF_UNIX, SOCK_STREAM, 0)) { if (sock_ == -1) { @@ -35,20 +36,34 @@ acceptor::acceptor(const std::string_view &sv) strcpy(static_cast(addr.sun_path), sv.data()); // NOLINT // Remove the existing socket - ::unlink(static_cast(addr.sun_path)); + int res = ::unlink(static_cast(addr.sun_path)); + if (res == -1 && errno != ENOENT) { + SPDLOG_ERROR("Failed to unlink {}: errno {}", addr.sun_path, errno); + throw std::system_error(errno, std::generic_category()); + } + SPDLOG_DEBUG("Unlinked {}", addr.sun_path); + + res = + // NOLINTNEXTLINE + ::bind(sock_, reinterpret_cast(&addr), sizeof(addr)); + if (res == -1) { + SPDLOG_ERROR( + "Failed to bind socket to {}: errno {}", addr.sun_path, errno); + throw std::system_error(errno, std::generic_category()); + } - socklen_t const len = sv.size() + sizeof(addr.sun_family); - // NOLINTNEXTLINE - auto res = ::bind(sock_, reinterpret_cast(&addr), len); + res = ::chmod(sv.data(), 0777); // NOLINT if (res == -1) { + SPDLOG_ERROR( + "Failed to chmod socket {}: errno {}", addr.sun_path, errno); throw std::system_error(errno, std::generic_category()); } - ::chmod(sv.data(), 0777); // NOLINT static constexpr int backlog = 50; if (::listen(sock_, backlog) == -1) { throw std::system_error(errno, std::generic_category()); } + SPDLOG_INFO("Started listening on {}", sv); } void acceptor::set_accept_timeout(std::chrono::seconds timeout) @@ -78,7 +93,7 @@ socket::ptr acceptor::accept() throw std::system_error(errno, std::generic_category()); } - SPDLOG_DEBUG("New socket: {}", s); + SPDLOG_DEBUG("accept() returned a new socket: {}", s); return std::make_unique(s); } diff --git a/appsec/src/helper/network/acceptor.hpp b/appsec/src/helper/network/acceptor.hpp index ec57df5398..7ed5e36fa3 100644 --- a/appsec/src/helper/network/acceptor.hpp +++ b/appsec/src/helper/network/acceptor.hpp @@ -47,7 +47,12 @@ class acceptor : public base_acceptor { return *this; } - ~acceptor() override { close(sock_); } + ~acceptor() override + { + if (sock_ != -1) { + close(sock_); + } + } void set_accept_timeout(std::chrono::seconds timeout) override; [[nodiscard]] base_socket::ptr accept() override; diff --git a/appsec/src/helper/remote_config/client_handler.cpp b/appsec/src/helper/remote_config/client_handler.cpp index c0e33ba829..4e38f3a38a 100644 --- a/appsec/src/helper/remote_config/client_handler.cpp +++ b/appsec/src/helper/remote_config/client_handler.cpp @@ -100,7 +100,8 @@ void client_handler::poll() { try { rc_client_->poll(); - } catch (dds::remote_config::network_exception & /** e */) { + } catch (const std::exception &e) { + SPDLOG_WARN("Error polling remote config: {}", e.what()); handle_error(); } } @@ -114,7 +115,9 @@ void client_handler::discover() interval_ = poll_interval_; return; } - } catch (dds::remote_config::network_exception & /** e */) {} + } catch (const std::exception &e) { + SPDLOG_WARN("Error discovering remote config: {}", e.what()); + } handle_error(); } diff --git a/appsec/src/helper/remote_config/http_api.cpp b/appsec/src/helper/remote_config/http_api.cpp index af5ad69248..70044c8f9b 100644 --- a/appsec/src/helper/remote_config/http_api.cpp +++ b/appsec/src/helper/remote_config/http_api.cpp @@ -33,7 +33,7 @@ const int version = 11; // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) net::awaitable execute_request(const std::string &host, -// NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) const std::string &port, const http::request &request) { std::string result; diff --git a/appsec/src/helper/runner.cpp b/appsec/src/helper/runner.cpp index 042cbccbd0..dcd500927c 100644 --- a/appsec/src/helper/runner.cpp +++ b/appsec/src/helper/runner.cpp @@ -17,9 +17,9 @@ namespace dds { namespace { network::base_acceptor::ptr acceptor_from_config(const config::config &cfg) { - auto value{cfg.get("socket_path")}; - if (value.size() >= 4 && value.substr(0, 3) == "fd:") { - auto rest{value.substr(3)}; + std::string_view const sock_path{cfg.socket_file_path()}; + if (sock_path.size() >= 4 && sock_path.substr(0, 3) == "fd:") { + auto rest{sock_path.substr(3)}; int const fd = std::stoi(std::string{rest}); // can throw struct stat statbuf {}; int const res = fstat(fd, &statbuf); @@ -30,19 +30,18 @@ network::base_acceptor::ptr acceptor_from_config(const config::config &cfg) return std::make_unique(fd); } - return std::make_unique(value); + return std::make_unique(sock_path); } } // namespace -runner::runner(const config::config &cfg) - : runner(cfg, acceptor_from_config(cfg)) +runner::runner(const config::config &cfg, std::atomic &interrupted) + : runner{cfg, acceptor_from_config(cfg), interrupted} {} -runner::runner( - const config::config &cfg, network::base_acceptor::ptr &&acceptor) +runner::runner(const config::config &cfg, + network::base_acceptor::ptr &&acceptor, std::atomic &interrupted) : cfg_(cfg), service_manager_{std::make_shared()}, - acceptor_(std::move(acceptor)), - idle_timeout_(cfg.get("runner_idle_timeout")) + acceptor_(std::move(acceptor)), interrupted_{interrupted} { try { acceptor_->set_accept_timeout(1min); @@ -56,26 +55,12 @@ void runner::run() { try { auto last_not_idle = std::chrono::steady_clock::now(); - SPDLOG_INFO("Running"); - while (running_) { + SPDLOG_INFO("Runner running"); + while (!interrupted()) { network::base_socket::ptr socket; try { socket = acceptor_->accept(); } catch (const timeout_error &e) { - // If there are clients running, we don't - if (worker_pool_.worker_count() > 0) { - // We are not idle, update - last_not_idle = std::chrono::steady_clock::now(); - continue; - } - - auto elapsed = std::chrono::steady_clock::now() - last_not_idle; - if (elapsed >= idle_timeout_) { - SPDLOG_INFO("Runner idle for {} minutes, exiting", - idle_timeout_.count()); - break; - } - continue; } @@ -84,7 +69,7 @@ void runner::run() break; } - if (!running_) { + if (interrupted()) { break; } @@ -101,6 +86,10 @@ void runner::run() } catch (const std::exception &e) { SPDLOG_ERROR("exception: {}", e.what()); } + + SPDLOG_INFO("Runner exiting, stopping pool"); + worker_pool_.stop(); + SPDLOG_INFO("Pool stopped"); } } // namespace dds diff --git a/appsec/src/helper/runner.hpp b/appsec/src/helper/runner.hpp index 6c01fd9432..f0ac1b7a5e 100644 --- a/appsec/src/helper/runner.hpp +++ b/appsec/src/helper/runner.hpp @@ -5,8 +5,9 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include +#include #include +#include #include "config.hpp" #include "network/acceptor.hpp" @@ -18,8 +19,9 @@ namespace dds { class runner { public: - explicit runner(const config::config &cfg); - runner(const config::config &cfg, network::base_acceptor::ptr &&acceptor); + runner(const config::config &cfg, std::atomic &interrupted); + runner(const config::config &cfg, network::base_acceptor::ptr &&acceptor, + std::atomic &interrupted); runner(const runner &) = delete; runner &operator=(const runner &) = delete; runner(runner &&) = delete; @@ -28,17 +30,19 @@ class runner { void run() noexcept(false); - void exit() { running_ = false; } + bool interrupted() const + { + return interrupted_.load(std::memory_order_acquire); + } private: - const config::config &cfg_; + const config::config &cfg_; // NOLINT std::shared_ptr service_manager_; worker::pool worker_pool_; // Server variables network::base_acceptor::ptr acceptor_; - std::chrono::minutes idle_timeout_; - std::atomic running_{true}; + std::atomic &interrupted_; // NOLINT }; } // namespace dds diff --git a/appsec/tests/extension/helper_extra_args_parsing.phpt b/appsec/tests/extension/helper_extra_args_parsing.phpt deleted file mode 100644 index 510781acf4..0000000000 --- a/appsec/tests/extension/helper_extra_args_parsing.phpt +++ /dev/null @@ -1,166 +0,0 @@ ---TEST-- -Parsing of datadog.appsec.helper_extra_flags ---INI-- ---FILE-- - ---EXPECTF-- -: -array(1) { - [0]=> - string(%d) "%s" -} - -"": -array(2) { - [0]=> - string(%d) "%s" - [1]=> - string(0) "" -} - - : -array(1) { - [0]=> - string(%d) "%s" -} - -foo bar: -array(3) { - [0]=> - string(%d) "%s" - [1]=> - string(3) "foo" - [2]=> - string(3) "bar" -} - -\foo \bar: -array(3) { - [0]=> - string(%d) "%s" - [1]=> - string(3) "foo" - [2]=> - string(3) "bar" -} - - foo bar : -array(3) { - [0]=> - string(%d) "%s" - [1]=> - string(3) "foo" - [2]=> - string(3) "bar" -} - -foo\ bar\ : -array(2) { - [0]=> - string(%d) "%s" - [1]=> - string(8) "foo bar " -} - -foo""'': -array(2) { - [0]=> - string(%d) "%s" - [1]=> - string(3) "foo" -} - -foo"'"'"': -array(2) { - [0]=> - string(%d) "%s" - [1]=> - string(5) "foo'"" -} - -foo"bar\""'bar\'': -array(2) { - [0]=> - string(%d) "%s" - [1]=> - string(11) "foobar"bar'" -} - -"foo bar": -array(2) { - [0]=> - string(%d) "%s" - [1]=> - string(7) "foo bar" -} - -'foo bar': -array(2) { - [0]=> - string(%d) "%s" - [1]=> - string(7) "foo bar" -} - - -With errors - -bar': - -Warning: datadog\appsec\testing\get_helper_argv(): [ddappsec] datadog.appsec.helper_extra_args has unmatched quotes: bar' in %s on line %d -array(0) { -} - -bar": - -Warning: datadog\appsec\testing\get_helper_argv(): [ddappsec] datadog.appsec.helper_extra_args has unmatched quotes: bar" in %s on line %d -array(0) { -} - -'bar: - -Warning: datadog\appsec\testing\get_helper_argv(): [ddappsec] datadog.appsec.helper_extra_args has unmatched quotes: 'bar in %s on line %d -array(0) { -} - -"bar: - -Warning: datadog\appsec\testing\get_helper_argv(): [ddappsec] datadog.appsec.helper_extra_args has unmatched quotes: "bar in %s on line %d -array(0) { -} - -bar\: - -Warning: datadog\appsec\testing\get_helper_argv(): [ddappsec] datadog.appsec.helper_extra_args has an unpaired \ at the end: bar\ in %s on line %d -array(0) { -} diff --git a/appsec/tests/extension/helper_launch_failure.phpt b/appsec/tests/extension/helper_launch_failure.phpt deleted file mode 100644 index 51cf9ecfd8..0000000000 --- a/appsec/tests/extension/helper_launch_failure.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -The helper exits immediately ---SKIPIF-- - ---INI-- -datadog.appsec.helper_path=/usr/bin/true -datadog.appsec.helper_launch=1 -datadog.appsec.log_level=debug -datadog.appsec.log_file=/tmp/php_appsec_test.log ---FILE-- - ---EXPECTF-- -bool(false) -array(2) { - ["failed_count"]=> - int(1) - ["next_retry"]=> - float(%f) -} -found message in log matching /%s/ diff --git a/appsec/tests/extension/helper_launch_lock_taken.phpt b/appsec/tests/extension/helper_launch_lock_taken.phpt deleted file mode 100644 index 01fee870f8..0000000000 --- a/appsec/tests/extension/helper_launch_lock_taken.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -Can't launch helper because the lock is taken ---INI-- -datadog.appsec.helper_runtime_path=/tmp/appsec-ext-test/ -datadog.appsec.helper_path=/usr/bin/true -datadog.appsec.helper_launch=1 -datadog.appsec.log_file=/tmp/php_appsec_test.log -datadog.appsec.log_level=info ---FILE-- - ---EXPECTF-- -bool(true) -bool(false) -found message in log matching /Attempting to connect to UNIX socket \/tmp\/appsec-ext-test\/ddappsec_%s.sock/ -found message in log matching /The helper lock on \/tmp\/appsec-ext-test\/ddappsec_%s.lock is already being held/ -array(2) { - ["failed_count"]=> - int(1) - ["next_retry"]=> - float(%f) -} diff --git a/appsec/tests/extension/helper_launch_ok.phpt b/appsec/tests/extension/helper_launch_ok.phpt deleted file mode 100644 index c307d79b74..0000000000 --- a/appsec/tests/extension/helper_launch_ok.phpt +++ /dev/null @@ -1,95 +0,0 @@ ---TEST-- -Test that the helper is launched with the correct properties ---SKIPIF-- - ---INI-- -datadog.appsec.helper_log_file=/tmp/helper_test.log -datadog.appsec.helper_launch=1 -datadog.appsec.log_file=/tmp/php_appsec_test.log -datadog.appsec.log_level=debug ---FILE-- - ---EXPECTF-- -helper_mgr_acquire_conn run -bool(true) - -Connected? -bool(true) - -Contents of helper log: -[%s] pre-exec: Going for second fork -[%s] pre-exec: Intermediate process exiting -[%s] pre-exec: About to call execv -Checking open file descriptors -* has file descriptor 0 -* has file descriptor 1 -* has file descriptor 2 -Checking procmask -array ( -) -Checking umask -0 -Checking parent uid (should be 1) -1 -Checking process group id == pid (is a process group leader) -OK -Checking session id == pid (is a session leader) -OK - -Checking socket id -file descriptor is %d -opened fd %d - -Accepting a connection: -accepted a new connection -read initial message from extension (size %d) -read remaining data diff --git a/appsec/tests/fuzzer/main.cpp b/appsec/tests/fuzzer/main.cpp index 7f09fc2611..f6add1dfea 100644 --- a/appsec/tests/fuzzer/main.cpp +++ b/appsec/tests/fuzzer/main.cpp @@ -3,51 +3,157 @@ // // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. -#include -#include -#include -#include + #include "mutators.hpp" #include "network.hpp" +#include #include +#include +#include +#include +#include dds::fuzzer::acceptor *acceptor; -std::function mutator; +std::function mutator; // NOLINT -extern "C" int LLVMFuzzerRunDriver(int *argc, char ***argv, - int (*UserCb)(const uint8_t *Data, size_t Size)); +extern "C" int LLVMFuzzerRunDriver( + int *argc, char ***argv, int (*UserCb)(const uint8_t *Data, size_t Size)); -extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) +extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) { return 0; } -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* bytes, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size) { - acceptor->push_socket(std::make_unique(bytes, size)); + acceptor->push_socket( + std::make_unique(bytes, size)); return 0; } // The custom mutator: -extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, - size_t MaxSize, unsigned int Seed) +extern "C" size_t LLVMFuzzerCustomMutator( + uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed) { return mutator(Data, Size, MaxSize, Seed); } +namespace { + +class ext_config : public dds::config::config { + static auto args_to_map(int argc, char **argv) + { + auto kv{std::map{}}; + for (int i = 1; i < argc; ++i) { + std::string_view arg(argv[i]); + if (arg.size() < 2 || arg.substr(0, 2) != "--") { + // Not an option, weird + continue; + } + arg.remove_prefix(2); + + // Check if the option has an assignment + auto pos = arg.find('='); + if (pos != std::string::npos) { + kv[arg.substr(0, pos)] = arg.substr(pos + 1); + continue; + } + + // Check the next argument + if ((i + 1) < argc) { + const std::string_view value(argv[i + 1]); + if (arg.size() < 2 || arg.substr(0, 2) != "--") { + // Not an option, so we assume it's a value + kv[arg] = value; + // Skip on next iteration + ++i; + continue; + } + } + + // If the next argument is an option or this is the last argument, + // we assume it's just a modifier. + kv[arg] = std::string_view(); + } + + return kv; + } + + std::function(std::string_view)> + make_resolve_func(int argc, char **argv) + { + auto args = args_to_map(argc, argv); + + // also go for this side effect + auto fm = args.find("fuzz-mode"); + if (fm != args.end()) { + fuzz_mode = fm->second; + } else { + fuzz_mode = "raw"; + } + + return + [args = std::move(args)]( + std::string_view env_name) -> std::optional { + auto it = args.find(map_to_arg_name(env_name)); + if (it == args.end()) { + if (env_name == env_log_file_path) { + return {"/dev/stderr"}; + } + return std::nullopt; + } + return it->second; + }; + } + + static std::string_view map_to_arg_name(std::string_view env_name) + { + if (env_name == env_socket_file_path) { + return "socket_path"; + } + if (env_name == env_lock_file_path) { + return "lock_path"; + } + if (env_name == env_log_file_path) { + return "log_path"; + } + if (env_name == env_log_level) { + return "log_level"; + } + return ""; + } + +public: + std::string_view fuzz_mode; + + ext_config(int argc, char **argv) + : dds::config::config{make_resolve_func(argc, argv)} + { + auto args = args_to_map(argc, argv); + + // also go for this side effect + auto fm = args.find("fuzz-mode"); + if (fm != args.end()) { + fuzz_mode = fm->second; + } else { + fuzz_mode = "raw"; + } + } +}; +} // namespace + int main(int argc, char **argv) { - dds::config::config config(argc, argv); + ext_config const config{argc, argv}; auto logger = spdlog::stderr_color_mt("ddappsec"); spdlog::set_default_logger(logger); logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l][%t] %v"); - spdlog::set_level( - spdlog::level::from_str(config.get("log_level"))); + spdlog::set_level(config.log_level()); std::string fuzz_mode; try { - fuzz_mode = config.get("fuzz-mode"); + fuzz_mode = config.fuzz_mode; } catch (...) { fuzz_mode = "raw"; } @@ -60,19 +166,20 @@ int main(int argc, char **argv) mutator = RawMutator; } else if (fuzz_mode == "off") { mutator = NopMutator; - }else { + } else { std::cerr << "Unsupported fuzzing mode, using raw mutator" << std::endl; } auto acceptor_ptr = std::make_unique(); acceptor = acceptor_ptr.get(); - dds::runner runner(config, std::move(acceptor_ptr)); + std::atomic interrupted; + dds::runner runner{config, std::move(acceptor_ptr), interrupted}; - std::thread runner_thread([&runner]{ runner.run(); }); + std::thread runner_thread([&runner] { runner.run(); }); - int result = LLVMFuzzerRunDriver(&argc, &argv, LLVMFuzzerTestOneInput); + int result = LLVMFuzzerRunDriver(&argc, &argv, LLVMFuzzerTestOneInput); - runner.exit(); + interrupted.store(true, std::memory_order_release); acceptor->exit(); runner_thread.join(); diff --git a/appsec/tests/fuzzer/mutators.hpp b/appsec/tests/fuzzer/mutators.hpp index 2ed9f2b53c..b8ea1100cf 100644 --- a/appsec/tests/fuzzer/mutators.hpp +++ b/appsec/tests/fuzzer/mutators.hpp @@ -5,6 +5,9 @@ // Copyright 2021 Datadog, Inc. #pragma once +#include +#include + // Forward-declare the libFuzzer's mutator callback. extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); diff --git a/appsec/tests/helper/config_test.cpp b/appsec/tests/helper/config_test.cpp index bba4e1e210..c37aad7b65 100644 --- a/appsec/tests/helper/config_test.cpp +++ b/appsec/tests/helper/config_test.cpp @@ -5,6 +5,8 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "common.hpp" #include +#include +#include namespace dds { @@ -15,74 +17,35 @@ template constexpr size_t vsize(T (&)[size]) } } // namespace -TEST(ConfigTest, ValidConstruction) -{ - char *argv[] = {const_cast("tester"), const_cast("--key"), - const_cast("value"), nullptr}; - EXPECT_NO_THROW(config::config(vsize(argv) - 1, argv)); -} - -TEST(ConfigTest, NonNullTerminatedListConstruction) -{ - char *argv[] = {const_cast("a"), const_cast("b")}; - EXPECT_NO_THROW(config::config(vsize(argv), argv)); -} - -TEST(ConfigTest, InvalidParameter) -{ - char *argv[] = {const_cast("tester"), - const_cast("parameter_missing_dashes"), nullptr}; - EXPECT_NO_THROW(config::config(vsize(argv) - 1, argv)); -} - TEST(ConfigTest, TestDefaultKeys) { - config::config cfg(0, nullptr); - EXPECT_NO_THROW(cfg.get("lock_path")); - EXPECT_NO_THROW(cfg.get("socket_path")); - EXPECT_NO_THROW(cfg.get("log_level")); + config::config cfg{[](std::string_view) { return std::nullopt; }}; + EXPECT_EQ(cfg.lock_file_path(), "/tmp/ddappsec.lock"sv); + EXPECT_EQ(cfg.socket_file_path(), "/tmp/ddappsec.sock"sv); + EXPECT_EQ(cfg.log_file_path(), "/tmp/ddappsec_helper.log"sv); + EXPECT_EQ(cfg.log_level(), spdlog::level::level_enum::warn); } TEST(ConfigTest, TestDefaultOverride) { - char *argv[] = {const_cast("tester"), - const_cast("--lock_path"), const_cast("unknown"), - const_cast("--socket_path"), const_cast("unknown"), - const_cast("--log_level"), const_cast("unknown"), - nullptr}; + static std::unordered_map defaults = { + {"_DD_SIDECAR_APPSEC_LOCK_FILE_PATH", "/foo/ddappsec.lock"}, + {"_DD_SIDECAR_APPSEC_SOCKET_FILE_PATH", "/foo/ddappsec.sock"}, + {"_DD_SIDECAR_APPSEC_LOG_FILE_PATH", "/foo/ddappsec_helper.log"}, + {"_DD_SIDECAR_APPSEC_LOG_LEVEL", "debug"}}; - config::config cfg(vsize(argv) - 1, argv); - EXPECT_TRUE(cfg.get("lock_path") == "unknown"); - EXPECT_TRUE(cfg.get("socket_path") == "unknown"); - EXPECT_TRUE(cfg.get("log_level") == "unknown"); -} + config::config cfg{[&](std::string_view key) { + auto it = defaults.find(key); + if (it != defaults.end()) { + return std::optional{it->second}; + } + return std::optional{}; + }}; -TEST(ConfigTest, TestInvalidKeys) -{ - config::config cfg(0, nullptr); - EXPECT_THROW(cfg.get("invalid"), std::out_of_range); + EXPECT_EQ(cfg.lock_file_path(), "/foo/ddappsec.lock"sv); + EXPECT_EQ(cfg.log_file_path(), "/foo/ddappsec_helper.log"sv); + EXPECT_EQ(cfg.socket_file_path(), "/foo/ddappsec.sock"sv); + EXPECT_EQ(cfg.log_level(), spdlog::level::level_enum::debug); } -TEST(ConfigTest, TestKeyValue) -{ - char *argv[] = {const_cast("tester"), const_cast("--a_key"), - const_cast("a_value"), const_cast("--b_key"), - const_cast("b_value"), const_cast("--c_key=c_value"), - nullptr}; - - config::config cfg(vsize(argv) - 1, argv); - EXPECT_TRUE(cfg.get("a_key") == "a_value"); - EXPECT_TRUE(cfg.get("b_key") == "b_value"); - EXPECT_TRUE(cfg.get("c_key") == "c_value"); -} - -TEST(ConfigTest, TestModifiers) -{ - int argc = 2; - char *argv[] = {const_cast("tester"), - const_cast("--modifier"), nullptr}; - - config::config cfg(argc, argv); - EXPECT_TRUE(cfg.get("modifier")); -} } // namespace dds diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index 83b5b9d6d5..fa20894b41 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -318,7 +318,7 @@ def buildAppSecTask = { String version, String variant -> ], outputs: [ volume: 'php-appsec', - files: ['ddappsec.so', 'ddappsec-helper'], + files: ['ddappsec.so', 'libddappsec-helper.so'], ], command: [ '-e', '-c', @@ -332,7 +332,7 @@ def buildAppSecTask = { String version, String variant -> -DCMAKE_TOOLCHAIN_FILE=/build/Toolchain.cmake \\ -DDD_APPSEC_TESTING=ON /project/appsec make -j extension ddappsec-helper && \\ - touch ddappsec.so ddappsec-helper + touch ddappsec.so libddappsec-helper.so ''' ] ) diff --git a/appsec/tests/integration/src/docker/toolchain/Makefile b/appsec/tests/integration/src/docker/toolchain/Makefile index 2290cff05e..cc09881c17 100644 --- a/appsec/tests/integration/src/docker/toolchain/Makefile +++ b/appsec/tests/integration/src/docker/toolchain/Makefile @@ -11,7 +11,7 @@ TARGET_ARCH := $(shell arch) TARGET := $(TARGET_ARCH)-none-linux-musl -install: .libcxx-installed .libcxxabi-installed +install: $(MUSL_SYSROOT)/lib/libglibc_compat.a .libcxx-installed .libcxxabi-installed clean: rm -rf src/ build/ *.tar.xz *.tar.gz \ .compiler-rt-installed .gcc-toolchain-installed .libcxxabi-installed \ @@ -22,6 +22,13 @@ CC_TOOLCHAIN := /usr GCC_TOOL_PREFIX := /usr/bin/ GCC_TOOLCHAIN_SYSROOT := / +$(MUSL_SYSROOT)/lib/libglibc_compat.a: glibc_compat.c .musl-installed + mkdir -p $(MUSL_SYSROOT)/lib + clang --sysroot $(MUSL_SYSROOT) -fpie -O2 -fno-omit-frame-pointer \ + -ggdb3 -c glibc_compat.c -o /tmp/glibc_compat.o && \ + ar rcs $@ /tmp/glibc_compat.o && \ + rm /tmp/glibc_compat.o + .gcc-toolchain-installed: cp -v /lib/$(TARGET_ARCH)-linux-gnu/libgcc_s.so.1 /usr/lib/gcc/$(TARGET_ARCH)-linux-gnu/10/libgcc_s.so.1 touch $@ @@ -152,14 +159,3 @@ COMMON_CMAKE_OPTIONS := -DCMAKE_BUILD_TYPE=$(RELTYPE) \ $(MAKE) -j $(shell nproc) && $(MAKE) install && \ popd && \ touch $@ - -libddwaf: - mkdir -p build/libddwaf && \ - pushd build/libddwaf && \ - cmake -DCMAKE_BUILD_TYPE=$(RELTYPE) -DCMAKE_TOOLCHAIN_FILE="$(realpath .)/Toolchain.cmake" \ - -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=PRE_TEST \ - -DLIBDDWAF_TEST_COVERAGE=OFF \ - ../../src/libddwaf && \ - make -j && make -j testPowerWAF && patchelf --remove-needed libc.so libddwaf.so && make package - -.PHONY: libddwaf diff --git a/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake b/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake index 04fd1fa44b..a444fc21c1 100644 --- a/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake +++ b/appsec/tests/integration/src/docker/toolchain/Toolchain.cmake @@ -16,12 +16,14 @@ set(CMAKE_ASM_COMPILER_TARGET ${triple}) set(CMAKE_C_COMPILER /usr/bin/clang-11) set(CMAKE_C_COMPILER_TARGET ${triple}) set(c_cxx_flags "-nostdinc -isystem${CMAKE_SYSROOT}/include -isystem/usr/lib/llvm-11/lib/clang/11.0.1/include -resource-dir ${CMAKE_SYSROOT} -Qunused-arguments -rtlib=compiler-rt -unwindlib=libunwind -static-libgcc") -set(CMAKE_C_FLAGS ${c_cxx_flags}) +set(CMAKE_C_FLAGS_INIT ${c_cxx_flags}) set(CMAKE_CXX_COMPILER /usr/bin/clang++-11) set(CMAKE_CXX_COMPILER_TARGET ${triple}) -set(CMAKE_CXX_FLAGS "-stdlib=libc++ -isystem${CMAKE_SYSROOT}/include/c++/v1 ${c_cxx_flags}") +set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++ -isystem${CMAKE_SYSROOT}/include/c++/v1 ${c_cxx_flags}") set(CMAKE_EXE_LINKER_FLAGS_INIT "-v -fuse-ld=lld -static -nodefaultlibs -lc++ -lc++abi ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a -lunwind -lc ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a") -set(CMAKE_SHARED_LINKER_FLAGS_INIT "-v -fuse-ld=lld -nodefaultlibs -Wl,-Bstatic -lc++ -lc++abi ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a -lunwind -Wl,-Bdynamic -lc ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "-v -fuse-ld=lld -nodefaultlibs -Wl,-Bstatic -lc++ -lc++abi ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a -lunwind -lglibc_compat -Wl,-Bdynamic ${CMAKE_SYSROOT}/lib/linux/libclang_rt.builtins-${ARCH}.a") +set(CMAKE_C_STANDARD_LIBRARIES "-Wl,-Bdynamic -lc") +set(CMAKE_CXX_STANDARD_LIBRARIES "-Wl,-Bdynamic -lc") set(CMAKE_NM /usr/bin/llvm-nm-11) set(CMAKE_RANLIB /usr/bin/llvm-ranlib-11) diff --git a/appsec/tests/integration/src/docker/toolchain/glibc_compat.c b/appsec/tests/integration/src/docker/toolchain/glibc_compat.c new file mode 100644 index 0000000000..68d5e1f978 --- /dev/null +++ b/appsec/tests/integration/src/docker/toolchain/glibc_compat.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) && !defined(__GLIBC__) + +# ifdef __x86_64__ +float ceilf(float x) +{ + float result; + // NOLINTNEXTLINE(hicpp-no-assembler) + __asm__("roundss $0x0A, %[x], %[result]" + : [result] "=x"(result) + : [x] "x"(x)); + return result; +} +double ceil(double x) +{ + double result; + // NOLINTNEXTLINE(hicpp-no-assembler) + __asm__("roundsd $0x0A, %[x], %[result]" + : [result] "=x"(result) + : [x] "x"(x)); + return result; +} +# endif + +# ifdef __aarch64__ +float ceilf(float x) +{ + float result; + __asm__("frintp %s0, %s1\n" : "=w"(result) : "w"(x)); + return result; +} +double ceil(double x) +{ + double result; + __asm__("frintp %d0, %d1\n" : "=w"(result) : "w"(x)); + return result; +} +# endif + +# ifdef __aarch64__ +# define _STAT_VER 0 +# else +# define _STAT_VER 1 +# endif + +// glibc before 2.33 (2021) doesn't have these +int stat(const char *restrict path, void *restrict buf) +{ + int __xstat(int, const char *restrict, void *restrict); + return __xstat(_STAT_VER, path, buf); +} + +int fstat(int fd, void *buf) +{ + int __fxstat(int, int, void *); + return __fxstat(_STAT_VER, fd, buf); +} + +int lstat(const char *restrict path, void *restrict buf) +{ + int __lxstat(int, const char *restrict, void *restrict); + return __lxstat(_STAT_VER, path, buf); +} + +// glibc doesn't define pthread_atfork on aarch64. We need to delegate to +// glibc's __register_atfork() instead. __register_atfork() takes an extra +// argument, __dso_handle, which is a pointer to the DSO that is registering the +// fork handlers. This is used to ensure that the handlers are not called after +// the DSO is unloaded. glibc on amd64 also implements pthread_atfork() in terms +// of __register_atfork(). (musl never unloads modules so that potential +// problem doesn't exist) + +// On amd64, even though pthread_atfork is exported by glibc, it should not be +// used. Code that uses pthread_atfork will compile to an import to +// __register_atfork(), but here we're compiling against musl, resulting in an +// an import to pthread_atfork. This will cause a runtime error after the test +// that unloads our module. The reason is that when we call pthread_atfork in +// glibc, __register_atfork() is called with the __dso_handle of libc6.so, not +// the __dso_handle of our module. So the fork handler is not unregistered when +// our module is unloaded. + +extern void *__dso_handle __attribute__((weak)); +int __register_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void), void *__dso_handle) __attribute__((weak)); + +int pthread_atfork( + void (*prepare)(void), void (*parent)(void), void (*child)(void)) +{ + // glibc + if (__dso_handle && __register_atfork) { + return __register_atfork(prepare, parent, child, __dso_handle); + } + + static int (*real_atfork)(void (*)(void), void (*)(void), void (*)(void)); + + if (!real_atfork) { + // dlopen musl +# ifdef __aarch64__ + void *handle = dlopen("ld-musl-aarch64.so.1", RTLD_LAZY); + if (!handle) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlopen of ld-musl-aarch64.so.1 failed: %s\n", + dlerror()); + abort(); + } +# else + void *handle = dlopen("libc.musl-x86_64.so.1", RTLD_LAZY); + if (!handle) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlopen of libc.musl-x86_64.so.1 failed: %s\n", + dlerror()); + abort(); + } +# endif + real_atfork = dlsym(handle, "pthread_atfork"); + if (!real_atfork) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlsym of pthread_atfork failed: %s\n", dlerror()); + abort(); + } + } + + return real_atfork(prepare, parent, child); +} + +// the symbol strerror_r in glibc is not the POSIX version; it returns char * +// __xpg_sterror_r is exported by both glibc and musl +int strerror_r(int errnum, char *buf, size_t buflen) +{ + int __xpg_strerror_r(int, char *, size_t); + return __xpg_strerror_r(errnum, buf, buflen); +} + +// when compiling with --coverage, some references to atexit show up. +// glibc doesn't provide atexit for similar reasons as pthread_atfork presumably +int __cxa_atexit(void (*func)(void *), void *arg, void *dso_handle); +int atexit(void (*function)(void)) +{ + if (!__dso_handle) { + (void)fprintf(stderr, "Aborting because __dso_handle is NULL\n"); + abort(); + } + + // the cast is harmless on amd64 and aarch64. Passing an extra argument to a + // function that expects none causes no problems + return __cxa_atexit((void (*)(void *))function, 0, __dso_handle); +} + +// introduced in glibc 2.25 +ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { + // SYS_getrandom is 318 (amd64) or 278 (aarch64) + // This was only added in Linux 3.17 (2014), so don't use it + // return syscall(SYS_getrandom, buf, buflen, flags); + int fd; + size_t bytes_read = 0; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return -1; + } + + while (bytes_read < buflen) { + ssize_t result = read(fd, (char*)buf + bytes_read, buflen - bytes_read); + if (result < 0) { + if (errno == EINTR) { + continue; + } + close(fd); + return -1; + } + bytes_read += result; + } + + close(fd); + return (ssize_t)bytes_read; +} + +#endif diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy index 2b3a51ab52..625e58c467 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy @@ -78,9 +78,10 @@ class AppSecContainer> extends GenericContain withEnv 'DD_TRACE_GIT_METADATA_ENABLED', '0' withEnv 'DD_INSTRUMENTATION_TELEMETRY_ENABLED', '1' withEnv '_DD_DEBUG_SIDECAR_LOG_METHOD', 'file:///tmp/logs/sidecar.log' + withEnv 'DD_SPAWN_WORKER_USE_EXEC', '1' // gdb fails following child with fdexec withEnv 'DD_TELEMETRY_HEARTBEAT_INTERVAL', '10' withEnv 'DD_TELEMETRY_EXTENDED_HEARTBEAT_INTERVAL', '10' - withEnv '_DD_SHARED_LIB_DEBUG', '1' + // withEnv '_DD_SHARED_LIB_DEBUG', '1' if (System.getProperty('XDEBUG') == '1') { Testcontainers.exposeHostPorts(9003) withEnv 'XDEBUG', '1' diff --git a/appsec/tests/integration/src/test/bin/enable_extensions.sh b/appsec/tests/integration/src/test/bin/enable_extensions.sh index ff3b67edcf..7363a31907 100755 --- a/appsec/tests/integration/src/test/bin/enable_extensions.sh +++ b/appsec/tests/integration/src/test/bin/enable_extensions.sh @@ -21,9 +21,9 @@ if [[ -f /appsec/ddappsec.so && -d /project ]]; then { echo extension=/appsec/ddappsec.so echo datadog.appsec.enabled=true - echo datadog.appsec.helper_extra_args=--log_level info - echo datadog.appsec.helper_path=/appsec/ddappsec-helper + echo datadog.appsec.helper_path=/appsec/libddappsec-helper.so echo datadog.appsec.helper_log_file=/tmp/logs/helper.log + echo datadog.appsec.helper_log_level=info echo datadog.appsec.rules=/etc/recommended.json echo datadog.appsec.log_file=/tmp/logs/appsec.log echo datadog.appsec.log_level=debug diff --git a/appsec/third_party/CMakeLists.txt b/appsec/third_party/CMakeLists.txt index d06aa54ff3..d925ea6a5f 100644 --- a/appsec/third_party/CMakeLists.txt +++ b/appsec/third_party/CMakeLists.txt @@ -22,6 +22,9 @@ if(DD_APPSEC_BUILD_HELPER) file(GLOB_RECURSE CPPBASE64_C_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cpp-base64/base64.cpp) add_library(cpp-base64 STATIC ${CPPBASE64_C_SOURCES}) target_include_directories(cpp-base64 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/cpp-base64) + set_target_properties(cpp-base64 PROPERTIES + POSITION_INDEPENDENT_CODE 1 + C_VISIBILITY_PRESET hidden) endif() if(DD_APPSEC_BUILD_EXTENSION) @@ -44,6 +47,7 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG eb3220622e73a4889eee355ffa37972b3cac3df5) FetchContent_MakeAvailable(spdlog) +set_target_properties(spdlog PROPERTIES POSITION_INDEPENDENT_CODE 1) set(ZLIB_VERSION v1.3) FetchContent_Declare( diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index a66edb1f3c..5355d883bb 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -155,6 +155,12 @@ void ddog_reset_logger(void); uint32_t ddog_get_logs_count(ddog_CharSlice level); +ddog_MaybeError ddog_sidecar_enable_appsec(ddog_CharSlice shared_lib_path, + ddog_CharSlice socket_file_path, + ddog_CharSlice lock_file_path, + ddog_CharSlice log_file_path, + ddog_CharSlice log_level); + ddog_MaybeError ddog_sidecar_connect_php(struct ddog_SidecarTransport **connection, const char *error_path, ddog_CharSlice log_level, diff --git a/components-rs/sidecar.rs b/components-rs/sidecar.rs index a6b747a8e6..5db98fb09a 100644 --- a/components-rs/sidecar.rs +++ b/components-rs/sidecar.rs @@ -1,11 +1,16 @@ use std::ffi::{c_char, CStr, OsStr}; +use std::ops::DerefMut; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; -use datadog_sidecar::config::{self, LogMethod}; +#[cfg(windows)] +use std::os::windows::ffi::OsStrExt; +use std::sync::Mutex; +use datadog_sidecar::config::{self, AppSecConfig, LogMethod}; use datadog_sidecar::service::blocking::SidecarTransport; use ddcommon_ffi::slice::AsBytes; use ddcommon_ffi::{CharSlice, self as ffi, MaybeError}; use ddtelemetry_ffi::try_c; +use lazy_static::lazy_static; #[cfg(any(windows, php_shared_build))] use spawn_worker::LibDependency; #[cfg(windows)] @@ -46,6 +51,52 @@ fn run_sidecar(mut cfg: config::Config) -> anyhow::Result { datadog_sidecar::start_or_connect_to_sidecar(cfg) } +lazy_static! { + static ref APPSEC_CONFIG: Mutex> = Mutex::new(None); +} + +// must be called prior to ddog_sidecar_connect +#[no_mangle] +pub extern "C" fn ddog_sidecar_enable_appsec( + shared_lib_path: CharSlice, + socket_file_path: CharSlice, + lock_file_path: CharSlice, + log_file_path: CharSlice, + log_level: CharSlice, +) -> MaybeError { + let mut appsec_config_guard = APPSEC_CONFIG.lock().unwrap(); + let shared_lib_path_os: std::ffi::OsString; + let socket_file_path_os: std::ffi::OsString; + let lock_file_path_os: std::ffi::OsString; + let log_file_path_os: std::ffi::OsString; + + #[cfg(unix)] + { + shared_lib_path_os = OsStr::from_bytes(shared_lib_path.as_bytes()).to_owned(); + socket_file_path_os = OsStr::from_bytes(socket_file_path.as_bytes()).to_owned(); + lock_file_path_os = OsStr::from_bytes(lock_file_path.as_bytes()).to_owned(); + log_file_path_os = OsStr::from_bytes(log_file_path.as_bytes()).to_owned(); + } + + #[cfg(windows)] + { + shared_lib_path_os = OsStr::new(&*shared_lib_path.to_utf8_lossy()).to_owned(); + socket_file_path_os = OsStr::new(&*socket_file_path.to_utf8_lossy()).to_owned(); + lock_file_path_os = OsStr::new(&*lock_file_path.to_utf8_lossy()).to_owned(); + log_file_path_os = OsStr::new(&*log_file_path.to_utf8_lossy()).to_owned(); + } + + appsec_config_guard.deref_mut().replace(AppSecConfig { + shared_lib_path: shared_lib_path_os, + socket_file_path: socket_file_path_os, + lock_file_path: lock_file_path_os, + log_file_path: log_file_path_os, + log_level: log_level.to_utf8_lossy().to_string(), + }); + + MaybeError::None +} + #[no_mangle] pub extern "C" fn ddog_sidecar_connect_php( connection: &mut *mut SidecarTransport, @@ -55,6 +106,8 @@ pub extern "C" fn ddog_sidecar_connect_php( ) -> MaybeError { let mut cfg = config::FromEnv::config(); cfg.self_telemetry = enable_telemetry; + let appsec_cfg_guard = APPSEC_CONFIG.lock().unwrap(); + cfg.appsec_config = appsec_cfg_guard.clone(); unsafe { if *error_path != 0 { let error_path = CStr::from_ptr(error_path).to_bytes(); diff --git a/datadog-setup.php b/datadog-setup.php index ce066b5933..8663b777f0 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -468,7 +468,7 @@ function install($options) $tmpArchiveRoot = $tmpDir . '/dd-library-php'; $tmpArchiveTraceRoot = $tmpDir . '/dd-library-php/trace'; $tmpArchiveAppsecRoot = $tmpDir . '/dd-library-php/appsec'; - $tmpArchiveAppsecBin = "{$tmpArchiveAppsecRoot}/bin"; + $tmpArchiveAppsecLib = "{$tmpArchiveAppsecRoot}/lib"; $tmpArchiveAppsecEtc = "{$tmpArchiveAppsecRoot}/etc"; $tmpArchiveProfilingRoot = $tmpDir . '/dd-library-php/profiling'; $tmpSrcDir = $tmpArchiveTraceRoot . '/src'; @@ -533,8 +533,8 @@ function install($options) // Appsec helper and rules if (file_exists($tmpArchiveAppsecRoot)) { execute_or_exit( - "Cannot copy files from '$tmpArchiveAppsecBin' to '$installDir'", - (IS_WINDOWS ? "xcopy /s /e /y /g /b /o /h " : "cp -rf ") . escapeshellarg("$tmpArchiveAppsecBin") . ' ' . escapeshellarg($installDir) + "Cannot copy files from '$tmpArchiveAppsecLib' to '$installDir'", + (IS_WINDOWS ? "xcopy /s /e /y /g /b /o /h " : "cp -rf ") . escapeshellarg("$tmpArchiveAppsecLib") . ' ' . escapeshellarg($installDir) ); execute_or_exit( "Cannot copy files from '$tmpArchiveAppsecEtc' to '$installDir'", @@ -617,7 +617,7 @@ function install($options) $appsecExtensionDestination = $extDir . '/' . EXTENSION_PREFIX . 'ddappsec.' . EXTENSION_SUFFIX; safe_copy_extension($appsecExtensionRealPath, $appsecExtensionDestination); } - $appSecHelperPath = $installDir . '/bin/ddappsec-helper'; + $appSecHelperPath = $installDir . '/lib/libddappsec-helper.so'; if (isset($options[OPT_PHP_INI])) { $iniFilePaths = $options[OPT_PHP_INI]; @@ -2274,24 +2274,11 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'commented' => false, 'description' => [ 'If ddappsec.helper_launch is enabled, this setting determines which binary', - 'the extension should try to execute.', + 'the extension should try to load in the sidecar.', 'Only relevant if ddappsec.helper_launch is enabled.', 'This ini setting is configured by the installer', ], ], - [ - 'name' => 'datadog.appsec.helper_extra_args', - 'default' => '', - 'commented' => true, - 'description' => [ - 'Additional arguments that should be used when attempting to launch the helper', - 'process. The extension always passes \'--lock_path - --socket_path fd:\'', - 'The arguments should be space separated. Both single and double quotes can', - 'be used should an argument contain spaces. The backslash (\) can be used to', - 'escape spaces, quotes, and the backslash itself.', - 'Only relevant if ddappsec.helper_launch is enabled', - ], - ], [ 'name' => 'datadog.appsec.rules', 'default' => $appsecRulesPath, @@ -2317,11 +2304,17 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'default' => '/dev/null', 'commented' => true, 'description' => [ - 'The location of the log file of the helper. This default to /dev/null (the log', - 'messages will be discarded). This file is opened by the extension just before', - 'launching the daemon and the file descriptor is passed to the helper as its', - 'stderr, to which it will write its messages; this setting is therefore only', - 'relevant if ddappsec.helper_launch is enabled', + 'The location of the log file of the helper. This defaults to /dev/null ', + '(the log messages will be discarded).', + ], + ], + [ + 'name' => 'datadog.appsec.helper_log_level', + 'default' => 'info', + 'commented' => true, + 'description' => [ + 'The verbosity of the logging of the appsec helper loaded in the sidecar. ', + 'Valid values are trace, debug, info, warn, err, critical and off', ], ], [ diff --git a/dockerfiles/verify_packages/installer/test_appsec_install_disabled.sh b/dockerfiles/verify_packages/installer/test_appsec_install_disabled.sh index a7b4e9e051..ed797d094d 100644 --- a/dockerfiles/verify_packages/installer/test_appsec_install_disabled.sh +++ b/dockerfiles/verify_packages/installer/test_appsec_install_disabled.sh @@ -20,5 +20,5 @@ assert_appsec_version "${version}" assert_appsec_disabled assert_file_exists "$(get_php_extension_dir)"/ddappsec.so -assert_file_exists /opt/datadog/dd-library/${version}/bin/ddappsec-helper +assert_file_exists /opt/datadog/dd-library/${version}/lib/libddappsec-helper.so assert_file_exists /opt/datadog/dd-library/${version}/etc/recommended.json diff --git a/dockerfiles/verify_packages/installer/test_appsec_install_enabled.sh b/dockerfiles/verify_packages/installer/test_appsec_install_enabled.sh index 1f2ec9dcec..d4408e3605 100644 --- a/dockerfiles/verify_packages/installer/test_appsec_install_enabled.sh +++ b/dockerfiles/verify_packages/installer/test_appsec_install_enabled.sh @@ -19,5 +19,5 @@ assert_appsec_version "${version}" assert_appsec_enabled assert_file_exists "$(get_php_extension_dir)"/ddappsec.so -assert_file_exists /opt/datadog/dd-library/${version}/bin/ddappsec-helper +assert_file_exists /opt/datadog/dd-library/${version}/lib/libddappsec-helper.so assert_file_exists /opt/datadog/dd-library/${version}/etc/recommended.json diff --git a/ext/configuration.h b/ext/configuration.h index 8328f15ade..919f5e5eda 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -205,6 +205,7 @@ enum ddtrace_sampling_rules_format { CONFIG(STRING, DD_TRACE_LOG_LEVEL, "error", .ini_change = ddtrace_alter_dd_trace_log_level, \ .env_config_fallback = ddtrace_conf_otel_log_level) \ CONFIG(BOOL, DD_APPSEC_SCA_ENABLED, "false", .ini_change = zai_config_system_ini_change) \ + CONFIG(BOOL, DD_APPSEC_TESTING, "false") \ CONFIG(BOOL, DD_TRACE_GIT_METADATA_ENABLED, "true") \ CONFIG(STRING, DD_GIT_COMMIT_SHA, "") \ CONFIG(STRING, DD_GIT_REPOSITORY_URL, "") \ diff --git a/ext/ddtrace.c b/ext/ddtrace.c index b51bbaaaa0..7b01f7498e 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -1,4 +1,3 @@ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/sidecar.c b/ext/sidecar.c index ce7bbc5f82..d35bd7286c 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -6,6 +6,7 @@ #include "logging.h" #include #include +#include #include "sidecar.h" #include "telemetry.h" #include "serializer.h" @@ -87,10 +88,34 @@ ddog_SidecarTransport *dd_sidecar_connection_factory(void) { return sidecar_transport; } +static void maybe_enable_appsec() { + // this must be done in ddtrace rather than ddappsec because + // the sidecar is launched by ddtrace before ddappsec has a chance + // to run its first rinit + +#if defined(__linux__) || defined(__APPLE__) + if (get_global_DD_APPSEC_TESTING()) { + return; + } + zend_module_entry *appsec_module = zend_hash_str_find_ptr(&module_registry, "ddappsec", sizeof("ddappsec") - 1); + if (!appsec_module) { + return; + } + void *handle = dlsym(appsec_module->handle, "dd_appsec_maybe_enable_helper"); + if (!handle) { + return; + } + void (*dd_appsec_maybe_enable_helper)(typeof(&ddog_sidecar_enable_appsec) enable_appsec) = handle; + dd_appsec_maybe_enable_helper(ddog_sidecar_enable_appsec); +#endif +} + void ddtrace_sidecar_setup(void) { ddtrace_set_non_resettable_sidecar_globals(); ddtrace_set_resettable_sidecar_globals(); + maybe_enable_appsec(); + ddtrace_sidecar = dd_sidecar_connection_factory(); if (!ddtrace_sidecar && ddtrace_endpoint) { // Something went wrong ddog_endpoint_drop(ddtrace_endpoint); diff --git a/libdatadog b/libdatadog index 89d4831877..866b79d450 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 89d4831877096f19e49888999fc8d1e4148237b9 +Subproject commit 866b79d450a7305f95d2792a9de199a13631a32d diff --git a/tooling/bin/generate-final-artifact.sh b/tooling/bin/generate-final-artifact.sh index 606d87176f..c475dec8fd 100755 --- a/tooling/bin/generate-final-artifact.sh +++ b/tooling/bin/generate-final-artifact.sh @@ -141,14 +141,14 @@ for architecture in "${architectures[@]}"; do done # Helper - mkdir -p "${tmp_folder_final_gnu_appsec}/bin" "${tmp_folder_final_musl_appsec}/bin" + mkdir -p "${tmp_folder_final_gnu_appsec}/lib" "${tmp_folder_final_musl_appsec}/lib" cp \ - "./appsec_${architecture}/ddappsec-helper" \ - "${tmp_folder_final_gnu_appsec}/bin/ddappsec-helper" + "./appsec_${architecture}/libddappsec-helper.so" \ + "${tmp_folder_final_gnu_appsec}/lib/libddappsec-helper.so" cp \ - "./appsec_${architecture}/ddappsec-helper" \ - "${tmp_folder_final_musl_appsec}/bin/ddappsec-helper" + "./appsec_${architecture}/libddappsec-helper.so" \ + "${tmp_folder_final_musl_appsec}/lib/libddappsec-helper.so" # Recommended rules mkdir -p "${tmp_folder_final_gnu_appsec}/etc" "${tmp_folder_final_musl_appsec}/etc" From 9d6dfa5c631431be84a0ed04865cda8321fef9c4 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Fri, 26 Jul 2024 17:46:57 +0100 Subject: [PATCH 015/103] Always enable sidecar when appsec is present --- .../SidecarFeaturesDisabledTests.groovy | 66 +++++++++++++++++++ ext/ddtrace.c | 3 +- 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/SidecarFeaturesDisabledTests.groovy diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/SidecarFeaturesDisabledTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/SidecarFeaturesDisabledTests.groovy new file mode 100644 index 0000000000..acd6916355 --- /dev/null +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/SidecarFeaturesDisabledTests.groovy @@ -0,0 +1,66 @@ +package com.datadog.appsec.php.integration + +import com.datadog.appsec.php.docker.AppSecContainer +import com.datadog.appsec.php.docker.FailOnUnmatchedTraces +import com.datadog.appsec.php.docker.InspectContainerHelper +import groovy.util.logging.Slf4j +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.DisabledIf +import org.testcontainers.junit.jupiter.Container +import org.testcontainers.junit.jupiter.Testcontainers + +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +import static com.datadog.appsec.php.integration.TestParams.getPhpVersion +import static com.datadog.appsec.php.integration.TestParams.getVariant +import static java.net.http.HttpResponse.BodyHandlers.ofString +import static org.testcontainers.containers.Container.ExecResult + +@Testcontainers +@Slf4j +@DisabledIf('isNotPhp83') +class SidecarFeaturesDisabledTests { + static boolean isNotPhp83() { + !getPhpVersion().startsWith('8.3') + } + + @Container + @FailOnUnmatchedTraces + public static final AppSecContainer CONTAINER = + new AppSecContainer( + workVolume: this.name, + baseTag: 'apache2-mod-php', + phpVersion: phpVersion, + phpVariant: variant, + www: 'base', + ) { + @Override + void configure() { + super.configure() + withEnv('DD_INSTRUMENTATION_TELEMETRY_ENABLED', 'false') + withEnv('DD_TRACE_SIDECAR_TRACE_SENDER', 'false') + } + } + + static void main(String[] args) { + InspectContainerHelper.run(CONTAINER) + } + + @Test + void 'appsec is enabled and sidecar is launched'() { + HttpRequest req = CONTAINER.buildReq('/hello.php') + .GET().build() + def trace = CONTAINER.traceFromRequest(req, ofString()) { HttpResponse resp -> + resp.body() == 'Hello world!' + } + + assert trace.first().metrics."_dd.appsec.enabled" == 1.0d + + ExecResult res = CONTAINER.execInContainer( + '/bin/bash', '-ce', 'ps auxww; pgrep -f datadog-ipc-helper') + if (res.exitCode != 0) { + throw new AssertionError("Could not find helper: $res.stdout\n$res.stderr") + } + } +} diff --git a/ext/ddtrace.c b/ext/ddtrace.c index 7b01f7498e..0c85d32110 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -412,7 +412,8 @@ static void dd_activate_once(void) { bgs_service = ddtrace_default_service_name(); } } - if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED() || get_global_DD_TRACE_SIDECAR_TRACE_SENDER()) + zend_module_entry *appsec_module = zend_hash_str_find_ptr(&module_registry, "ddappsec", sizeof("ddappsec") - 1); + if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED() || get_global_DD_TRACE_SIDECAR_TRACE_SENDER() || appsec_module) #endif { bool modules_activated = PG(modules_activated); From 73fe0854bfa5a29cd1b69d693fd2a76a228536a6 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 29 Jul 2024 11:57:05 +0100 Subject: [PATCH 016/103] Fix leak in _dd_curl_send_stack --- ext/coms.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/coms.c b/ext/coms.c index 15ace13601..0ae0263677 100644 --- a/ext/coms.c +++ b/ext/coms.c @@ -863,6 +863,10 @@ static void _dd_curl_send_stack(struct _writer_loop_data_t *writer, ddtrace_coms // We can ignore that for now as we don't do TLS traffic to the agent currently curl_easy_setopt(writer->curl, CURLOPT_NOSIGNAL, 1); + if (response.s) { + smart_str_free_ex(&response, true); + } + continue; } else { if (get_global_DD_TRACE_DEBUG_CURL_OUTPUT()) { From 1b33826c041c50b8c22ad1256c07df48126903ab Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 1 Aug 2024 18:46:57 +0100 Subject: [PATCH 017/103] make cbindgen --- components-rs/common.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/components-rs/common.h b/components-rs/common.h index dd08695ac3..1184bb3924 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -104,6 +104,20 @@ typedef struct ddog_Option_Error { typedef struct ddog_Option_Error ddog_MaybeError; +typedef enum ddog_Option_U32_Tag { + DDOG_OPTION_U32_SOME_U32, + DDOG_OPTION_U32_NONE_U32, +} ddog_Option_U32_Tag; + +typedef struct ddog_Option_U32 { + ddog_Option_U32_Tag tag; + union { + struct { + uint32_t some; + }; + }; +} ddog_Option_U32; + /** * A wrapper for returning owned strings from FFI */ @@ -321,6 +335,8 @@ void ddog_MaybeError_drop(ddog_MaybeError); DDOG_CHECK_RETURN struct ddog_Endpoint *ddog_endpoint_from_url(ddog_CharSlice url); +DDOG_CHECK_RETURN struct ddog_Endpoint *ddog_endpoint_from_filename(ddog_CharSlice filename); + DDOG_CHECK_RETURN struct ddog_Endpoint *ddog_endpoint_from_api_key(ddog_CharSlice api_key); DDOG_CHECK_RETURN @@ -332,6 +348,10 @@ void ddog_endpoint_set_timeout(struct ddog_Endpoint *endpoint, uint64_t millis); void ddog_endpoint_drop(struct ddog_Endpoint*); +struct ddog_Option_U32 ddog_Option_U32_some(uint32_t v); + +struct ddog_Option_U32 ddog_Option_U32_none(void); + /** * # Safety * Only pass null or a valid reference to a `ddog_StringWrapper`. From c1191af00ce8cb30bcedd502c655637bf811579a Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Fri, 2 Aug 2024 00:03:34 +0100 Subject: [PATCH 018/103] Unpin docker image in circleci --- .circleci/continue_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 6d5b659e60..0b79a25fc6 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3380,7 +3380,7 @@ jobs: - <<: *STEP_CHECKOUT - <<: *STEP_ATTACH_WORKSPACE - setup_docker: - docker_image: "public.ecr.aws/b1o7r7e0/nginx_musl_toolchain@sha256:afbb4503fd930729e884e18074b227b2ea182dd2895e580635ca119250aa0fe2" + docker_image: "public.ecr.aws/b1o7r7e0/nginx_musl_toolchain" - run: mkdir -p appsec_$(uname -m) - run: name: Build From fc551c29d75257204b6b0546c63a7f840d4f0a62 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 8 Aug 2024 10:43:32 +0100 Subject: [PATCH 019/103] [AppSec] Automated user ID collection modes (#2782) --- appsec/src/extension/configuration.c | 2 +- appsec/src/extension/configuration.h | 4 +- appsec/src/extension/tags.c | 195 +++++++----------- appsec/src/extension/tags.h | 4 +- appsec/src/extension/user_tracking.c | 139 +++++++++++++ appsec/src/extension/user_tracking.h | 16 ++ .../extension/headers_collection_06.phpt | 4 +- .../extension/headers_collection_07.phpt | 4 +- .../extension/headers_collection_08.phpt | 4 +- .../extension/headers_collection_09.phpt | 34 ++- .../extension/headers_collection_10.phpt | 34 ++- .../extension/headers_collection_11.phpt | 34 ++- ...stom_event_functions_appsec_disabled.phpt} | 0 .../track_user_login_failure_event.phpt | 2 +- ...gin_failure_event_automated_anon_mode.phpt | 38 ++++ ...ure_event_automated_anon_mode_compat.phpt} | 9 +- ...e_event_automated_anon_mode_full_name.phpt | 38 ++++ ..._failure_event_automated_default_mode.phpt | 4 +- ...lure_event_automated_disabled_config.phpt} | 15 +- ...failure_event_automated_disabled_mode.phpt | 4 +- ...n_failure_event_automated_ident_mode.phpt} | 8 +- ...ailure_event_automated_ident_mode_02.phpt} | 7 +- ...re_event_automated_ident_mode_compat.phpt} | 5 +- ...event_automated_ident_mode_full_name.phpt} | 8 +- ..._failure_event_automated_invalid_mode.phpt | 7 +- ..._failure_event_automated_safe_mode_04.phpt | 30 --- ..._failure_event_automated_safe_mode_05.phpt | 30 --- ...ser_login_failure_event_existing_user.phpt | 2 +- ...rack_user_login_failure_event_no_user.phpt | 2 +- ...ogin_failure_event_sdk_takes_priority.phpt | 4 +- ...n_failure_event_sdk_takes_priority_02.phpt | 2 +- ...n_failure_event_sdk_takes_priority_03.phpt | 2 +- ...n_failure_event_sdk_takes_priority_04.phpt | 8 +- ...n_failure_event_sdk_takes_priority_05.phpt | 8 +- ...n_failure_event_sdk_takes_priority_06.phpt | 8 +- .../track_user_login_success_event.phpt | 2 +- ...in_success_event_automated_anon_mode.phpt} | 9 +- ...ess_event_automated_anon_mode_compat.phpt} | 8 +- ...s_event_automated_anon_mode_full_name.phpt | 29 +++ ..._success_event_automated_default_mode.phpt | 2 +- ...cess_event_automated_disabled_config.phpt} | 10 +- ...success_event_automated_disabled_mode.phpt | 2 +- ...n_success_event_automated_ident_mode.phpt} | 8 +- ...uccess_event_automated_ident_mode_02.phpt} | 7 +- ...ss_event_automated_ident_mode_compat.phpt} | 5 +- ..._event_automated_ident_mode_full_name.phpt | 29 +++ ..._success_event_automated_invalid_mode.phpt | 6 +- ..._success_event_automated_safe_mode_04.phpt | 29 --- ..._success_event_automated_safe_mode_05.phpt | 29 --- ...ogin_success_event_sdk_takes_priority.phpt | 4 +- ...n_success_event_sdk_takes_priority_02.phpt | 2 +- ...n_success_event_sdk_takes_priority_03.phpt | 2 +- ...ser_signup_event_automated_anon_mode.phpt} | 16 +- ...nup_event_automated_anon_mode_compat.phpt} | 6 +- ...p_event_automated_anon_mode_full_name.phpt | 35 ++++ ...r_signup_event_automated_default_mode.phpt | 2 +- ...gnup_event_automated_disabled_config.phpt} | 16 +- ..._signup_event_automated_disabled_mode.phpt | 2 +- ..._signup_event_automated_extended_mode.phpt | 30 --- ...er_signup_event_automated_ident_mode.phpt} | 10 +- ...up_event_automated_ident_mode_compat.phpt} | 5 +- ...event_automated_ident_mode_full_name.phpt} | 9 +- ...r_signup_event_automated_invalid_mode.phpt | 6 +- ...r_signup_event_automated_safe_mode_05.phpt | 29 --- ..._user_signup_event_sdk_takes_priority.phpt | 2 +- ...er_signup_event_sdk_takes_priority_02.phpt | 2 +- ...er_signup_event_sdk_takes_priority_03.phpt | 2 +- ...racking_do_nothing_from_login_success.phpt | 2 +- .../php/integration/Laravel8xTests.groovy | 6 +- .../php/integration/Symfony62Tests.groovy | 8 +- 70 files changed, 639 insertions(+), 447 deletions(-) rename appsec/tests/extension/{tracking_functions_disabled_01.phpt => track_custom_event_functions_appsec_disabled.phpt} (100%) create mode 100644 appsec/tests/extension/track_user_login_failure_event_automated_anon_mode.phpt rename appsec/tests/extension/{track_user_login_failure_event_automated_safe_mode_06.phpt => track_user_login_failure_event_automated_anon_mode_compat.phpt} (70%) create mode 100644 appsec/tests/extension/track_user_login_failure_event_automated_anon_mode_full_name.phpt rename appsec/tests/extension/{track_user_login_failure_event_automated_safe_mode_02.phpt => track_user_login_failure_event_automated_disabled_config.phpt} (55%) rename appsec/tests/extension/{track_user_login_failure_event_automated_safe_mode_03.phpt => track_user_login_failure_event_automated_ident_mode.phpt} (65%) rename appsec/tests/extension/{track_user_login_failure_event_automated_extended_mode_02.phpt => track_user_login_failure_event_automated_ident_mode_02.phpt} (74%) rename appsec/tests/extension/{track_user_login_failure_event_automated_extended_mode.phpt => track_user_login_failure_event_automated_ident_mode_compat.phpt} (75%) rename appsec/tests/extension/{track_user_login_failure_event_automated_safe_mode.phpt => track_user_login_failure_event_automated_ident_mode_full_name.phpt} (64%) delete mode 100644 appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_04.phpt delete mode 100644 appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_05.phpt rename appsec/tests/extension/{track_user_login_success_event_automated_safe_mode_02.phpt => track_user_login_success_event_automated_anon_mode.phpt} (57%) rename appsec/tests/extension/{track_user_login_success_event_automated_safe_mode.phpt => track_user_login_success_event_automated_anon_mode_compat.phpt} (62%) create mode 100644 appsec/tests/extension/track_user_login_success_event_automated_anon_mode_full_name.phpt rename appsec/tests/extension/{track_user_login_success_event_automated_safe_mode_06.phpt => track_user_login_success_event_automated_disabled_config.phpt} (68%) rename appsec/tests/extension/{track_user_login_success_event_automated_safe_mode_03.phpt => track_user_login_success_event_automated_ident_mode.phpt} (62%) rename appsec/tests/extension/{track_user_login_success_event_automated_extended_mode_02.phpt => track_user_login_success_event_automated_ident_mode_02.phpt} (71%) rename appsec/tests/extension/{track_user_login_success_event_automated_extended_mode.phpt => track_user_login_success_event_automated_ident_mode_compat.phpt} (73%) create mode 100644 appsec/tests/extension/track_user_login_success_event_automated_ident_mode_full_name.phpt delete mode 100644 appsec/tests/extension/track_user_login_success_event_automated_safe_mode_04.phpt delete mode 100644 appsec/tests/extension/track_user_login_success_event_automated_safe_mode_05.phpt rename appsec/tests/extension/{track_user_signup_event_automated_safe_mode.phpt => track_user_signup_event_automated_anon_mode.phpt} (52%) rename appsec/tests/extension/{track_user_signup_event_automated_safe_mode_06.phpt => track_user_signup_event_automated_anon_mode_compat.phpt} (74%) create mode 100644 appsec/tests/extension/track_user_signup_event_automated_anon_mode_full_name.phpt rename appsec/tests/extension/{track_user_signup_event_automated_safe_mode_04.phpt => track_user_signup_event_automated_disabled_config.phpt} (56%) delete mode 100644 appsec/tests/extension/track_user_signup_event_automated_extended_mode.phpt rename appsec/tests/extension/{track_user_signup_event_automated_safe_mode_03.phpt => track_user_signup_event_automated_ident_mode.phpt} (59%) rename appsec/tests/extension/{track_user_signup_event_automated_extended_mode_02.phpt => track_user_signup_event_automated_ident_mode_compat.phpt} (78%) rename appsec/tests/extension/{track_user_signup_event_automated_safe_mode_02.phpt => track_user_signup_event_automated_ident_mode_full_name.phpt} (58%) delete mode 100644 appsec/tests/extension/track_user_signup_event_automated_safe_mode_05.phpt diff --git a/appsec/src/extension/configuration.c b/appsec/src/extension/configuration.c index cda3239ea7..b6e4259368 100644 --- a/appsec/src/extension/configuration.c +++ b/appsec/src/extension/configuration.c @@ -12,7 +12,7 @@ #include "ip_extraction.h" #include "logging.h" #include "php_objects.h" -#include "tags.h" +#include "user_tracking.h" #include "zai_string/string.h" #define DD_TO_DATADOG_INC 5 /* "DD" expanded to "datadog" */ diff --git a/appsec/src/extension/configuration.h b/appsec/src/extension/configuration.h index 9fdefb0404..6db9fde266 100644 --- a/appsec/src/extension/configuration.h +++ b/appsec/src/extension/configuration.h @@ -60,7 +60,9 @@ extern bool runtime_config_first_init; CONFIG(INT, DD_APPSEC_MAX_BODY_BUFF_SIZE, "524288") \ CONFIG(STRING, DD_TRACE_AGENT_URL, "") \ CONFIG(BOOL, DD_TRACE_ENABLED, "true") \ - CONFIG(CUSTOM(STRING), DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING, "safe", .parser = dd_parse_automated_user_events_tracking) \ + CALIAS(CUSTOM(STRING), DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE, "ident", \ + CALIASES("DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING"), .parser = dd_parse_user_collection_mode) \ + CONFIG(BOOL, DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED, "true") \ CONFIG(STRING, DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML, "") \ CONFIG(STRING, DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON, "") \ CONFIG(DOUBLE, DD_API_SECURITY_REQUEST_SAMPLE_RATE, "0.1", .ini_change = zai_config_system_ini_change) \ diff --git a/appsec/src/extension/tags.c b/appsec/src/extension/tags.c index 6b6ca7601d..626900526c 100644 --- a/appsec/src/extension/tags.c +++ b/appsec/src/extension/tags.c @@ -4,6 +4,7 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "tags.h" +#include "configuration.h" #include "ddappsec.h" #include "ddtrace.h" #include "ext/pcre/php_pcre.h" @@ -59,17 +60,6 @@ #define DD_EVENTS_USER_LOGIN_FAILURE_SDK \ "_dd.appsec.events.users.login.failure.sdk" -typedef enum _automated_user_events_tracking_mode { - NOT_ENABLED = 0, - SAFE, - EXTENDED -} automated_user_events_tracking_mode; -static THREAD_LOCAL_ON_ZTS automated_user_events_tracking_mode - automated_user_events_tracking = SAFE; -static zend_string *_mode_safe_cstr; -static zend_string *_mode_extended_cstr; -static THREAD_LOCAL_ON_ZTS zend_string *_mode_cstr; - static zend_string *_dd_tag_data_zstr; static zend_string *_dd_tag_event_zstr; static zend_string *_dd_tag_blocked_zstr; @@ -107,12 +97,10 @@ static zend_string *_true_zstr; static zend_string *_false_zstr; static zend_string *_track_zstr; static zend_string *_usr_exists_zstr; -static zend_string *_uuid_zstr; -static zend_string *_id_zstr; static zend_string *_server_zstr; static HashTable _relevant_headers; // headers for requests with attacks static HashTable _relevant_basic_headers; // headers for all requests -static THREAD_LOCAL_ON_ZTS bool _extended_user_event_triggered; +static THREAD_LOCAL_ON_ZTS bool _user_event_triggered; static THREAD_LOCAL_ON_ZTS bool _appsec_json_frags_inited; static THREAD_LOCAL_ON_ZTS zend_llist _appsec_json_frags; static THREAD_LOCAL_ON_ZTS zend_string *nullable _event_user_id; @@ -207,21 +195,8 @@ void dd_tags_startup() _usr_exists_zstr = zend_string_init_interned(LSTRARG("usr.exists"), 1 /* permanent */); - _uuid_zstr = zend_string_init_interned( - LSTRARG( - "/^[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}$/"), - 1 /* permanent */); - _id_zstr = - zend_string_init_interned(LSTRARG("/^\\d+$/"), 1 /* permanent */); - _server_zstr = zend_string_init_interned(LSTRARG("_SERVER"), 1); - _mode_safe_cstr = - zend_string_init_interned(LSTRARG("safe"), 1 /* permanent */); - _mode_extended_cstr = - zend_string_init_interned(LSTRARG("extended"), 1 /* permanent */); - _mode_cstr = _mode_safe_cstr; // default - _init_relevant_headers(); _register_functions(); @@ -292,7 +267,7 @@ void dd_tags_shutdown() void dd_tags_rinit() { bool init_list = false; - _extended_user_event_triggered = false; + _user_event_triggered = false; if (UNEXPECTED(!_appsec_json_frags_inited)) { init_list = true; _appsec_json_frags_inited = true; @@ -363,8 +338,8 @@ void dd_tags_add_tags( } _add_basic_ancillary_tags(span, server, - _extended_user_event_triggered ? &_relevant_headers - : &_relevant_basic_headers); + _user_event_triggered ? &_relevant_headers + : &_relevant_basic_headers); return; } @@ -884,12 +859,6 @@ bool match_regex(zend_string *pattern, zend_string *subject) return Z_TYPE(ret) == IS_LONG && Z_LVAL(ret) > 0; } -static bool _is_user_id_sensitive(zend_string *user_id) -{ - return !( - match_regex(_uuid_zstr, user_id) || match_regex(_id_zstr, user_id)); -} - static zval *nullable _root_span_get_meta() { zend_object *nullable span = dd_req_lifecycle_get_cur_span(); @@ -905,14 +874,6 @@ static zval *nullable _root_span_get_meta() return meta; } -static void _set_extended_user_event_triggered(bool automated) -{ - if (automated && automated_user_events_tracking != EXTENDED) { - return; - } - _extended_user_event_triggered = true; -} - static PHP_FUNCTION(datadog_appsec_track_user_signup_event) { UNUSED(return_value); @@ -925,25 +886,30 @@ static PHP_FUNCTION(datadog_appsec_track_user_signup_event) zend_string *user_id = NULL; HashTable *metadata = NULL; zend_bool automated = false; // Don't document. Only internal usage + zend_bool copy_user_id = true; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|hb", &user_id, &metadata, &automated) == FAILURE) { mlog(dd_log_warning, "Unexpected parameter combination, expected " "(user_id, metadata)"); return; } + if (automated) { - if (automated_user_events_tracking == NOT_ENABLED) { + user_collection_mode mode = dd_get_user_collection_mode(); + if (mode == user_mode_disabled || + !get_DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED()) { return; } - if (automated_user_events_tracking == SAFE) { - if (user_id != NULL && _is_user_id_sensitive(user_id)) { - user_id = NULL; + if (mode == user_mode_anon) { + // Anonymize the user ID and ensure it isn't copied twice + user_id = dd_user_id_anonymize(user_id); + if (user_id == NULL) { + mlog(dd_log_debug, "Failed to anonymize user ID"); + return; } - if (metadata != NULL && zend_array_count(metadata) > 0) { - metadata = NULL; - } + copy_user_id = false; } } else { if (user_id == NULL || ZSTR_LEN(user_id) == 0) { @@ -954,10 +920,13 @@ static PHP_FUNCTION(datadog_appsec_track_user_signup_event) zval *nullable meta = _root_span_get_meta(); if (!meta) { + if (!copy_user_id) { + zend_string_release(user_id); + } return; } - _set_extended_user_event_triggered(automated); + _user_event_triggered = true; zend_array *meta_ht = Z_ARRVAL_P(meta); bool override = !automated; @@ -965,24 +934,27 @@ static PHP_FUNCTION(datadog_appsec_track_user_signup_event) if (user_id && ZSTR_LEN(user_id) > 0) { // usr.id = _add_new_zstr_to_meta( - meta_ht, _dd_tag_user_id, user_id, true, override); + meta_ht, _dd_tag_user_id, user_id, copy_user_id, override); } if (automated) { + // In automated mode, metadata must no longer be sent + // _dd.appsec.events.users.signup.auto.mode = // - if (_mode_cstr && automated_user_events_tracking != NOT_ENABLED) { + if (dd_get_user_collection_mode() != user_mode_disabled) { _add_new_zstr_to_meta(meta_ht, _dd_signup_event_auto_mode, - _mode_cstr, true, override); + dd_get_user_collection_mode_zstr(), true, override); } } else { // _dd.appsec.events.users.signup.sdk = true _add_new_zstr_to_meta( meta_ht, _dd_signup_event_sdk, _true_zstr, true, override); - } - // appsec.events.users.signup. = - _add_custom_event_metadata(meta_ht, _dd_signup_event, metadata, override); + // appsec.events.users.signup. = + _add_custom_event_metadata( + meta_ht, _dd_signup_event, metadata, override); + } // appsec.events.users.login.success.track = true _add_custom_event_keyval( @@ -1003,6 +975,7 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_success_event) zend_string *user_id = NULL; HashTable *metadata = NULL; zend_bool automated = false; // Don't document. Only internal usage + zend_bool copy_user_id = true; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|hb", &user_id, &metadata, &automated) == FAILURE) { mlog(dd_log_warning, "Unexpected parameter combination, expected " @@ -1010,18 +983,20 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_success_event) return; } if (automated) { - if (automated_user_events_tracking == NOT_ENABLED) { + user_collection_mode mode = dd_get_user_collection_mode(); + if (mode == user_mode_disabled || + !get_DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED()) { return; } - if (automated_user_events_tracking == SAFE) { - if (user_id != NULL && _is_user_id_sensitive(user_id)) { - user_id = NULL; + if (mode == user_mode_anon) { + user_id = dd_user_id_anonymize(user_id); + if (user_id == NULL) { + mlog(dd_log_debug, "Failed to anonymize user ID"); + return; } - if (metadata != NULL && zend_array_count(metadata) > 0) { - metadata = NULL; - } + copy_user_id = false; } } else { if (user_id == NULL || ZSTR_LEN(user_id) == 0) { @@ -1032,10 +1007,13 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_success_event) zval *nullable meta = _root_span_get_meta(); if (!meta) { + if (!copy_user_id) { + zend_string_release(user_id); + } return; } - _set_extended_user_event_triggered(automated); + _user_event_triggered = true; zend_array *meta_ht = Z_ARRVAL_P(meta); bool override = !automated; @@ -1044,30 +1022,32 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_success_event) dd_find_and_apply_verdict_for_user(user_id); // usr.id = _add_new_zstr_to_meta( - meta_ht, _dd_tag_user_id, user_id, true, override); + meta_ht, _dd_tag_user_id, user_id, copy_user_id, override); } if (automated) { + // In automated mode, metadata must no longer be sent + // _dd.appsec.events.users.login.success.auto.mode = // - if (_mode_cstr && automated_user_events_tracking != NOT_ENABLED) { + if (dd_get_user_collection_mode() != user_mode_disabled) { _add_new_zstr_to_meta(meta_ht, _dd_login_success_event_auto_mode, - _mode_cstr, true, override); + dd_get_user_collection_mode_zstr(), true, override); } } else { // _dd.appsec.events.users.login.success.sdk = true _add_new_zstr_to_meta( meta_ht, _dd_login_success_event_sdk, _true_zstr, true, override); + + // appsec.events.users.login.success. = + _add_custom_event_metadata( + meta_ht, _dd_login_success_event, metadata, override); } // appsec.events.users.login.success.track = true _add_custom_event_keyval(meta_ht, _dd_login_success_event, _track_zstr, _true_zstr, true, override); - // appsec.events.users.login.success. = - _add_custom_event_metadata( - meta_ht, _dd_login_success_event, metadata, override); - dd_tags_set_sampling_priority(); } @@ -1091,16 +1071,23 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_failure_event) return; } + zend_bool copy_user_id = true; if (automated) { - if (automated_user_events_tracking == NOT_ENABLED) { + user_collection_mode mode = dd_get_user_collection_mode(); + if (mode == user_mode_disabled || + !get_DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED()) { return; } - if (automated_user_events_tracking == SAFE) { - if (_is_user_id_sensitive(user_id)) { - user_id = NULL; + if (mode == user_mode_anon) { + user_id = dd_user_id_anonymize(user_id); + if (user_id == NULL) { + mlog(dd_log_debug, "Failed to anonymize user ID"); + return; } + copy_user_id = false; + if (metadata != NULL && zend_array_count(metadata) > 0) { metadata = NULL; } @@ -1109,10 +1096,13 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_failure_event) zval *nullable meta = _root_span_get_meta(); if (!meta) { + if (!copy_user_id) { + zend_string_release(user_id); + } return; } - _set_extended_user_event_triggered(automated); + _user_event_triggered = true; zend_array *meta_ht = Z_ARRVAL_P(meta); bool override = !automated; @@ -1120,7 +1110,7 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_failure_event) if (user_id != NULL && ZSTR_LEN(user_id) > 0) { // appsec.events.users.login.failure.usr.id = _add_custom_event_keyval(meta_ht, _dd_login_failure_event, - _dd_tag_user_id, user_id, true, override); + _dd_tag_user_id, user_id, copy_user_id, override); } // appsec.events.users.login.failure.track = true @@ -1128,26 +1118,28 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_failure_event) _true_zstr, true, override); if (automated) { + // In automated mode, metadata must no longer be sent + // _dd.appsec.events.users.login.failure.auto.mode = // - if (_mode_cstr && automated_user_events_tracking != NOT_ENABLED) { + if (dd_get_user_collection_mode() != user_mode_disabled) { _add_new_zstr_to_meta(meta_ht, _dd_login_failure_event_auto_mode, - _mode_cstr, true, override); + dd_get_user_collection_mode_zstr(), true, override); } } else { // _dd.appsec.events.users.login.success.sdk = true _add_new_zstr_to_meta( meta_ht, _dd_login_failure_event_sdk, _true_zstr, true, override); + + // appsec.events.users.login.failure. = + _add_custom_event_metadata( + meta_ht, _dd_login_failure_event, metadata, override); } // appsec.events.users.login.failure.usr.exists = _add_custom_event_keyval(meta_ht, _dd_login_failure_event, _usr_exists_zstr, exists ? _true_zstr : _false_zstr, true, override); - // appsec.events.users.login.failure. = - _add_custom_event_metadata( - meta_ht, _dd_login_failure_event, metadata, override); - dd_tags_set_sampling_priority(); } @@ -1209,39 +1201,6 @@ static bool _set_appsec_enabled(zval *metrics_zv) NULL; } -bool dd_parse_automated_user_events_tracking( - zai_str value, zval *nonnull decoded_value, bool persistent) -{ - if (!value.len) { - return false; - } - - bool result = false; - size_t len = strlen((const char *)value.ptr); - if (dd_string_equals_lc(value.ptr, len, ZEND_STRL("safe"))) { - automated_user_events_tracking = SAFE; - result = true; - _mode_cstr = _mode_safe_cstr; - } else if (dd_string_equals_lc(value.ptr, len, ZEND_STRL("extended"))) { - automated_user_events_tracking = EXTENDED; - result = true; - _mode_cstr = _mode_extended_cstr; - } else if (dd_string_equals_lc(value.ptr, len, ZEND_STRL("disabled"))) { - automated_user_events_tracking = NOT_ENABLED; - result = true; - _mode_cstr = NULL; - } - - if (result) { - ZVAL_STR(decoded_value, zend_string_alloc(value.len, persistent)); - char *out = Z_STRVAL_P(decoded_value); - memcpy(out, value.ptr, value.len); - out[value.len] = '\0'; - } - - return result; -} - static PHP_FUNCTION(datadog_appsec_testing_add_all_ancillary_tags) { UNUSED(return_value); diff --git a/appsec/src/extension/tags.h b/appsec/src/extension/tags.h index 19beb1084b..172677e435 100644 --- a/appsec/src/extension/tags.h +++ b/appsec/src/extension/tags.h @@ -5,7 +5,6 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once #include "attributes.h" -#include "configuration.h" #include #include #include @@ -27,5 +26,4 @@ void dd_tags_set_event_user_id(zend_string *nonnull zstr); // does not increase the refcount on zstr void dd_tags_add_appsec_json_frag(zend_string *nonnull zstr); -bool dd_parse_automated_user_events_tracking( - zai_str value, zval *nonnull decoded_value, bool persistent); + diff --git a/appsec/src/extension/user_tracking.c b/appsec/src/extension/user_tracking.c index 43017fc0d0..8f301128f4 100644 --- a/appsec/src/extension/user_tracking.c +++ b/appsec/src/extension/user_tracking.c @@ -17,9 +17,24 @@ #include "string_helpers.h" #include "tags.h" #include +#include +#include + +static THREAD_LOCAL_ON_ZTS user_collection_mode _user_mode = user_mode_disabled; + +static zend_string *_user_mode_anon_zstr; +static zend_string *_user_mode_ident_zstr; +static zend_string *_user_mode_disabled_zstr; +static zend_string *_sha256_algo_zstr; static void (*_ddtrace_set_user)(INTERNAL_FUNCTION_PARAMETERS) = NULL; +#if PHP_VERSION_ID < 80000 +typedef const php_hash_ops *(*hash_fetch_ops_t)( + const char *algo, size_t algo_len); +static hash_fetch_ops_t _hash_fetch_ops; +#endif + static PHP_FUNCTION(set_user_wrapper) { if (DDAPPSEC_G(active) || UNEXPECTED(get_global_DD_APPSEC_TESTING())) { @@ -45,6 +60,24 @@ static PHP_FUNCTION(set_user_wrapper) void dd_user_tracking_startup(void) { + _user_mode_ident_zstr = zend_string_init_interned( + LSTRARG("identification"), 1 /* persistent */); + _user_mode_anon_zstr = + zend_string_init_interned(LSTRARG("anonymization"), 1 /* persistent */); + _user_mode_disabled_zstr = + zend_string_init_interned(LSTRARG("disabled"), 1 /* persistent */); + _sha256_algo_zstr = + zend_string_init_interned(LSTRARG("sha256"), 1 /* persistent */); + +#if PHP_VERSION_ID < 80000 + _hash_fetch_ops = + (hash_fetch_ops_t)dlsym(RTLD_DEFAULT, "php_hash_fetch_ops"); + if (!_hash_fetch_ops) { + mlog(dd_log_warning, "Failed to load php_hash_fetch_ops: %s", + dlerror()); // NOLINT(concurrency-mt-unsafe) + } +#endif + if (!dd_trace_loaded()) { return; } @@ -115,3 +148,109 @@ void dd_find_and_apply_verdict_for_user(zend_string *nonnull user_id) } } } + +bool dd_parse_user_collection_mode( + zai_str value, zval *nonnull decoded_value, bool persistent) +{ + if (!value.len) { + return false; + } + + if (dd_string_equals_lc(value.ptr, value.len, ZEND_STRL("ident")) || + dd_string_equals_lc( + value.ptr, value.len, ZEND_STRL("identification")) || + dd_string_equals_lc(value.ptr, value.len, ZEND_STRL("extended"))) { + _user_mode = user_mode_ident; + } else if (dd_string_equals_lc(value.ptr, value.len, ZEND_STRL("anon")) || + dd_string_equals_lc( + value.ptr, value.len, ZEND_STRL("anonymization")) || + dd_string_equals_lc(value.ptr, value.len, ZEND_STRL("safe"))) { + _user_mode = user_mode_anon; + } else { // If the value is disabled or an unknown value, we disable user ID + // collection + + if (!get_global_DD_APPSEC_TESTING()) { + mlog_g(dd_log_warning, "Unknown user collection mode: %.*s", + (int)value.len, value.ptr); + } + _user_mode = user_mode_disabled; + } + + ZVAL_STR(decoded_value, zend_string_init(value.ptr, value.len, persistent)); + + return true; +} + +zend_string *nullable dd_user_id_anonymize(zend_string *nonnull user_id) +{ + zend_string *digest; + const php_hash_ops *ops; + void *context; + +#if PHP_VERSION_ID < 80000 + if (!_hash_fetch_ops) { + return NULL; + } + + ops = _hash_fetch_ops( + ZSTR_VAL(_sha256_algo_zstr), ZSTR_LEN(_sha256_algo_zstr)); +#else + ops = php_hash_fetch_ops(_sha256_algo_zstr); +#endif + if (!ops) { + mlog(dd_log_debug, "Failed to load sha256 algorithm"); + return NULL; + } + +#if PHP_VERSION_ID < 80000 + context = emalloc(ops->context_size); +#else + context = php_hash_alloc_context(ops); +#endif + +#if PHP_VERSION_ID < 80100 + ops->hash_init(context); +#else + ops->hash_init(context, NULL); +#endif + + ops->hash_update( + context, (unsigned char *)ZSTR_VAL(user_id), ZSTR_LEN(user_id)); + + digest = zend_string_alloc(ops->digest_size, 0); + ops->hash_final((unsigned char *)ZSTR_VAL(digest), context); + efree(context); + +#define ANON_PREFIX "anon_" + // Anonymized IDs start with anon_ followed by the 128 most-significant bits + // of the sha256 + zend_string *anon_user_id = zend_string_safe_alloc( + LSTRLEN(ANON_PREFIX) + ops->digest_size, 1, 0, 0); + + // Copy prefix + memcpy(ZSTR_VAL(anon_user_id), LSTRARG(ANON_PREFIX)); + + char *digest_begin = ZSTR_VAL(anon_user_id) + LSTRLEN(ANON_PREFIX); + php_hash_bin2hex( + digest_begin, (unsigned char *)ZSTR_VAL(digest), ops->digest_size / 2); + digest_begin[ops->digest_size] = 0; + + zend_string_release(digest); + + return anon_user_id; +} + +user_collection_mode dd_get_user_collection_mode() { return _user_mode; } + +zend_string *nonnull dd_get_user_collection_mode_zstr() +{ + if (_user_mode == user_mode_ident) { + return _user_mode_ident_zstr; + } + + if (_user_mode == user_mode_anon) { + return _user_mode_anon_zstr; + } + + return _user_mode_disabled_zstr; +} diff --git a/appsec/src/extension/user_tracking.h b/appsec/src/extension/user_tracking.h index e55fc36578..2137b8c0c4 100644 --- a/appsec/src/extension/user_tracking.h +++ b/appsec/src/extension/user_tracking.h @@ -5,9 +5,25 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once +#include "configuration.h" #include "attributes.h" #include +typedef enum _user_collection_mode { + user_mode_disabled = 0, + user_mode_anon, + user_mode_ident, +} user_collection_mode; + void dd_user_tracking_startup(void); void dd_user_tracking_shutdown(void); + void dd_find_and_apply_verdict_for_user(zend_string *nonnull user_id); + +bool dd_parse_user_collection_mode( + zai_str value, zval *nonnull decoded_value, bool persistent); + +zend_string*nullable dd_user_id_anonymize(zend_string *nonnull user_id); + +user_collection_mode dd_get_user_collection_mode(void); +zend_string *nonnull dd_get_user_collection_mode_zstr(void); diff --git a/appsec/tests/extension/headers_collection_06.phpt b/appsec/tests/extension/headers_collection_06.phpt index f5cfe6fe64..2b7ae5a853 100644 --- a/appsec/tests/extension/headers_collection_06.phpt +++ b/appsec/tests/extension/headers_collection_06.phpt @@ -1,12 +1,12 @@ --TEST-- -All headers are collected when track_user_signup_event is triggered by automation and extended mode is set +All headers are collected when track_user_signup_event is triggered by automation and identification mode is set --INI-- extension=ddtrace.so datadog.appsec.log_file=/tmp/php_appsec_test.log datadog.appsec.log_level=debug datadog.appsec.enabled=1 --ENV-- -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident HTTP_X_FORWARDED_FOR=7.7.7.7 DD_TRACE_CLIENT_IP_HEADER_DISABLED=true HTTP_X_FORWARDED_FOR=7.7.7.6,10.0.0.1 diff --git a/appsec/tests/extension/headers_collection_07.phpt b/appsec/tests/extension/headers_collection_07.phpt index 373a9cd8f0..7dd33113f3 100644 --- a/appsec/tests/extension/headers_collection_07.phpt +++ b/appsec/tests/extension/headers_collection_07.phpt @@ -1,12 +1,12 @@ --TEST-- -All headers are collected when track_user_login_success_event is triggered by automation and extended mode is set +All headers are collected when track_user_login_success_event is triggered by automation and identification mode is set --INI-- extension=ddtrace.so datadog.appsec.log_file=/tmp/php_appsec_test.log datadog.appsec.log_level=debug datadog.appsec.enabled=1 --ENV-- -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident HTTP_X_FORWARDED_FOR=7.7.7.7 DD_TRACE_CLIENT_IP_HEADER_DISABLED=true HTTP_X_FORWARDED_FOR=7.7.7.6,10.0.0.1 diff --git a/appsec/tests/extension/headers_collection_08.phpt b/appsec/tests/extension/headers_collection_08.phpt index e052c3b44f..adf34edef3 100644 --- a/appsec/tests/extension/headers_collection_08.phpt +++ b/appsec/tests/extension/headers_collection_08.phpt @@ -1,12 +1,12 @@ --TEST-- -All headers are collected when track_user_login_failure_event is triggered by automation and extended mode is set +All headers are collected when track_user_login_failure_event is triggered by automation and identification mode is set --INI-- extension=ddtrace.so datadog.appsec.log_file=/tmp/php_appsec_test.log datadog.appsec.log_level=debug datadog.appsec.enabled=1 --ENV-- -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident HTTP_X_FORWARDED_FOR=7.7.7.7 DD_TRACE_CLIENT_IP_HEADER_DISABLED=true HTTP_X_FORWARDED_FOR=7.7.7.6,10.0.0.1 diff --git a/appsec/tests/extension/headers_collection_09.phpt b/appsec/tests/extension/headers_collection_09.phpt index 4497cbd394..f55f03e657 100644 --- a/appsec/tests/extension/headers_collection_09.phpt +++ b/appsec/tests/extension/headers_collection_09.phpt @@ -1,12 +1,12 @@ --TEST-- -Basic headers are collected when track_user_signup_event is triggered by automation and extended mode is not set +All headers are collected when track_user_signup_event is triggered by automation and anonymization mode is set --INI-- extension=ddtrace.so datadog.appsec.log_file=/tmp/php_appsec_test.log datadog.appsec.log_level=debug datadog.appsec.enabled=1 --ENV-- -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon HTTP_X_FORWARDED_FOR=7.7.7.7 DD_TRACE_CLIENT_IP_HEADER_DISABLED=true HTTP_X_FORWARDED_FOR=7.7.7.6,10.0.0.1 @@ -80,25 +80,53 @@ var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- -array(11) { +array(25) { ["http.request.headers.accept"]=> string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" ["http.request.headers.cf-ray"]=> string(5) "cfray" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.forwarded-for"]=> + string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" ["http.request.headers.user-agent"]=> string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" ["http.request.headers.x-appgw-trace-id"]=> string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" ["http.request.headers.x-sigsci-requestid"]=> string(15) "sigscirequestid" ["http.request.headers.x-sigsci-tags"]=> diff --git a/appsec/tests/extension/headers_collection_10.phpt b/appsec/tests/extension/headers_collection_10.phpt index 7a80d1db88..ad6791bfa7 100644 --- a/appsec/tests/extension/headers_collection_10.phpt +++ b/appsec/tests/extension/headers_collection_10.phpt @@ -1,12 +1,12 @@ --TEST-- -Basic headers are collected when track_user_login_success_event is triggered by automation and extended mode is not set +All headers are collected when track_user_login_success_event is triggered by automation and anonymization mode is set --INI-- extension=ddtrace.so datadog.appsec.log_file=/tmp/php_appsec_test.log datadog.appsec.log_level=debug datadog.appsec.enabled=1 --ENV-- -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon HTTP_X_FORWARDED_FOR=7.7.7.7 DD_TRACE_CLIENT_IP_HEADER_DISABLED=true HTTP_X_FORWARDED_FOR=7.7.7.6,10.0.0.1 @@ -80,25 +80,53 @@ var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- -array(11) { +array(25) { ["http.request.headers.accept"]=> string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" ["http.request.headers.cf-ray"]=> string(5) "cfray" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.forwarded-for"]=> + string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" ["http.request.headers.user-agent"]=> string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" ["http.request.headers.x-appgw-trace-id"]=> string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" ["http.request.headers.x-sigsci-requestid"]=> string(15) "sigscirequestid" ["http.request.headers.x-sigsci-tags"]=> diff --git a/appsec/tests/extension/headers_collection_11.phpt b/appsec/tests/extension/headers_collection_11.phpt index 72199d86b5..c2c9104fd5 100644 --- a/appsec/tests/extension/headers_collection_11.phpt +++ b/appsec/tests/extension/headers_collection_11.phpt @@ -1,12 +1,12 @@ --TEST-- -Basic headers are collected when track_user_login_failure_event is triggered by automation and extended mode is not set +All headers are collected when track_user_login_failure_event is triggered by automation and anonymization mode is set --INI-- extension=ddtrace.so datadog.appsec.log_file=/tmp/php_appsec_test.log datadog.appsec.log_level=debug datadog.appsec.enabled=1 --ENV-- -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon HTTP_X_FORWARDED_FOR=7.7.7.7 DD_TRACE_CLIENT_IP_HEADER_DISABLED=true HTTP_X_FORWARDED_FOR=7.7.7.6,10.0.0.1 @@ -80,25 +80,53 @@ var_dump($headers); $helper->finished_with_commands(); ?> --EXPECTF-- -array(11) { +array(25) { ["http.request.headers.accept"]=> string(3) "*/*" + ["http.request.headers.accept-encoding"]=> + string(4) "gzip" + ["http.request.headers.accept-language"]=> + string(5) "pt-PT" ["http.request.headers.akamai-user-risk"]=> string(13) "akamaiuserisk" ["http.request.headers.cf-ray"]=> string(5) "cfray" ["http.request.headers.cloudfront-viewer-ja3-fingerprint"]=> string(16) "cloudfrontviewer" + ["http.request.headers.content-encoding"]=> + string(5) "utf-8" + ["http.request.headers.content-length"]=> + string(1) "0" ["http.request.headers.content-type"]=> string(10) "text/plain" + ["http.request.headers.forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.forwarded-for"]=> + string(17) "7.7.7.10,10.0.0.1" + ["http.request.headers.host"]=> + string(11) "myhost:8888" + ["http.request.headers.true-client-ip"]=> + string(8) "7.7.7.11" ["http.request.headers.user-agent"]=> string(13) "my user agent" + ["http.request.headers.via"]=> + string(12) "HTTP/1.1 GWA" ["http.request.headers.x-amzn-trace-id"]=> string(13) "amazontraceid" ["http.request.headers.x-appgw-trace-id"]=> string(12) "appgvtraceid" + ["http.request.headers.x-client-ip"]=> + string(7) "7.7.7.7" ["http.request.headers.x-cloud-trace-context"]=> string(17) "cloudtracecontext" + ["http.request.headers.x-cluster-client-ip"]=> + string(7) "7.7.7.9" + ["http.request.headers.x-forwarded"]=> + string(9) "for="foo"" + ["http.request.headers.x-forwarded-for"]=> + string(16) "7.7.7.6,10.0.0.1" + ["http.request.headers.x-real-ip"]=> + string(7) "7.7.7.8" ["http.request.headers.x-sigsci-requestid"]=> string(15) "sigscirequestid" ["http.request.headers.x-sigsci-tags"]=> diff --git a/appsec/tests/extension/tracking_functions_disabled_01.phpt b/appsec/tests/extension/track_custom_event_functions_appsec_disabled.phpt similarity index 100% rename from appsec/tests/extension/tracking_functions_disabled_01.phpt rename to appsec/tests/extension/track_custom_event_functions_appsec_disabled.phpt diff --git a/appsec/tests/extension/track_user_login_failure_event.phpt b/appsec/tests/extension/track_user_login_failure_event.phpt index 32a8f11502..fef52ad536 100644 --- a/appsec/tests/extension/track_user_login_failure_event.phpt +++ b/appsec/tests/extension/track_user_login_failure_event.phpt @@ -37,10 +37,10 @@ Array [appsec.events.users.login.failure.usr.id] => Admin [appsec.events.users.login.failure.track] => true [_dd.appsec.events.users.login.failure.sdk] => true - [appsec.events.users.login.failure.usr.exists] => false [appsec.events.users.login.failure.value] => something [appsec.events.users.login.failure.metadata] => some other metadata [appsec.events.users.login.failure.email] => noneofyour@business.com + [appsec.events.users.login.failure.usr.exists] => false [_dd.runtime_family] => php [_dd.p.dm] => -4 ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode.phpt new file mode 100644 index 0000000000..8a5cd6ea96 --- /dev/null +++ b/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode.phpt @@ -0,0 +1,38 @@ +--TEST-- +Track automated user login failure with anonymization mode and verify the tags in the root span +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_ENABLED=1 +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon +--FILE-- + "something", + "metadata" => "some other metadata", + "email" => "noneofyour@business.com" + ] + , true +); + +echo "root_span_get_meta():\n"; +print_r(root_span_get_meta()); +?> +--EXPECTF-- +root_span_get_meta(): +Array +( + [runtime-id] => %s + [appsec.events.users.login.failure.usr.id] => anon_03ac674216f3e15c761ee1a5e255f067 + [appsec.events.users.login.failure.track] => true + [_dd.appsec.events.users.login.failure.auto.mode] => anonymization + [appsec.events.users.login.failure.usr.exists] => true +) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_06.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode_compat.phpt similarity index 70% rename from appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_06.phpt rename to appsec/tests/extension/track_user_login_failure_event_automated_anon_mode_compat.phpt index c3153a3f0b..bdc990bd65 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_06.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode_compat.phpt @@ -1,5 +1,5 @@ --TEST-- -Metadata is discarded on automated safe mode +Track automated user login failure with anonymization mode, configured through the deprecated variable --INI-- extension=ddtrace.so --ENV-- @@ -13,8 +13,7 @@ include __DIR__ . '/inc/ddtrace_version.php'; ddtrace_version_at_least('0.79.0'); -track_user_login_failure_event( - "8d701714-5b26-4113-a8bf-ea7a681bcc3e", +track_user_login_failure_event("1234", true, [ "value" => "something", @@ -32,8 +31,8 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [appsec.events.users.login.failure.usr.id] => 8d701714-5b26-4113-a8bf-ea7a681bcc3e + [appsec.events.users.login.failure.usr.id] => anon_03ac674216f3e15c761ee1a5e255f067 [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [_dd.appsec.events.users.login.failure.auto.mode] => anonymization [appsec.events.users.login.failure.usr.exists] => true ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode_full_name.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode_full_name.phpt new file mode 100644 index 0000000000..3996d5bbbd --- /dev/null +++ b/appsec/tests/extension/track_user_login_failure_event_automated_anon_mode_full_name.phpt @@ -0,0 +1,38 @@ +--TEST-- +Track automated user login failure with anonymization mode, using the full name as configuration +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_ENABLED=1 +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anonymization +--FILE-- + "something", + "metadata" => "some other metadata", + "email" => "noneofyour@business.com" + ] + , true +); + +echo "root_span_get_meta():\n"; +print_r(root_span_get_meta()); +?> +--EXPECTF-- +root_span_get_meta(): +Array +( + [runtime-id] => %s + [appsec.events.users.login.failure.usr.id] => anon_03ac674216f3e15c761ee1a5e255f067 + [appsec.events.users.login.failure.track] => true + [_dd.appsec.events.users.login.failure.auto.mode] => anonymization + [appsec.events.users.login.failure.usr.exists] => true +) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_default_mode.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_default_mode.phpt index 7cb2a8bf88..532242aecb 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_default_mode.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_default_mode.phpt @@ -1,5 +1,5 @@ --TEST-- -Metadata is discarded on automated safe mode +Metadata is kept on automated (default) identification mode --INI-- extension=ddtrace.so --ENV-- @@ -24,6 +24,6 @@ Array [runtime-id] => %s [appsec.events.users.login.failure.usr.id] => 1234 [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_02.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_disabled_config.phpt similarity index 55% rename from appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_02.phpt rename to appsec/tests/extension/track_user_login_failure_event_automated_disabled_config.phpt index 291741a1ea..f7c8285ee1 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_02.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_disabled_config.phpt @@ -1,10 +1,11 @@ --TEST-- -Safe mode does not allow sensitive ids +Ensure automated user login failure is disabled through configuration --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident +DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED=0 --FILE-- "something", + "metadata" => "some other metadata", + "email" => "noneofyour@business.com" +], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -23,7 +29,4 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe - [appsec.events.users.login.failure.usr.exists] => true ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_disabled_mode.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_disabled_mode.phpt index ba15a9438c..6e55520b0c 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_disabled_mode.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_disabled_mode.phpt @@ -1,10 +1,10 @@ --TEST-- -Track automated user login failure with disabled mode and verify there is no tags +Track automated user login failure with disabled mode and verify there are no tags --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=disabled +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=disabled --FILE-- 'some@email.com'], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -25,6 +25,6 @@ Array [runtime-id] => %s [appsec.events.users.login.failure.usr.id] => 1234 [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_extended_mode_02.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_02.phpt similarity index 74% rename from appsec/tests/extension/track_user_login_failure_event_automated_extended_mode_02.phpt rename to appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_02.phpt index 2e805fbef6..780034a358 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_extended_mode_02.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_02.phpt @@ -1,10 +1,10 @@ --TEST-- -Verify on extended mode sensitive ids are not discarded +Verify on identification mode sensitive ids are not discarded --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident --FILE-- %s [appsec.events.users.login.failure.usr.id] => sensitiveId [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => extended + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true - [appsec.events.users.login.failure.email] => some@email.com ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_extended_mode.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_compat.phpt similarity index 75% rename from appsec/tests/extension/track_user_login_failure_event_automated_extended_mode.phpt rename to appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_compat.phpt index a7cfbabfc3..df397c733c 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_extended_mode.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_compat.phpt @@ -1,5 +1,5 @@ --TEST-- -Track automated user login failure with extended mode mode event and verify the tags in the root span +Track automated user login failure with identification mode, configured through the deprecated variable --INI-- extension=ddtrace.so --ENV-- @@ -25,7 +25,6 @@ Array [runtime-id] => %s [appsec.events.users.login.failure.usr.id] => 1234 [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => extended + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true - [appsec.events.users.login.failure.email] => some@email.com ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_full_name.phpt similarity index 64% rename from appsec/tests/extension/track_user_login_failure_event_automated_safe_mode.phpt rename to appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_full_name.phpt index ff51da2c51..2796a3e5d9 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_ident_mode_full_name.phpt @@ -1,10 +1,10 @@ --TEST-- -Track automated user login failure with safe mode mode event and verify the tags in the root span +Track automated user login failure event with identification mode, using the full name as configuration --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=identification --FILE-- 'some@email.com'], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -25,6 +25,6 @@ Array [runtime-id] => %s [appsec.events.users.login.failure.usr.id] => 1234 [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_invalid_mode.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_invalid_mode.phpt index f83ae2d536..98af06f8bc 100644 --- a/appsec/tests/extension/track_user_login_failure_event_automated_invalid_mode.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_automated_invalid_mode.phpt @@ -1,10 +1,10 @@ --TEST-- -Track automated user login failure with invalid mode mode event and verify the tags in the root span +Validate that when the mode is set to an invalid value, collection is disabled --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=invalid +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=invalid --FILE-- %s - [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe - [appsec.events.users.login.failure.usr.exists] => true ) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_04.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_04.phpt deleted file mode 100644 index 25c8adec2d..0000000000 --- a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_04.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -Safe mode allows uuid v1 ---INI-- -extension=ddtrace.so ---ENV-- -DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe ---FILE-- - ---EXPECTF-- -root_span_get_meta(): -Array -( - [runtime-id] => %s - [appsec.events.users.login.failure.usr.id] => 85e37758-0b85-11ee-be56-0242ac120002 - [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe - [appsec.events.users.login.failure.usr.exists] => true -) diff --git a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_05.phpt b/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_05.phpt deleted file mode 100644 index 039e29c6d6..0000000000 --- a/appsec/tests/extension/track_user_login_failure_event_automated_safe_mode_05.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -Safe mode allows uuid v4 ---INI-- -extension=ddtrace.so ---ENV-- -DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe ---FILE-- - ---EXPECTF-- -root_span_get_meta(): -Array -( - [runtime-id] => %s - [appsec.events.users.login.failure.usr.id] => 8d701714-5b26-4113-a8bf-ea7a681bcc3e - [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe - [appsec.events.users.login.failure.usr.exists] => true -) diff --git a/appsec/tests/extension/track_user_login_failure_event_existing_user.phpt b/appsec/tests/extension/track_user_login_failure_event_existing_user.phpt index 188a712be3..60fbe15c89 100644 --- a/appsec/tests/extension/track_user_login_failure_event_existing_user.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_existing_user.phpt @@ -30,8 +30,8 @@ Array [appsec.events.users.login.failure.usr.id] => Admin [appsec.events.users.login.failure.track] => true [_dd.appsec.events.users.login.failure.sdk] => true - [appsec.events.users.login.failure.usr.exists] => true [appsec.events.users.login.failure.value] => something [appsec.events.users.login.failure.metadata] => some other metadata [appsec.events.users.login.failure.email] => noneofyour@business.com + [appsec.events.users.login.failure.usr.exists] => true ) diff --git a/appsec/tests/extension/track_user_login_failure_event_no_user.phpt b/appsec/tests/extension/track_user_login_failure_event_no_user.phpt index 5116a252ba..ec10309588 100644 --- a/appsec/tests/extension/track_user_login_failure_event_no_user.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_no_user.phpt @@ -23,6 +23,6 @@ Array ( [runtime-id] => %s [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => false ) diff --git a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority.phpt b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority.phpt index d77e3ac7d7..e5de6e72f3 100644 --- a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority.phpt @@ -26,7 +26,7 @@ Array [appsec.events.users.login.failure.usr.id] => Admin [appsec.events.users.login.failure.track] => true [_dd.appsec.events.users.login.failure.sdk] => true - [appsec.events.users.login.failure.usr.exists] => true [appsec.events.users.login.failure.value] => something-from-sdk - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [appsec.events.users.login.failure.usr.exists] => true + [_dd.appsec.events.users.login.failure.auto.mode] => identification ) diff --git a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_02.phpt b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_02.phpt index 745edbdd4a..e0ed50ca3c 100644 --- a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_02.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_02.phpt @@ -25,7 +25,7 @@ Array [runtime-id] => %s [appsec.events.users.login.failure.usr.id] => Admin [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true [_dd.appsec.events.users.login.failure.sdk] => true [appsec.events.users.login.failure.value] => something-from-sdk diff --git a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_03.phpt b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_03.phpt index 5c5f6f7de5..7fc0ecbf69 100644 --- a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_03.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_03.phpt @@ -27,7 +27,7 @@ Array [runtime-id] => %s [appsec.events.users.login.failure.usr.id] => Other [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => safe + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true [_dd.appsec.events.users.login.failure.sdk] => true [appsec.events.users.login.failure.value] => something-from-sdk-2 diff --git a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_04.phpt b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_04.phpt index 28a9531a25..bdef3bf6a1 100644 --- a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_04.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_04.phpt @@ -1,10 +1,10 @@ --TEST-- -When values are set with automated event and with sdk, SDK takes priority on extended mode +When values are set with automated event and with sdk, SDK takes priority on identification mode --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident --FILE-- Admin [appsec.events.users.login.failure.track] => true [_dd.appsec.events.users.login.failure.sdk] => true - [appsec.events.users.login.failure.usr.exists] => true [appsec.events.users.login.failure.value] => something-from-sdk - [_dd.appsec.events.users.login.failure.auto.mode] => extended + [appsec.events.users.login.failure.usr.exists] => true + [_dd.appsec.events.users.login.failure.auto.mode] => identification ) diff --git a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_05.phpt b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_05.phpt index ca7904599b..bb5ca10fe7 100644 --- a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_05.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_05.phpt @@ -1,10 +1,10 @@ --TEST-- -When values are set with automated event and with sdk, SDK takes priority on extended mode +When values are set with automated event and with sdk, SDK takes priority on identification mode --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident --FILE-- %s [appsec.events.users.login.failure.usr.id] => Admin [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => extended + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true - [appsec.events.users.login.failure.value] => something-from-sdk [_dd.appsec.events.users.login.failure.sdk] => true + [appsec.events.users.login.failure.value] => something-from-sdk ) diff --git a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_06.phpt b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_06.phpt index a81bd57b8f..05a68b8fb5 100644 --- a/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_06.phpt +++ b/appsec/tests/extension/track_user_login_failure_event_sdk_takes_priority_06.phpt @@ -1,10 +1,10 @@ --TEST-- -Latest sdk values take priority on extended mode +Latest sdk values take priority on identification mode --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident --FILE-- %s [appsec.events.users.login.failure.usr.id] => Other [appsec.events.users.login.failure.track] => true - [_dd.appsec.events.users.login.failure.auto.mode] => extended + [_dd.appsec.events.users.login.failure.auto.mode] => identification [appsec.events.users.login.failure.usr.exists] => true - [appsec.events.users.login.failure.value] => something-from-sdk-2 [_dd.appsec.events.users.login.failure.sdk] => true + [appsec.events.users.login.failure.value] => something-from-sdk-2 ) diff --git a/appsec/tests/extension/track_user_login_success_event.phpt b/appsec/tests/extension/track_user_login_success_event.phpt index b256a20be5..ae3c5b2914 100644 --- a/appsec/tests/extension/track_user_login_success_event.phpt +++ b/appsec/tests/extension/track_user_login_success_event.phpt @@ -36,10 +36,10 @@ Array [runtime-id] => %s [usr.id] => Admin [_dd.appsec.events.users.login.success.sdk] => true - [appsec.events.users.login.success.track] => true [appsec.events.users.login.success.value] => something [appsec.events.users.login.success.metadata] => some other metadata [appsec.events.users.login.success.email] => noneofyour@business.com + [appsec.events.users.login.success.track] => true [_dd.runtime_family] => php [_dd.p.dm] => -4 ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_02.phpt b/appsec/tests/extension/track_user_login_success_event_automated_anon_mode.phpt similarity index 57% rename from appsec/tests/extension/track_user_login_success_event_automated_safe_mode_02.phpt rename to appsec/tests/extension/track_user_login_success_event_automated_anon_mode.phpt index 5b9f3abef1..e387d0e4c1 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_02.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_anon_mode.phpt @@ -1,10 +1,10 @@ --TEST-- -Safe mode does not allow sensitive ids +Track automated user login success event with anonymization mode and verify the tags in the root span --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon --FILE-- 'discarded'], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -23,6 +23,7 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [_dd.appsec.events.users.login.success.auto.mode] => safe + [usr.id] => anon_8c6976e5b5410415bde908bd4dee15df + [_dd.appsec.events.users.login.success.auto.mode] => anonymization [appsec.events.users.login.success.track] => true ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode.phpt b/appsec/tests/extension/track_user_login_success_event_automated_anon_mode_compat.phpt similarity index 62% rename from appsec/tests/extension/track_user_login_success_event_automated_safe_mode.phpt rename to appsec/tests/extension/track_user_login_success_event_automated_anon_mode_compat.phpt index e0961fc3d5..d340fdd843 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_anon_mode_compat.phpt @@ -1,5 +1,5 @@ --TEST-- -Track automated user login success event with safe mode and verify the tags in the root span +Track automated user login success event with anonymization mode, configured through the deprecated variable --INI-- extension=ddtrace.so --ENV-- @@ -13,7 +13,7 @@ include __DIR__ . '/inc/ddtrace_version.php'; ddtrace_version_at_least('0.79.0'); -track_user_login_success_event("1234", ['something' => 'discarded'], true); +track_user_login_success_event("admin", ['something' => 'discarded'], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -23,7 +23,7 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [usr.id] => 1234 - [_dd.appsec.events.users.login.success.auto.mode] => safe + [usr.id] => anon_8c6976e5b5410415bde908bd4dee15df + [_dd.appsec.events.users.login.success.auto.mode] => anonymization [appsec.events.users.login.success.track] => true ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_anon_mode_full_name.phpt b/appsec/tests/extension/track_user_login_success_event_automated_anon_mode_full_name.phpt new file mode 100644 index 0000000000..39ea6130c8 --- /dev/null +++ b/appsec/tests/extension/track_user_login_success_event_automated_anon_mode_full_name.phpt @@ -0,0 +1,29 @@ +--TEST-- +Track automated user login success with anonymization mode, using the full name as configuration +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_ENABLED=1 +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anonymization +--FILE-- + 'discarded'], true); + +echo "root_span_get_meta():\n"; +print_r(root_span_get_meta()); +?> +--EXPECTF-- +root_span_get_meta(): +Array +( + [runtime-id] => %s + [usr.id] => anon_8c6976e5b5410415bde908bd4dee15df + [_dd.appsec.events.users.login.success.auto.mode] => anonymization + [appsec.events.users.login.success.track] => true +) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_default_mode.phpt b/appsec/tests/extension/track_user_login_success_event_automated_default_mode.phpt index f92df7f877..1db2098598 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_default_mode.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_default_mode.phpt @@ -23,6 +23,6 @@ Array ( [runtime-id] => %s [usr.id] => 1234 - [_dd.appsec.events.users.login.success.auto.mode] => safe + [_dd.appsec.events.users.login.success.auto.mode] => identification [appsec.events.users.login.success.track] => true ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_06.phpt b/appsec/tests/extension/track_user_login_success_event_automated_disabled_config.phpt similarity index 68% rename from appsec/tests/extension/track_user_login_success_event_automated_safe_mode_06.phpt rename to appsec/tests/extension/track_user_login_success_event_automated_disabled_config.phpt index 9a66e2cd79..236952150c 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_06.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_disabled_config.phpt @@ -1,10 +1,11 @@ --TEST-- -Metadata is discarded in +Ensure automated user login success is disabled through configuration --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident +DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED=0 --FILE-- "something", "metadata" => "some other metadata", @@ -29,7 +30,4 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [usr.id] => 1234 - [_dd.appsec.events.users.login.success.auto.mode] => safe - [appsec.events.users.login.success.track] => true ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_disabled_mode.phpt b/appsec/tests/extension/track_user_login_success_event_automated_disabled_mode.phpt index c57e6d2c9e..a75e1bd8c9 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_disabled_mode.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_disabled_mode.phpt @@ -4,7 +4,7 @@ Track automated user login success event with disabled mode and verify there is extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=disabled +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=disabled --FILE-- 'some@email.com'], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -24,6 +24,6 @@ Array ( [runtime-id] => %s [usr.id] => 1234 - [_dd.appsec.events.users.login.success.auto.mode] => safe + [_dd.appsec.events.users.login.success.auto.mode] => identification [appsec.events.users.login.success.track] => true ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_extended_mode_02.phpt b/appsec/tests/extension/track_user_login_success_event_automated_ident_mode_02.phpt similarity index 71% rename from appsec/tests/extension/track_user_login_success_event_automated_extended_mode_02.phpt rename to appsec/tests/extension/track_user_login_success_event_automated_ident_mode_02.phpt index dfac4d3913..f9f2b469a3 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_extended_mode_02.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_ident_mode_02.phpt @@ -1,10 +1,10 @@ --TEST-- -Verify on extended mode sensitive ids are not discarded +Verify on identification mode sensitive ids are not discarded --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=extended +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident --FILE-- %s [usr.id] => sensitiveId - [_dd.appsec.events.users.login.success.auto.mode] => extended + [_dd.appsec.events.users.login.success.auto.mode] => identification [appsec.events.users.login.success.track] => true - [appsec.events.users.login.success.email] => some@email.com ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_extended_mode.phpt b/appsec/tests/extension/track_user_login_success_event_automated_ident_mode_compat.phpt similarity index 73% rename from appsec/tests/extension/track_user_login_success_event_automated_extended_mode.phpt rename to appsec/tests/extension/track_user_login_success_event_automated_ident_mode_compat.phpt index 847943f906..88874a4d58 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_extended_mode.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_ident_mode_compat.phpt @@ -1,5 +1,5 @@ --TEST-- -Track automated user login success event with safe mode and verify the tags in the root span +Track automated user login success event with identification mode, configured through the deprecated variable --INI-- extension=ddtrace.so --ENV-- @@ -24,7 +24,6 @@ Array ( [runtime-id] => %s [usr.id] => 1234 - [_dd.appsec.events.users.login.success.auto.mode] => extended + [_dd.appsec.events.users.login.success.auto.mode] => identification [appsec.events.users.login.success.track] => true - [appsec.events.users.login.success.email] => some@email.com ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_ident_mode_full_name.phpt b/appsec/tests/extension/track_user_login_success_event_automated_ident_mode_full_name.phpt new file mode 100644 index 0000000000..156c884711 --- /dev/null +++ b/appsec/tests/extension/track_user_login_success_event_automated_ident_mode_full_name.phpt @@ -0,0 +1,29 @@ +--TEST-- +Track automated user login success event with identification mode, using the full name as configuration +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_ENABLED=1 +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=identification +--FILE-- + 'some@email.com'], true); + +echo "root_span_get_meta():\n"; +print_r(root_span_get_meta()); +?> +--EXPECTF-- +root_span_get_meta(): +Array +( + [runtime-id] => %s + [usr.id] => 1234 + [_dd.appsec.events.users.login.success.auto.mode] => identification + [appsec.events.users.login.success.track] => true +) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_invalid_mode.phpt b/appsec/tests/extension/track_user_login_success_event_automated_invalid_mode.phpt index 31bbaee3cb..e9972158e7 100644 --- a/appsec/tests/extension/track_user_login_success_event_automated_invalid_mode.phpt +++ b/appsec/tests/extension/track_user_login_success_event_automated_invalid_mode.phpt @@ -1,10 +1,10 @@ --TEST-- -Track automated user login success event with safe mode and verify the tags in the root span +Track automated user login success event, validate that an invalid mode disables collection --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=invalid +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=invalid --FILE-- %s - [_dd.appsec.events.users.login.success.auto.mode] => safe - [appsec.events.users.login.success.track] => true ) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_04.phpt b/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_04.phpt deleted file mode 100644 index 76a61f26a1..0000000000 --- a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_04.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -Safe mode allows uuid v1 ---INI-- -extension=ddtrace.so ---ENV-- -DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe ---FILE-- - ---EXPECTF-- -root_span_get_meta(): -Array -( - [runtime-id] => %s - [usr.id] => 85e37758-0b85-11ee-be56-0242ac120002 - [_dd.appsec.events.users.login.success.auto.mode] => safe - [appsec.events.users.login.success.track] => true -) diff --git a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_05.phpt b/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_05.phpt deleted file mode 100644 index 1bc9765c6c..0000000000 --- a/appsec/tests/extension/track_user_login_success_event_automated_safe_mode_05.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -Safe mode allows uuid v4 ---INI-- -extension=ddtrace.so ---ENV-- -DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe ---FILE-- - ---EXPECTF-- -root_span_get_meta(): -Array -( - [runtime-id] => %s - [usr.id] => 8d701714-5b26-4113-a8bf-ea7a681bcc3e - [_dd.appsec.events.users.login.success.auto.mode] => safe - [appsec.events.users.login.success.track] => true -) diff --git a/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority.phpt b/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority.phpt index 2924541003..f1c2faed96 100644 --- a/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority.phpt +++ b/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority.phpt @@ -25,7 +25,7 @@ Array [runtime-id] => %s [usr.id] => Admin [_dd.appsec.events.users.login.success.sdk] => true - [appsec.events.users.login.success.track] => true [appsec.events.users.login.success.value] => something-from-sdk - [_dd.appsec.events.users.login.success.auto.mode] => safe + [appsec.events.users.login.success.track] => true + [_dd.appsec.events.users.login.success.auto.mode] => identification ) diff --git a/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_02.phpt b/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_02.phpt index be38b41e8c..d21274d619 100644 --- a/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_02.phpt +++ b/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_02.phpt @@ -24,7 +24,7 @@ Array ( [runtime-id] => %s [usr.id] => Admin - [_dd.appsec.events.users.login.success.auto.mode] => safe + [_dd.appsec.events.users.login.success.auto.mode] => identification [appsec.events.users.login.success.track] => true [_dd.appsec.events.users.login.success.sdk] => true [appsec.events.users.login.success.value] => something-from-sdk diff --git a/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_03.phpt b/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_03.phpt index 94d395aafd..530f2ac8e4 100644 --- a/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_03.phpt +++ b/appsec/tests/extension/track_user_login_success_event_sdk_takes_priority_03.phpt @@ -26,7 +26,7 @@ Array ( [runtime-id] => %s [usr.id] => OtherUser - [_dd.appsec.events.users.login.success.auto.mode] => safe + [_dd.appsec.events.users.login.success.auto.mode] => identification [appsec.events.users.login.success.track] => true [_dd.appsec.events.users.login.success.sdk] => true [appsec.events.users.login.success.value] => something-from-sdk-2 diff --git a/appsec/tests/extension/track_user_signup_event_automated_safe_mode.phpt b/appsec/tests/extension/track_user_signup_event_automated_anon_mode.phpt similarity index 52% rename from appsec/tests/extension/track_user_signup_event_automated_safe_mode.phpt rename to appsec/tests/extension/track_user_signup_event_automated_anon_mode.phpt index f0afe5e569..0d7c26fb3a 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_safe_mode.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_anon_mode.phpt @@ -1,10 +1,10 @@ --TEST-- -Track automated user sign up event with safe mode and verify the tags in the root span +Track automated user sign up event with anonymization mode and verify the tags in the root span --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon --FILE-- 'discarded'], true); +track_user_signup_event("1234", +[ + "value" => "something", + "metadata" => "some other metadata", + "email" => "noneofyour@business.com" +], +true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -23,7 +29,7 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [usr.id] => 1234 - [_dd.appsec.events.users.signup.auto.mode] => safe + [usr.id] => anon_03ac674216f3e15c761ee1a5e255f067 + [_dd.appsec.events.users.signup.auto.mode] => anonymization [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_06.phpt b/appsec/tests/extension/track_user_signup_event_automated_anon_mode_compat.phpt similarity index 74% rename from appsec/tests/extension/track_user_signup_event_automated_safe_mode_06.phpt rename to appsec/tests/extension/track_user_signup_event_automated_anon_mode_compat.phpt index cb1d9f37a4..1319f23d53 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_06.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_anon_mode_compat.phpt @@ -1,5 +1,5 @@ --TEST-- -Metadata is discarded in +Track automated user sign up event with anonymization mode, configured through the deprecated variable --INI-- extension=ddtrace.so --ENV-- @@ -29,7 +29,7 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [usr.id] => 1234 - [_dd.appsec.events.users.signup.auto.mode] => safe + [usr.id] => anon_03ac674216f3e15c761ee1a5e255f067 + [_dd.appsec.events.users.signup.auto.mode] => anonymization [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_anon_mode_full_name.phpt b/appsec/tests/extension/track_user_signup_event_automated_anon_mode_full_name.phpt new file mode 100644 index 0000000000..76fd4a6a55 --- /dev/null +++ b/appsec/tests/extension/track_user_signup_event_automated_anon_mode_full_name.phpt @@ -0,0 +1,35 @@ +--TEST-- +Track automated user signup with anonymization mode, using the full name as configuration +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_ENABLED=1 +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anonymization +--FILE-- + "something", + "metadata" => "some other metadata", + "email" => "noneofyour@business.com" +], +true); + +echo "root_span_get_meta():\n"; +print_r(root_span_get_meta()); +?> +--EXPECTF-- +root_span_get_meta(): +Array +( + [runtime-id] => %s + [usr.id] => anon_03ac674216f3e15c761ee1a5e255f067 + [_dd.appsec.events.users.signup.auto.mode] => anonymization + [appsec.events.users.signup.track] => true +) diff --git a/appsec/tests/extension/track_user_signup_event_automated_default_mode.phpt b/appsec/tests/extension/track_user_signup_event_automated_default_mode.phpt index 4dd9d939d5..bb544de19c 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_default_mode.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_default_mode.phpt @@ -23,6 +23,6 @@ Array ( [runtime-id] => %s [usr.id] => 1234 - [_dd.appsec.events.users.signup.auto.mode] => safe + [_dd.appsec.events.users.signup.auto.mode] => identification [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_04.phpt b/appsec/tests/extension/track_user_signup_event_automated_disabled_config.phpt similarity index 56% rename from appsec/tests/extension/track_user_signup_event_automated_safe_mode_04.phpt rename to appsec/tests/extension/track_user_signup_event_automated_disabled_config.phpt index 0233cf2e50..9e8ce5a750 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_04.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_disabled_config.phpt @@ -1,10 +1,11 @@ --TEST-- -Safe mode allows uuid v1 +Ensure automated user signup is disabled through configuration --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident +DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED=0 --FILE-- "something", + "metadata" => "some other metadata", + "email" => "noneofyour@business.com" +], +true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -23,7 +30,4 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [usr.id] => 85e37758-0b85-11ee-be56-0242ac120002 - [_dd.appsec.events.users.signup.auto.mode] => safe - [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_disabled_mode.phpt b/appsec/tests/extension/track_user_signup_event_automated_disabled_mode.phpt index 87893cc30c..915b295a1d 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_disabled_mode.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_disabled_mode.phpt @@ -4,7 +4,7 @@ Track automated user login success event with disabled mode and verify there is extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=disabled +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=disabled --FILE-- 'some@email.com'], true); - -echo "root_span_get_meta():\n"; -print_r(root_span_get_meta()); -?> ---EXPECTF-- -root_span_get_meta(): -Array -( - [runtime-id] => %s - [usr.id] => 1234 - [_dd.appsec.events.users.signup.auto.mode] => extended - [appsec.events.users.signup.email] => some@email.com - [appsec.events.users.signup.track] => true -) diff --git a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_03.phpt b/appsec/tests/extension/track_user_signup_event_automated_ident_mode.phpt similarity index 59% rename from appsec/tests/extension/track_user_signup_event_automated_safe_mode_03.phpt rename to appsec/tests/extension/track_user_signup_event_automated_ident_mode.phpt index 5a5cc7957c..17e79fe0c6 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_03.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_ident_mode.phpt @@ -1,10 +1,10 @@ --TEST-- -Safe mode allows numeric ids +Track automated user sign up event with identification mode and verify the tags in the root span --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=ident --FILE-- 'some@email.com'], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -23,7 +23,7 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [usr.id] => 1234 - [_dd.appsec.events.users.signup.auto.mode] => safe + [usr.id] => sensitiveId + [_dd.appsec.events.users.signup.auto.mode] => identification [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_extended_mode_02.phpt b/appsec/tests/extension/track_user_signup_event_automated_ident_mode_compat.phpt similarity index 78% rename from appsec/tests/extension/track_user_signup_event_automated_extended_mode_02.phpt rename to appsec/tests/extension/track_user_signup_event_automated_ident_mode_compat.phpt index 0ca11f26a0..ef68b6c59c 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_extended_mode_02.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_ident_mode_compat.phpt @@ -1,5 +1,5 @@ --TEST-- -Verify on extended mode sensitive ids are not discarded +Track automated user sign up event with identification mode, configured through the deprecated variable --INI-- extension=ddtrace.so --ENV-- @@ -24,7 +24,6 @@ Array ( [runtime-id] => %s [usr.id] => sensitiveId - [_dd.appsec.events.users.signup.auto.mode] => extended - [appsec.events.users.signup.email] => some@email.com + [_dd.appsec.events.users.signup.auto.mode] => identification [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_02.phpt b/appsec/tests/extension/track_user_signup_event_automated_ident_mode_full_name.phpt similarity index 58% rename from appsec/tests/extension/track_user_signup_event_automated_safe_mode_02.phpt rename to appsec/tests/extension/track_user_signup_event_automated_ident_mode_full_name.phpt index 244ebe868e..9511cd5937 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_02.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_ident_mode_full_name.phpt @@ -1,10 +1,10 @@ --TEST-- -Safe mode does not allow sensitive ids +Track automated user signup with identification mode, using the full name as configuration --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=identification --FILE-- 'some@email.com'], true); echo "root_span_get_meta():\n"; print_r(root_span_get_meta()); @@ -23,6 +23,7 @@ root_span_get_meta(): Array ( [runtime-id] => %s - [_dd.appsec.events.users.signup.auto.mode] => safe + [usr.id] => sensitiveId + [_dd.appsec.events.users.signup.auto.mode] => identification [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_invalid_mode.phpt b/appsec/tests/extension/track_user_signup_event_automated_invalid_mode.phpt index 9cabe5f6b7..1dc84ac48b 100644 --- a/appsec/tests/extension/track_user_signup_event_automated_invalid_mode.phpt +++ b/appsec/tests/extension/track_user_signup_event_automated_invalid_mode.phpt @@ -1,10 +1,10 @@ --TEST-- -Track automated user signup event with safe mode and verify the tags in the root span +Track automated user signup event, verify that an invalid mode disables collection --INI-- extension=ddtrace.so --ENV-- DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=invalid +DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=invalid --FILE-- %s - [_dd.appsec.events.users.signup.auto.mode] => safe - [appsec.events.users.signup.track] => true ) diff --git a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_05.phpt b/appsec/tests/extension/track_user_signup_event_automated_safe_mode_05.phpt deleted file mode 100644 index 462b6a71d3..0000000000 --- a/appsec/tests/extension/track_user_signup_event_automated_safe_mode_05.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -Safe mode allows uuid v4 ---INI-- -extension=ddtrace.so ---ENV-- -DD_APPSEC_ENABLED=1 -DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING=safe ---FILE-- - ---EXPECTF-- -root_span_get_meta(): -Array -( - [runtime-id] => %s - [usr.id] => 8d701714-5b26-4113-a8bf-ea7a681bcc3e - [_dd.appsec.events.users.signup.auto.mode] => safe - [appsec.events.users.signup.track] => true -) diff --git a/appsec/tests/extension/track_user_signup_event_sdk_takes_priority.phpt b/appsec/tests/extension/track_user_signup_event_sdk_takes_priority.phpt index 9ead34fb87..b80c3f8a75 100644 --- a/appsec/tests/extension/track_user_signup_event_sdk_takes_priority.phpt +++ b/appsec/tests/extension/track_user_signup_event_sdk_takes_priority.phpt @@ -27,5 +27,5 @@ Array [_dd.appsec.events.users.signup.sdk] => true [appsec.events.users.signup.value] => something-from-sdk [appsec.events.users.signup.track] => true - [_dd.appsec.events.users.signup.auto.mode] => safe + [_dd.appsec.events.users.signup.auto.mode] => identification ) diff --git a/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_02.phpt b/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_02.phpt index ed543ecfc1..edd7d8612c 100644 --- a/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_02.phpt +++ b/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_02.phpt @@ -24,7 +24,7 @@ Array ( [runtime-id] => %s [usr.id] => Admin - [_dd.appsec.events.users.signup.auto.mode] => safe + [_dd.appsec.events.users.signup.auto.mode] => identification [appsec.events.users.signup.track] => true [_dd.appsec.events.users.signup.sdk] => true [appsec.events.users.signup.value] => something-from-sdk diff --git a/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_03.phpt b/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_03.phpt index f2426c0c1e..e84a564ead 100644 --- a/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_03.phpt +++ b/appsec/tests/extension/track_user_signup_event_sdk_takes_priority_03.phpt @@ -26,7 +26,7 @@ Array ( [runtime-id] => %s [usr.id] => OtherUser - [_dd.appsec.events.users.signup.auto.mode] => safe + [_dd.appsec.events.users.signup.auto.mode] => identification [appsec.events.users.signup.track] => true [_dd.appsec.events.users.signup.sdk] => true [appsec.events.users.signup.value] => something-from-sdk-2 diff --git a/appsec/tests/extension/user_tracking_do_nothing_from_login_success.phpt b/appsec/tests/extension/user_tracking_do_nothing_from_login_success.phpt index c2a3f0f19d..02524c88bc 100644 --- a/appsec/tests/extension/user_tracking_do_nothing_from_login_success.phpt +++ b/appsec/tests/extension/user_tracking_do_nothing_from_login_success.phpt @@ -48,8 +48,8 @@ Array [runtime-id] => %s [usr.id] => Admin [_dd.appsec.events.users.login.success.sdk] => true - [appsec.events.users.login.success.track] => true [appsec.events.users.login.success.value] => something [appsec.events.users.login.success.metadata] => some other metadata [appsec.events.users.login.success.email] => noneofyour@business.com + [appsec.events.users.login.success.track] => true ) diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy index 381a0d793c..86001ab57c 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy @@ -50,7 +50,7 @@ class Laravel8xTests { Span span = trace.first() assert span.meta."appsec.events.users.login.failure.track" == "true" - assert span.meta."_dd.appsec.events.users.login.failure.auto.mode" == "safe" + assert span.meta."_dd.appsec.events.users.login.failure.auto.mode" == "identification" assert span.meta."appsec.events.users.login.failure.usr.exists" == "false" assert span.metrics._sampling_priority_v1 == 2.0d } @@ -66,7 +66,7 @@ class Laravel8xTests { //ciuser@example.com user id is 1 Span span = trace.first() assert span.meta."usr.id" == "1" - assert span.meta."_dd.appsec.events.users.login.success.auto.mode" == "safe" + assert span.meta."_dd.appsec.events.users.login.success.auto.mode" == "identification" assert span.meta."appsec.events.users.login.success.track" == "true" assert span.metrics._sampling_priority_v1 == 2.0d } @@ -81,7 +81,7 @@ class Laravel8xTests { Span span = trace.first() assert span.meta."usr.id" == "2" - assert span.meta."_dd.appsec.events.users.signup.auto.mode" == "safe" + assert span.meta."_dd.appsec.events.users.signup.auto.mode" == "identification" assert span.meta."appsec.events.users.signup.track" == "true" assert span.metrics._sampling_priority_v1 == 2.0d } diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy index 27f77c8c72..dd205114bb 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy @@ -53,7 +53,8 @@ class Symfony62Tests { assert resp.statusCode() == 302 } Span span = trace.first() - assert span.meta."_dd.appsec.events.users.login.success.auto.mode" == "safe" + assert span.meta."usr.id" != "" + assert span.meta."_dd.appsec.events.users.login.success.auto.mode" == "identification" assert span.meta."appsec.events.users.login.success.track" == "true" assert span.metrics._sampling_priority_v1 == 2.0d } @@ -69,7 +70,7 @@ class Symfony62Tests { } Span span = trace.first() assert span.meta."appsec.events.users.login.failure.track" == 'true' - assert span.meta."_dd.appsec.events.users.login.failure.auto.mode" == 'safe' + assert span.meta."_dd.appsec.events.users.login.failure.auto.mode" == 'identification' assert span.meta."appsec.events.users.login.failure.usr.exists" == 'false' assert span.metrics._sampling_priority_v1 == 2.0d } @@ -84,7 +85,8 @@ class Symfony62Tests { assert resp.statusCode() == 302 } Span span = trace.first() - assert span.meta."_dd.appsec.events.users.signup.auto.mode" == "safe" + assert span.meta."usr.id" != "" + assert span.meta."_dd.appsec.events.users.signup.auto.mode" == "identification" assert span.meta."appsec.events.users.signup.track" == "true" assert span.metrics._sampling_priority_v1 == 2.0d } From 4667080296ac418228af6007b69ce41846f20574 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 8 Aug 2024 12:51:51 +0200 Subject: [PATCH 020/103] Bump the version of the CI builds (#2785) This ensures we can compare capabilities against e.g. system tests, distinguishing old and new release, otherwise everything would be the same version for them. --- .circleci/continue_config.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 49dc22da70..5e53a0495a 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -376,7 +376,7 @@ commands: default: /bin/bash -eo pipefail steps: - run: - name: Append build id to version number + name: Append build id to version number and bump it shell: << parameters.shell >> command: | githash="${CIRCLE_SHA1?}" @@ -387,8 +387,20 @@ commands: if [[ "$CIRCLE_BRANCH" =~ "ddtrace-" ]] ; then echo "Release branch detected; not adding git sha1 to version number." else - echo -n "+$githash" >>VERSION - echo "Appended +$githash to version number." + version=$(cat VERSION) + # if we have e.g. a beta suffix, just strip it + if [[ $version == *-* ]]; then + version=${version%-*} + else + # otherwise increment minor version + parts=($(echo -n "$version" | tr '.' '\n')) + parts[1]=$((parts[1]+1)) + parts[2]=0 + version=$(export IFS=.; (echo "${parts[*]}")) + fi + version="$version+$githash" + echo -n "$version" > VERSION + echo "Set version number to $version." fi From 369c3fbc60597d0ab1035f8b1354407314c9c04a Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 13 Aug 2024 15:41:46 +0200 Subject: [PATCH 021/103] fix ini creation when `--extension-dir` is used (#2789) --- datadog-setup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog-setup.php b/datadog-setup.php index 8663b777f0..a10d60131c 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -1789,7 +1789,7 @@ function add_missing_ini_settings($iniFilePath, $settings, $replacements) // right extension setting is available. $settingRegex = '(' . preg_quote($setting['name']) . '\s?=\s?'; if ($setting['name'] === 'extension' || $setting['name'] == 'zend_extension') { - $settingRegex .= preg_quote($setting['default']); + $settingRegex .= ".*".preg_quote($setting['default']); } $settingRegex .= ')'; From e29a1195f8142101014948a25a4082f82add4b84 Mon Sep 17 00:00:00 2001 From: Levi Morrison Date: Fri, 16 Aug 2024 04:42:19 -0600 Subject: [PATCH 022/103] Update Cargo.lock, pin cbindgen in CircleCI, and rerun cbindgen (#2795) * Update Cargo.lock and pin cbindgen in CircleCI The new cbindgen release requires Rust 1.74, we're on 1.71. * Run `make cbindgen` --- .circleci/continue_config.yml | 3 +- Cargo.lock | 152 +++++++++++++++++++++++++++++- components-rs/common.h | 168 ++++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+), 5 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 5e53a0495a..5323c18e94 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3867,7 +3867,8 @@ jobs: - git_checkout - run: name: Install cbindgen - command: cargo install cbindgen + # cbindgen 0.27 requires Rust 1.74+ + command: cargo install cbindgen@0.26.0 - run: name: Regenerate cbindgen headers and command: | diff --git a/Cargo.lock b/Cargo.lock index e2fbfd2c71..4acfc3da8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,6 +561,97 @@ dependencies = [ "piper", ] +[[package]] +name = "bolero" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212e8dca6d4001cc6cac941d6932ddaa8cd27f57e5e44a9da19c913eb6a43b33" +dependencies = [ + "bolero-afl", + "bolero-engine", + "bolero-generator", + "bolero-honggfuzz", + "bolero-kani", + "bolero-libfuzzer", + "cfg-if", + "rand 0.8.5", +] + +[[package]] +name = "bolero-afl" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b34f05de1527425bb05287da09ff1ff1612538648824db49e16d9693b24065" +dependencies = [ + "bolero-engine", + "cc", +] + +[[package]] +name = "bolero-engine" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6206263ebdd42e093c1229dab3957f61c9fd68d73c00f238ae25a378778b6bd3" +dependencies = [ + "anyhow", + "backtrace", + "bolero-generator", + "lazy_static", + "pretty-hex", + "rand 0.8.5", +] + +[[package]] +name = "bolero-generator" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac749fb4f2e14734e835a9352c0d1eb2ab62a025d4c56a823fa3f391e015741a" +dependencies = [ + "bolero-generator-derive", + "either", + "rand_core 0.6.4", +] + +[[package]] +name = "bolero-generator-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53397bfda19ccb48527faa14025048fc4bb76f090ccdeef1e5a355bfe4a94467" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bolero-honggfuzz" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf78581db1a7263620a8767e645b93ad287c70122ae76f5bd67040c7f06ff8e3" +dependencies = [ + "bolero-engine", +] + +[[package]] +name = "bolero-kani" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e55cec272a617f5ae4ce670db035108eb97c10cd4f67de851a3c8d3f18f19cb" +dependencies = [ + "bolero-engine", +] + +[[package]] +name = "bolero-libfuzzer" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb42f66ee3ec89b9c411994de59d4710ced19df96fea2059feea1c2d73904c5b" +dependencies = [ + "bolero-engine", + "cc", +] + [[package]] name = "bollard" version = "0.16.1" @@ -742,9 +833,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1027,6 +1118,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.19" @@ -1250,7 +1350,6 @@ dependencies = [ "httpmock", "hyper 0.14.28", "io-lifetimes", - "kernel32-sys", "lazy_static", "libc 0.2.154", "manual_future", @@ -1276,7 +1375,7 @@ dependencies = [ "tracing-log", "tracing-subscriber", "uuid", - "winapi 0.2.8", + "winapi 0.3.9", "windows 0.51.1", "zwohash", ] @@ -1415,7 +1514,10 @@ name = "ddcommon-ffi" version = "0.0.1" dependencies = [ "anyhow", + "bolero", "build_common", + "chrono", + "crossbeam-queue", "ddcommon 0.0.1", "hyper 0.14.28", ] @@ -3380,6 +3482,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -3420,6 +3528,16 @@ dependencies = [ "indexmap 1.9.3", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -4798,6 +4916,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.9.2" @@ -5476,6 +5611,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "x86" version = "0.47.0" diff --git a/components-rs/common.h b/components-rs/common.h index 1184bb3924..63f9792a63 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -104,6 +104,104 @@ typedef struct ddog_Option_Error { typedef struct ddog_Option_Error ddog_MaybeError; +typedef struct ddog_ArrayQueue { + struct ddog_ArrayQueue *inner; + void (*item_delete_fn)(void*); +} ddog_ArrayQueue; + +typedef enum ddog_ArrayQueue_NewResult_Tag { + DDOG_ARRAY_QUEUE_NEW_RESULT_OK, + DDOG_ARRAY_QUEUE_NEW_RESULT_ERR, +} ddog_ArrayQueue_NewResult_Tag; + +typedef struct ddog_ArrayQueue_NewResult { + ddog_ArrayQueue_NewResult_Tag tag; + union { + struct { + struct ddog_ArrayQueue *ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_ArrayQueue_NewResult; + +/** + * Data structure for the result of the push() and force_push() functions. + * force_push() replaces the oldest element if the queue is full, while push() returns the given + * value if the queue is full. For push(), it's redundant to return the value since the caller + * already has it, but it's returned for consistency with crossbeam API and with force_push(). + */ +typedef enum ddog_ArrayQueue_PushResult_Tag { + DDOG_ARRAY_QUEUE_PUSH_RESULT_OK, + DDOG_ARRAY_QUEUE_PUSH_RESULT_FULL, + DDOG_ARRAY_QUEUE_PUSH_RESULT_ERR, +} ddog_ArrayQueue_PushResult_Tag; + +typedef struct ddog_ArrayQueue_PushResult { + ddog_ArrayQueue_PushResult_Tag tag; + union { + struct { + void *full; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_ArrayQueue_PushResult; + +typedef enum ddog_ArrayQueue_PopResult_Tag { + DDOG_ARRAY_QUEUE_POP_RESULT_OK, + DDOG_ARRAY_QUEUE_POP_RESULT_EMPTY, + DDOG_ARRAY_QUEUE_POP_RESULT_ERR, +} ddog_ArrayQueue_PopResult_Tag; + +typedef struct ddog_ArrayQueue_PopResult { + ddog_ArrayQueue_PopResult_Tag tag; + union { + struct { + void *ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_ArrayQueue_PopResult; + +typedef enum ddog_ArrayQueue_BoolResult_Tag { + DDOG_ARRAY_QUEUE_BOOL_RESULT_OK, + DDOG_ARRAY_QUEUE_BOOL_RESULT_ERR, +} ddog_ArrayQueue_BoolResult_Tag; + +typedef struct ddog_ArrayQueue_BoolResult { + ddog_ArrayQueue_BoolResult_Tag tag; + union { + struct { + bool ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_ArrayQueue_BoolResult; + +typedef enum ddog_ArrayQueue_UsizeResult_Tag { + DDOG_ARRAY_QUEUE_USIZE_RESULT_OK, + DDOG_ARRAY_QUEUE_USIZE_RESULT_ERR, +} ddog_ArrayQueue_UsizeResult_Tag; + +typedef struct ddog_ArrayQueue_UsizeResult { + ddog_ArrayQueue_UsizeResult_Tag tag; + union { + struct { + uintptr_t ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_ArrayQueue_UsizeResult; + typedef enum ddog_Option_U32_Tag { DDOG_OPTION_U32_SOME_U32, DDOG_OPTION_U32_NONE_U32, @@ -333,6 +431,76 @@ ddog_CharSlice ddog_Error_message(const struct ddog_Error *error); void ddog_MaybeError_drop(ddog_MaybeError); +/** + * Creates a new ArrayQueue with the given capacity and item_delete_fn. + * The item_delete_fn is called when an item is dropped from the queue. + */ +DDOG_CHECK_RETURN +struct ddog_ArrayQueue_NewResult ddog_ArrayQueue_new(uintptr_t capacity, + void (*item_delete_fn)(void*)); + +/** + * Drops the ArrayQueue. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. + */ +void ddog_ArrayQueue_drop(struct ddog_ArrayQueue *queue); + +/** + * Pushes an item into the ArrayQueue. It returns the given value if the queue is full. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. The value + * is null or points to a valid memory location that can be deallocated by the item_delete_fn. + */ +struct ddog_ArrayQueue_PushResult ddog_ArrayQueue_push(const struct ddog_ArrayQueue *queue_ptr, + void *value); + +/** + * Pushes an element into the queue, replacing the oldest element if necessary. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. The value + * is null or points to a valid memory location that can be deallocated by the item_delete_fn. + */ +DDOG_CHECK_RETURN +struct ddog_ArrayQueue_PushResult ddog_ArrayQueue_force_push(const struct ddog_ArrayQueue *queue_ptr, + void *value); + +/** + * Pops an item from the ArrayQueue. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. + */ +DDOG_CHECK_RETURN +struct ddog_ArrayQueue_PopResult ddog_ArrayQueue_pop(const struct ddog_ArrayQueue *queue_ptr); + +/** + * Checks if the ArrayQueue is empty. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. + */ +struct ddog_ArrayQueue_BoolResult ddog_ArrayQueue_is_empty(const struct ddog_ArrayQueue *queue_ptr); + +/** + * Returns the length of the ArrayQueue. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. + */ +struct ddog_ArrayQueue_UsizeResult ddog_ArrayQueue_len(const struct ddog_ArrayQueue *queue_ptr); + +/** + * Returns true if the underlying queue is full. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. + */ +struct ddog_ArrayQueue_BoolResult ddog_ArrayQueue_is_full(const struct ddog_ArrayQueue *queue_ptr); + +/** + * Returns the capacity of the ArrayQueue. + * # Safety + * The pointer is null or points to a valid memory location allocated by ArrayQueue_new. + */ +struct ddog_ArrayQueue_UsizeResult ddog_ArrayQueue_capacity(const struct ddog_ArrayQueue *queue_ptr); + DDOG_CHECK_RETURN struct ddog_Endpoint *ddog_endpoint_from_url(ddog_CharSlice url); DDOG_CHECK_RETURN struct ddog_Endpoint *ddog_endpoint_from_filename(ddog_CharSlice filename); From d75a84de450c1e914552e88d6feb5c6782708107 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 16 Aug 2024 20:18:13 +0200 Subject: [PATCH 023/103] Fix fatal error when reading uninit $meta on a span (#2790) Signed-off-by: Bob Weinand --- appsec/src/extension/ddtrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appsec/src/extension/ddtrace.c b/appsec/src/extension/ddtrace.c index d3348765f5..cbf9d62565 100644 --- a/appsec/src/extension/ddtrace.c +++ b/appsec/src/extension/ddtrace.c @@ -248,14 +248,14 @@ static zval *_get_span_modifiable_array_property( { #if PHP_VERSION_ID >= 80000 zval *res = - zobj->handlers->get_property_ptr_ptr(zobj, propname, BP_VAR_R, NULL); + zobj->handlers->get_property_ptr_ptr(zobj, propname, BP_VAR_IS, NULL); #else zval obj; ZVAL_OBJ(&obj, zobj); zval prop; ZVAL_STR(&prop, propname); zval *res = - zobj->handlers->get_property_ptr_ptr(&obj, &prop, BP_VAR_R, NULL); + zobj->handlers->get_property_ptr_ptr(&obj, &prop, BP_VAR_IS, NULL); #endif From 2a48b149b887707472aa009e32e524e4e627321f Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 19 Aug 2024 18:13:12 +0200 Subject: [PATCH 024/103] Fix JIT crash with instrumented generators (#2797) The JIT will look up zend_op_trace_info in (char*)EX(opline) + offset, where offset is generally somewhere in valid memory, but will effectively have random values, and crash when using these. Also add tooling to trivially open a randomized corefile from circleci. Signed-off-by: Bob Weinand --- .../install_hook/trace_generator_jit.phpt | 42 +++++++++++++ tests/randomized/README.md | 12 +++- tests/randomized/circleci_core.sh | 59 +++++++++++++++++++ zend_abstract_interface/CMakeLists.txt | 3 + zend_abstract_interface/hook/CMakeLists.txt | 3 + zend_abstract_interface/hook/hook.c | 8 +++ .../hook/tests/CMakeLists.txt | 3 + .../jit_utils/CMakeLists.txt | 22 +++++++ 8 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 tests/ext/sandbox/install_hook/trace_generator_jit.phpt create mode 100755 tests/randomized/circleci_core.sh create mode 100644 zend_abstract_interface/jit_utils/CMakeLists.txt diff --git a/tests/ext/sandbox/install_hook/trace_generator_jit.phpt b/tests/ext/sandbox/install_hook/trace_generator_jit.phpt new file mode 100644 index 0000000000..a447530e89 --- /dev/null +++ b/tests/ext/sandbox/install_hook/trace_generator_jit.phpt @@ -0,0 +1,42 @@ +--TEST-- +generator hooking works with JIT +--SKIPIF-- + + +--INI-- +opcache.enable=1 +opcache.enable_cli = 1 +opcache.jit_buffer_size=512M +opcache.jit=1255 +zend_extension=opcache.so +--FILE-- + function($args, $retval) { + var_dump($retval); + } +]); + +for ($i = 0; $i < 2; ++$i) { + foreach (gen() as $val) { + var_dump($val); + } +} + +?> +--EXPECT-- +int(1) +int(1) +int(2) +int(2) +int(1) +int(1) +int(2) +int(2) diff --git a/tests/randomized/README.md b/tests/randomized/README.md index dfa4247811..cd0b5017e2 100644 --- a/tests/randomized/README.md +++ b/tests/randomized/README.md @@ -145,6 +145,16 @@ Note that the generated scenarios can be run individually. In the directory `.tm ### Analyzing a core dump generated in CI +Run a script `circleci_core.sh` which will install the used extension, and launch a gdb shell with the core dump: + +``` +tests/randomized/circleci_core.sh https://app.circleci.com/pipelines/github/DataDog/dd-trace-php/17113/workflows/196b5740-f3ce-4faf-8731-e9a4b15d114a/jobs/4964970/steps +``` + +The only argument to that script is the URL of the job. + +#### Alternative: Manual steps + Run a container with the most recent version of the proper docker image for the specific version of PHP. For example, assuming PHP 8.0: ``` @@ -172,7 +182,7 @@ Then load it in `gdb`: gdb --core=/tmp/core php-fpm|httpd|php ``` -### Analyzing using ASAN +#### Manual steps using ASAN In some cases it might be tricky to find the memory corruption, but you can always use an address sanitizer to try and find problems. diff --git a/tests/randomized/circleci_core.sh b/tests/randomized/circleci_core.sh new file mode 100755 index 0000000000..154e2348af --- /dev/null +++ b/tests/randomized/circleci_core.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# e.g. https://app.circleci.com/pipelines/github/DataDog/dd-trace-php/17113/workflows/196b5740-f3ce-4faf-8731-e9a4b15d114a/jobs/4964970/steps +URL=${1} + +if ! [[ $URL =~ workflows/([0-9a-f-]+)/jobs/([0-9]+) ]]; then + echo "Invalid URL?" + exit 1 +fi + +CIRCLE_WORKFLOW_ID=${BASH_REMATCH[1]} +CIRCLE_JOB_ID=${BASH_REMATCH[2]} + +TEST_NAME=$(curl -s -X GET "https://circleci.com/api/v2/project/gh/DataDog/dd-trace-php/job/$CIRCLE_JOB_ID" -H "Accept: application/json" | grep -Eo '"name":"randomized[^"]+') +TEST_NAME=${TEST_NAME:8} + +echo "Found test run: $TEST_NAME" + +COREFILES=($(curl -s -X GET "https://circleci.com/api/v2/project/gh/DataDog/dd-trace-php/$CIRCLE_JOB_ID/artifacts" -H "Accept: application/json" | grep -Eo 'https://[^"]+core')) + +if [[ -z $COREFILES ]]; then + echo "Found no core files..." + exit 1 +fi + +num=1 +for file in "${COREFILES[@]}"; do + echo "$((num++)): $(echo "$file" | grep -Eo 'randomized-[^/]+')" +done + +while : ; do + echo -n "Select one core file: " + read num + if [[ $num -gt ${#COREFILES[@]} || $num -le 0 ]]; then + echo "Invalid number $num" + else + break + fi +done + +COREFILE=${COREFILES[$((num - 1))]} +corefilename=$(echo "$COREFILE" | grep -Eo 'randomized-[^/]+') +container="datadog/dd-trace-ci:php-randomizedtests-$(if [[ $COREFILE == *buster* ]]; then echo buster; else echo centos7; fi)-${corefilename: -2:1}.${corefilename: -1}-2" + +if [[ $TEST_NAME == *asan* ]]; then + ARTIFACTS_JOB='package extension zts-debug-asan' +else + ARTIFACTS_JOB='package extension' +fi + +PACKAGE_ARCH=$(if [[ $TEST_NAME == *arm* ]]; then echo aarch64; else echo x86_64; fi) + +parent_job_id=$(curl -s -X GET "https://circleci.com/api/v2/workflow/$CIRCLE_WORKFLOW_ID/job" -H "Accept: application/json" | grep -Eo '\{[^}]*"'"$ARTIFACTS_JOB"'"[^}]*' | grep -Eo '"job_number":[^,]+' | tail -c +14) +ARTIFACTS_RESULT=$(curl -s -X GET "https://circleci.com/api/v2/project/github/DataDog/dd-trace-php/$parent_job_id/artifacts" -H "Accept: application/json") +artifact_url=$(echo "$ARTIFACTS_RESULT" | grep -Eo '\{[^}]*"dd-library-php-[^"]+'"${PACKAGE_ARCH}"'-linux-gnu.tar.gz"[^}]*' | grep -Eo '"url":"[^"]+' | tail -c +8) +setup_url=$(echo "$ARTIFACTS_RESULT" | grep -Eo '\{[^}]*"datadog-setup.php"[^}]*' | grep -Eo '"url":"[^"]+' | tail -c +8) + +set -x +docker run --platform=linux/$(if [[ $TEST_NAME == *arm* ]]; then echo arm64; else echo amd64; fi) --rm -ti "$container" bash -c "curl -Lo /tmp/datadog-setup.php '$setup_url'; curl -Lo /tmp/${artifact_url##*/} '$artifact_url'; curl -Lo /tmp/core '$COREFILE'; php /tmp/datadog-setup.php --php-bin all --file /tmp/${artifact_url##*/} --enable-profiling; $(if [[ $TEST_NAME == *asan* ]]; then echo "switch-php debug-zts-asan; "; fi)exec bash --rcfile <(echo 'gdb \$(file /tmp/core | grep -Po "'from\\s\\x27\\K[^\\x27:]+'") --core=/tmp/core -ix /usr/local/src/php/.gdbinit')" diff --git a/zend_abstract_interface/CMakeLists.txt b/zend_abstract_interface/CMakeLists.txt index e5ae6a1ded..f100b0285f 100644 --- a/zend_abstract_interface/CMakeLists.txt +++ b/zend_abstract_interface/CMakeLists.txt @@ -66,6 +66,9 @@ add_subdirectory(env) add_subdirectory(exceptions) add_subdirectory(config) add_subdirectory(json) +if(PhpConfig_VERNUM GREATER_EQUAL "80000") + add_subdirectory(jit_utils) +endif() add_subdirectory(symbols) add_subdirectory(hook) add_subdirectory(interceptor) diff --git a/zend_abstract_interface/hook/CMakeLists.txt b/zend_abstract_interface/hook/CMakeLists.txt index e8f4b2ca8c..c96ca3932a 100644 --- a/zend_abstract_interface/hook/CMakeLists.txt +++ b/zend_abstract_interface/hook/CMakeLists.txt @@ -7,6 +7,9 @@ target_include_directories( target_compile_features(zai_hook PUBLIC c_std_99) target_link_libraries(zai_hook PUBLIC Tea::Php Zai::Symbols) +if(PhpConfig_VERNUM GREATER_EQUAL "80000") + target_link_libraries(zai_hook PUBLIC Zai::JitUtils) +endif() set_target_properties(zai_hook PROPERTIES EXPORT_NAME Hook VERSION ${PROJECT_VERSION}) diff --git a/zend_abstract_interface/hook/hook.c b/zend_abstract_interface/hook/hook.c index 0cbb8f3731..a2fe7d770c 100644 --- a/zend_abstract_interface/hook/hook.c +++ b/zend_abstract_interface/hook/hook.c @@ -1,6 +1,7 @@ #include "../tsrmls_cache.h" #include #include +#include /* {{{ */ @@ -399,6 +400,13 @@ static void zai_hook_resolve_hooks_entry(zai_hooks_entry *hooks, zend_function * #endif } hooks->is_generator = (resolved->common.fn_flags & ZEND_ACC_GENERATOR) != 0; + if (hooks->is_generator) { +#if ZAI_JIT_BLACKLIST_ACTIVE + // Generator observers may replace the EX(opline) by a custom op. The tracing JIT will not like this. Blacklist them. + // Note that we cannot defer the blacklisting until the observer hook is invoked as that one will not be called before ZEND_GENERATOR_CREATE. + zai_jit_blacklist_function_inlining(&resolved->op_array); +#endif + } #endif if ((resolved->common.fn_flags & ZEND_ACC_CLOSURE) == 0) { diff --git a/zend_abstract_interface/hook/tests/CMakeLists.txt b/zend_abstract_interface/hook/tests/CMakeLists.txt index 69a9eb045d..d7abe945c3 100644 --- a/zend_abstract_interface/hook/tests/CMakeLists.txt +++ b/zend_abstract_interface/hook/tests/CMakeLists.txt @@ -4,6 +4,9 @@ add_executable(hooks ) target_link_libraries(hooks PUBLIC catch2_main Tea::Tea Zai::Symbols Zai::Hook) +if(PhpConfig_VERNUM GREATER_EQUAL "80000") + target_link_libraries(hooks PUBLIC Zai::JitUtils) +endif() file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/stubs DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/zend_abstract_interface/jit_utils/CMakeLists.txt b/zend_abstract_interface/jit_utils/CMakeLists.txt new file mode 100644 index 0000000000..18d5d50caf --- /dev/null +++ b/zend_abstract_interface/jit_utils/CMakeLists.txt @@ -0,0 +1,22 @@ +add_library(zai_jit_utils jit_blacklist.c) + +target_include_directories( + zai_jit_utils PUBLIC $ + $) + +target_compile_features(zai_jit_utils PUBLIC c_std_99) + +target_link_libraries(zai_jit_utils PUBLIC Tea::Php dl) + +set_target_properties(zai_jit_utils PROPERTIES EXPORT_NAME JitUtils + VERSION ${PROJECT_VERSION}) + +add_library(Zai::JitUtils ALIAS zai_jit_utils) + +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/jit_blacklist.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/jit_utils/) + +target_link_libraries(zai_zend_abstract_interface INTERFACE zai_jit_utils) + +install(TARGETS zai_jit_utils EXPORT ZendAbstractInterfaceTargets) From d37a63953e6451492635aadc87e8e4dcff645e88 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 20 Aug 2024 10:38:29 +0200 Subject: [PATCH 025/103] add timestamp to exception sample (#2793) --- profiling/src/profiling/mod.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/profiling/src/profiling/mod.rs b/profiling/src/profiling/mod.rs index a812074a29..f03b609eb6 100644 --- a/profiling/src/profiling/mod.rs +++ b/profiling/src/profiling/mod.rs @@ -684,7 +684,9 @@ impl Profiler { let (wall_time, cpu_time) = CLOCKS.with(|cell| cell.borrow_mut().rotate_clocks()); let labels = Profiler::common_labels(0); - let mut timestamp = 0; + let n_labels = labels.len(); + + let mut timestamp = NO_TIMESTAMP; #[cfg(feature = "timeline")] { let system_settings = self.system_settings.load(Ordering::SeqCst); @@ -696,8 +698,6 @@ impl Profiler { } } - let n_labels = labels.len(); - match self.prepare_and_send_message( frames, SampleValues { @@ -790,6 +790,18 @@ impl Profiler { let n_labels = labels.len(); + let mut timestamp = NO_TIMESTAMP; + #[cfg(feature = "timeline")] + { + let system_settings = self.system_settings.load(Ordering::SeqCst); + // SAFETY: system settings are stable during a request. + if unsafe { *ptr::addr_of!((*system_settings).profiling_timeline_enabled) } { + if let Ok(now) = SystemTime::now().duration_since(UNIX_EPOCH) { + timestamp = now.as_nanos() as i64; + } + } + } + match self.prepare_and_send_message( frames, SampleValues { @@ -797,7 +809,7 @@ impl Profiler { ..Default::default() }, labels, - NO_TIMESTAMP, + timestamp, ) { Ok(_) => trace!( "Sent stack sample of {depth} frames, {n_labels} labels with Exception {exception} to profiler." From a3f6980cd0f6d715f1aac5897bb5f653e705812c Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Tue, 20 Aug 2024 14:10:43 +0200 Subject: [PATCH 026/103] Metrics order is not guaranteed, sort them before comparing (#2799) --- tests/Common/SnapshotTestTrait.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/Common/SnapshotTestTrait.php b/tests/Common/SnapshotTestTrait.php index 7c7534a469..d8fbab1172 100644 --- a/tests/Common/SnapshotTestTrait.php +++ b/tests/Common/SnapshotTestTrait.php @@ -282,6 +282,12 @@ private function compareMetricsArrays($expectedMetrics, $receivedMetrics, $field $expectedMetrics = $this->filterMetrics($expectedMetrics, $fieldsToIgnore); $receivedMetrics = $this->filterMetrics($receivedMetrics, $fieldsToIgnore); + $alg = function ($a, $b) { + return strcmp($a['name'], $b['name']); + }; + usort($expectedMetrics, $alg); + usort($receivedMetrics, $alg); + TestCase::assertEquals($expectedMetrics, $receivedMetrics, "Metrics don't match"); } From bc14ea0ae81dd5e332fce32a0be249fa4093ed7c Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 20 Aug 2024 14:13:09 +0200 Subject: [PATCH 027/103] Handle absolute paths in extension_dir containing forward slashes (#2798) extension_dir = c:/php/ext is also valid on Windows. Fixes #2786. --- datadog-setup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog-setup.php b/datadog-setup.php index a10d60131c..018ed5db39 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -1574,7 +1574,7 @@ function ini_values($binary) if ($found[EXTENSION_DIR] == "") { $found[EXTENSION_DIR] = dirname(PHP_BINARY); - } elseif ($found[EXTENSION_DIR][0] != "/" && (!IS_WINDOWS || !preg_match('~^([A-Z]:|\\\\)\\\\~i', $found[EXTENSION_DIR]))) { + } elseif ($found[EXTENSION_DIR][0] != "/" && (!IS_WINDOWS || !preg_match('~^([A-Z]:[\\\\/]|\\\\{2})~i', $found[EXTENSION_DIR]))) { $found[EXTENSION_DIR] = dirname(PHP_BINARY) . '/' . $found[EXTENSION_DIR]; } From 3b147e12cfac8adcb38aff867b2a52544524a43a Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 20 Aug 2024 15:40:56 +0200 Subject: [PATCH 028/103] Parallelize integration and web tests (#2792) * Try weird stuff Signed-off-by: Bob Weinand * Eliminate scenarios We have phpunit load the correct autoloader and run composer in the individual integrations directories. For PHP extensions, we build them as part of the container, then load via CLI -d extension= INI setting. This is necessary to be able to run tests in parallel. Also removing old, redundant OpenTracing1-betaX testsuites. Signed-off-by: Bob Weinand * More fixes for conflicts Signed-off-by: Bob Weinand * Make testInstrumentation() less flaky * Fix openAI snapshots --------- Signed-off-by: Bob Weinand --- .circleci/config.yml | 76 +- .circleci/continue_config.yml | 73 +- Cargo.lock | 2 + Makefile | 304 +- components-rs/common.h | 2 + components-rs/sidecar.h | 7 + docker-compose.yml | 4 +- dockerfiles/ci/buster/build-extensions.sh | 31 +- ...ORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch | 27 + dockerfiles/ci/buster/php-8.0/Dockerfile | 2 + ...ORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch | 27 + dockerfiles/ci/buster/php-8.1/Dockerfile | 2 + ...ORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch | 27 + dockerfiles/ci/buster/php-8.2/Dockerfile | 2 + ...ORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch | 27 + dockerfiles/ci/buster/php-8.3/Dockerfile | 2 + dockerfiles/services/Makefile | 4 + dockerfiles/services/mysql/Dockerfile | 3 + dockerfiles/services/mysql/init.sql | 13 + .../services/request-replayer/src/index.php | 183 +- ext/coms.c | 16 +- ext/coms.h | 3 + ext/configuration.c | 1 + ext/configuration.h | 1 + ext/ddtrace.c | 4 + ext/sidecar.c | 25 +- ext/sidecar.h | 2 + libdatadog | 2 +- .../Roadrunner/RoadrunnerIntegration.php | 2 +- tests/Appsec/Mock.php | 24 +- tests/Benchmarks/composer.json | 7 + tests/Common/AppsecTestCase.php | 9 +- tests/Common/BaseTestCase.php | 12 +- tests/Common/CLITestCase.php | 3 +- tests/Common/IntegrationTestCase.php | 20 +- tests/Common/SnapshotTestTrait.php | 12 +- tests/Common/SpanChecker.php | 24 +- tests/Common/TracerTestTrait.php | 21 +- tests/Common/WebFrameworkTestCase.php | 5 +- .../Composer/ComposerInteroperabilityTest.php | 20 +- tests/DistributedTracing/SyntheticsTest.php | 2 +- .../Version_2_8/app/Config/database.php | 4 +- .../Drupal/Core/Command/InstallCommand.php | 2 +- .../Version_10_1/scripts/erase_drupal_db.php | 68 +- .../Drupal/Core/Command/InstallCommand.php | 2 +- .../Version_8_9/scripts/erase_drupal_db.php | 68 +- .../sites/default/default.settings.php | 4 +- .../Drupal/Version_9_5/composer.lock | 8625 ----------------- .../Metapackage/CoreRecommended/composer.json | 50 +- .../PinnedDevDependencies/composer.json | 64 +- .../DependencyInjection/composer.json | 2 +- .../Component/EventDispatcher/composer.json | 2 +- .../Drupal/Core/Command/InstallCommand.php | 2 +- .../Version_9_5/scripts/erase_drupal_db.php | 68 +- tests/Frameworks/Laravel/Octane/.gitignore | 1 + .../Laravel/Version_10_x/composer.json | 1 + .../Laravel/Version_10_x/config/database.php | 2 +- .../Version_4_2/app/config/database.php | 2 +- .../Laravel/Version_4_2/composer.json | 2 + .../Laravel/Version_5_7/composer.json | 1 + .../Laravel/Version_5_7/config/database.php | 2 +- .../Laravel/Version_5_8/composer.json | 1 + .../Laravel/Version_5_8/config/database.php | 2 +- tests/Frameworks/Laravel/Version_8_x/.env | 2 +- .../Laravel/Version_8_x/.env.example | 2 +- .../Laravel/Version_8_x/composer.json | 1 + .../Laravel/Version_8_x/config/database.php | 2 +- .../Laravel/Version_9_x/composer.json | 1 + .../Laravel/Version_9_x/config/database.php | 2 +- .../Magento/Version_2_3/install-magento | 4 +- .../Magento/Version_2_4/install-magento | 4 +- tests/Frameworks/Swoole/index.php | 2 +- .../app/config/parameters.yml.dist | 2 +- .../Symfony/Version_3_3/composer.json | 2 +- .../Symfony/Version_4_4/composer.json | 2 +- .../Version_4_4/config/packages/doctrine.yaml | 2 +- .../Symfony/Version_5_2/composer.json | 2 +- .../Version_5_2/config/packages/doctrine.yaml | 2 +- .../Symfony/Version_6_2/composer.json | 2 +- .../Version_6_2/config/packages/doctrine.yaml | 2 +- .../Symfony/Version_7_0/composer.json | 2 +- .../Version_7_0/config/packages/doctrine.yaml | 2 +- .../WordPress/Version_4_8/wp-config.php | 2 +- .../WordPress/Version_4_8/wp_2019-10-01.sql | 12 +- .../WordPress/Version_5_5/wp-config.php | 2 +- .../WordPress/Version_5_5/wp_2020-10-21.sql | 8 +- .../WordPress/Version_5_9/wp-config.php | 2 +- .../Version_6_1/scripts/wp_initdb.sql | 14 +- .../WordPress/Version_6_1/wp-config.php | 2 +- tests/Integration/ResponseStatusCodeTest.php | 4 +- tests/Integrations/AMQP/{ => V2}/AMQPTest.php | 26 +- tests/Integrations/AMQP/V2/composer.json | 5 + tests/Integrations/AMQP/V3_5/AMQPTest.php | 7 + tests/Integrations/AMQP/V3_5/composer.json | 5 + tests/Integrations/AMQP/scripts/receive.php | 2 +- tests/Integrations/AMQP/scripts/send.php | 1 + .../CLI/Symfony/V6_2/CommonScenariosTest.php | 6 +- .../CakePHP/V2_8/CommonScenariosTest.php | 8 +- .../CakePHP/V3_10/CommonScenariosTest.php | 8 +- .../CakePHP/V4_5/CommonScenariosTest.php | 8 +- .../CodeIgniter/V2_2/CommonScenariosTest.php | 8 +- .../CodeIgniter/V2_2/ExitTest.php | 2 +- .../CodeIgniter/V2_2/NoCI_ControllertTest.php | 2 +- .../Custom/Autoloaded/CommonScenariosTest.php | 6 +- .../Custom/Autoloaded/FatalErrorTest.php | 2 +- .../Custom/Autoloaded/InstrumentationTest.php | 14 +- .../Autoloaded/TraceSearchConfigTest.php | 2 +- .../HttpHeadersConfiguredTest.php | 2 +- .../HttpHeadersNotConfiguredTest.php | 2 +- .../NotAutoloaded/IncomingUserInfoTest.php | 4 +- tests/Integrations/DeferredLoading/index.php | 2 +- .../Drupal/V10_1/CommonScenariosTest.php | 2 + .../Drupal/V8_9/CommonScenariosTest.php | 4 +- .../Drupal/V9_5/CommonScenariosTest.php | 2 + .../V1/ElasticSearchIntegrationTest.php | 64 +- .../Elasticsearch/V1/composer.json | 6 + .../V7/ElasticSearchIntegrationTest.php | 7 + .../Elasticsearch/V7/composer.json | 6 + .../V8/ElasticSearchIntegrationTest.php | 64 +- .../Elasticsearch/V8/composer.json | 5 + .../Frankenphp/CommonScenariosTest.php | 4 +- tests/Integrations/Guzzle/V5/composer.json | 5 + .../V5/guzzle_in_distributed_web_request.php | 1 + .../Guzzle/V5/guzzle_in_web_request.php | 1 + .../Guzzle/V6/GuzzleIntegrationTest.php | 4 +- tests/Integrations/Guzzle/V6/composer.json | 5 + .../V6/guzzle_in_distributed_web_request.php | 1 + .../Guzzle/V6/guzzle_in_web_request.php | 1 + tests/Integrations/Guzzle/V7/composer.json | 6 + .../Laravel/AutomatedLoginEventsTestSuite.php | 2 + .../Laravel/Octane/CommonScenariosTest.php | 21 +- .../V10_x/AutomatedLoginEventsTest.php | 2 + .../Laravel/V10_x/CommonScenariosTest.php | 2 + .../Laravel/V4/AutomatedLoginEventsTest.php | 2 + .../Laravel/V4/CommonScenariosTest.php | 8 +- .../Integrations/Laravel/V4/EloquentTest.php | 4 +- .../Laravel/V4/PathParamsTest.php | 2 + .../Laravel/V4/TraceSearchConfigTest.php | 4 +- .../Laravel/V5_7/AutomatedLoginEventsTest.php | 2 + .../Laravel/V5_7/CommonScenariosTest.php | 2 + .../Laravel/V5_7/EloquentTest.php | 4 +- .../Laravel/V5_7/PathParamsTest.php | 2 + .../Laravel/V5_7/PipelineTracingTest.php | 5 +- tests/Integrations/Laravel/V5_7/QueueTest.php | 2 + .../Laravel/V5_7/TraceSearchConfigTest.php | 4 +- .../Laravel/V5_8/AutomatedLoginEventsTest.php | 2 + .../Laravel/V5_8/CommonScenariosTest.php | 7 +- .../Laravel/V5_8/EloquentTest.php | 7 +- .../Laravel/V5_8/PathParamsTest.php | 2 + tests/Integrations/Laravel/V5_8/QueueTest.php | 7 +- .../Laravel/V5_8/TraceSearchConfigTest.php | 4 +- .../Laravel/V8_x/AutomatedLoginEventsTest.php | 2 + .../Laravel/V8_x/CommonScenariosTest.php | 2 + .../Laravel/V8_x/EloquentTest.php | 7 +- .../Laravel/V8_x/HttpHideRouteTest.php | 4 +- .../Laravel/V8_x/InternalExceptionsTest.php | 9 +- .../Laravel/V8_x/PathParamsTest.php | 2 + tests/Integrations/Laravel/V8_x/QueueTest.php | 12 +- .../Laravel/V8_x/QueueTestNotDistributed.php | 7 +- .../Laravel/V8_x/RouteCachingTest.php | 6 +- .../Laravel/V8_x/TraceSearchConfigTest.php | 4 +- .../Laravel/V9_x/AutomatedLoginEventsTest.php | 2 + .../Laravel/V9_x/CommonScenariosTest.php | 2 + tests/Integrations/Logs/BaseLogsTest.php | 9 +- .../Logs/LaminasLogV2/LaminasLogV2Test.php | 2 +- .../Logs/LaminasLogV2/composer.json | 5 + .../Logs/MonologV1/MonologV1Test.php | 2 +- .../Integrations/Logs/MonologV1/composer.json | 5 + .../Integrations/Logs/MonologV2/composer.json | 5 + .../Integrations/Logs/MonologV3/composer.json | 5 + .../Lumen/V10_0/TraceSearchConfigTest.php | 2 +- .../Lumen/V5_2/CommonScenariosTest.php | 8 +- .../Lumen/V5_2/TraceSearchConfigTest.php | 2 +- .../Lumen/V5_6/CommonScenariosTest.php | 6 +- .../Lumen/V5_6/DeprecatedResourceNameTest.php | 2 +- .../Lumen/V5_6/TraceSearchConfigTest.php | 2 +- .../Lumen/V5_8/TraceSearchConfigTest.php | 2 +- .../Lumen/V8_1/TraceSearchConfigTest.php | 2 +- .../Lumen/V9_0/TraceSearchConfigTest.php | 2 +- .../Magento/V2_3/CommonScenariosTest.php | 2 + .../Magento/V2_4/CommonScenariosTest.php | 2 + tests/Integrations/Memcache/MemcacheTest.php | 2 + .../Integrations/Memcached/MemcachedTest.php | 2 + tests/Integrations/Mongo/MongoTest.php | 2 + tests/Integrations/MongoDB/MongoDBTest.php | 2 + tests/Integrations/MongoDB/composer.json | 5 + tests/Integrations/Mysqli/MysqliTest.php | 68 +- tests/Integrations/Nette/V2_4/NetteTest.php | 6 +- tests/Integrations/Nette/V3_0/NetteTest.php | 6 +- tests/Integrations/OpenAI/composer.json | 7 + tests/Integrations/PDO/PDOTest.php | 58 +- .../PHPRedis/V3/PHPRedisClusterTest.php | 2 + .../Integrations/PHPRedis/V3/PHPRedisTest.php | 2 + .../PHPRedis/V4/PHPRedisClusterTest.php | 2 + .../Integrations/PHPRedis/V4/PHPRedisTest.php | 2 + .../PHPRedis/V5/PHPRedisClusterTest.php | 2 + .../Integrations/PHPRedis/V5/PHPRedisTest.php | 2 + tests/Integrations/Predis/PredisTest.php | 2 + tests/Integrations/Predis/composer.json | 5 + .../Roadrunner/V2/CommonScenariosTest.php | 6 +- tests/Integrations/SQLSRV/SQLSRVTest.php | 17 +- .../Slim/V3_12/CommonScenariosTest.php | 8 +- .../Slim/V4/CommonScenariosTest.php | 8 +- .../Swoole/CommonScenariosTest.php | 6 +- .../Symfony/V3_0/CommonScenariosTest.php | 8 +- .../Symfony/V3_0/TraceSearchConfigTest.php | 2 +- .../Symfony/V3_3/AutomatedLoginEventsTest.php | 2 + .../Symfony/V3_3/CommonScenariosTest.php | 8 +- .../Symfony/V3_3/PathParamsTest.php | 2 + .../Symfony/V3_3/TraceSearchConfigTest.php | 2 +- .../V3_4/AutofinishedTracesSymfony34Test.php | 2 +- .../Symfony/V3_4/CommonScenariosTest.php | 8 +- .../Symfony/V3_4/TemplateEnginesTest.php | 2 +- .../Symfony/V3_4/TraceSearchConfigTest.php | 2 +- .../Symfony/V4_0/CommonScenariosTest.php | 8 +- .../Symfony/V4_0/TraceSearchConfigTest.php | 2 +- .../Symfony/V4_2/CommonScenariosTest.php | 8 +- .../Symfony/V4_2/TraceSearchConfigTest.php | 2 +- .../Symfony/V4_4/AutomatedLoginEventsTest.php | 2 + .../Symfony/V4_4/CommonScenariosTest.php | 8 +- .../Symfony/V4_4/PathParamsTest.php | 2 + .../Symfony/V4_4/TraceSearchConfigTest.php | 2 +- .../Symfony/V5_0/CommonScenariosTest.php | 8 +- .../Symfony/V5_0/TraceSearchConfigTest.php | 2 +- .../Symfony/V5_1/CommonScenariosTest.php | 8 +- .../Symfony/V5_1/TraceSearchConfigTest.php | 2 +- .../Symfony/V5_2/AutomatedLoginEventsTest.php | 2 + .../Symfony/V5_2/CommonScenariosTest.php | 8 +- .../Symfony/V5_2/PathParamsTest.php | 2 + .../Symfony/V5_2/TraceSearchConfigTest.php | 2 +- .../Symfony/V6_2/AutomatedLoginEventsTest.php | 1 + .../Symfony/V6_2/CommonScenariosTest.php | 8 +- .../Symfony/V6_2/ConsoleCommandTest.php | 2 +- .../Symfony/V6_2/PathParamsTest.php | 2 + .../Symfony/V6_2/TraceSearchConfigTest.php | 2 +- .../Symfony/V7_0/AutomatedLoginEventsTest.php | 2 + .../Symfony/V7_0/CommonScenariosTest.php | 8 +- .../V4_8/AutomatedLoginEventsTest.php | 2 + .../WordPress/V4_8/CommonScenariosTest.php | 4 +- .../WordPress/V4_8/PathParamsTest.php | 2 + .../V5_5/AutomatedLoginEventsTest.php | 2 + .../WordPress/V5_5/CommonScenariosTest.php | 4 +- .../WordPress/V5_5/PathParamsTest.php | 2 + .../V5_9/AutomatedLoginEventsTest.php | 2 + .../WordPress/V5_9/CommonScenariosTest.php | 4 +- .../WordPress/V5_9/PathParamsTest.php | 2 + .../WordPress/V6_1/CommonScenariosTest.php | 4 +- .../WordPress/V6_1/PathParamsTest.php | 2 + .../Yii/V2_0/CommonScenariosTest.php | 8 +- tests/Integrations/Yii/V2_0/ModuleTest.php | 2 +- .../Yii/V2_0/ParameterizedRouteTest.php | 2 +- .../Integrations/Yii/V2_0/YiiDetailsTest.php | 2 +- .../ZendFramework/V1/CommonScenariosTest.php | 6 +- .../V1/TraceSearchConfigTest.php | 2 +- .../V1_21/CommonScenariosTest.php | 6 +- .../V1_21/TraceSearchConfigTest.php | 2 +- tests/OpenTelemetry/composer.json | 8 + tests/OpenTracer1Unit/composer.json | 5 + tests/OpenTracerUnit/GlobalTracerTest.php | 28 - tests/OpenTracerUnit/ScopeManagerTest.php | 63 - tests/OpenTracerUnit/ScopeTest.php | 29 - tests/OpenTracerUnit/SpanContextTest.php | 65 - tests/OpenTracerUnit/SpanTest.php | 165 - tests/OpenTracerUnit/TracerTest.php | 315 - tests/Sapi/CliServer/CliServer.php | 2 - tests/Sapi/Frankenphp/FrankenphpServer.php | 2 - tests/Sapi/OctaneServer/OctaneServer.php | 8 +- tests/Sapi/Roadrunner/RoadrunnerServer.php | 7 +- tests/Sapi/SwooleServer/SwooleServer.php | 16 +- tests/Unit/Util/OrphansTest.php | 2 - tests/WebServer.php | 6 +- tests/bootstrap.php | 26 - ...otstrap_utils.php => bootstrap_common.php} | 20 +- tests/bootstrap_phpbench.php | 5 + tests/bootstrap_phpunit.php | 70 + tests/clean-composer-scenario-locks.sh | 4 - tests/composer.json | 226 - tests/ext/appsec/sca_flag_is_sent_01.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_02.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_03.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_04.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_05.phpt | 1 + tests/ext/appsec/sca_test.inc | 5 +- tests/ext/background-sender/CONFLICTS | 1 - .../ext/background-sender/agent_headers.phpt | 2 + .../agent_headers_container_id.phpt | 5 +- .../agent_headers_container_id_empty.phpt | 5 +- .../agent_headers_container_id_fargate.phpt | 5 +- .../agent_headers_ignore_userland.phpt | 2 + .../agent_headers_unix_domain_socket.phpt | 2 + .../ext/background-sender/agent_sampling.phpt | 4 + .../agent_sampling_sidecar.phpt | 63 +- .../background_sender_survives_setuid.phpt | 1 + .../background-sender/sidecar_fallback.phpt | 2 + tests/ext/includes/clear_skipif_telemetry.inc | 11 + tests/ext/includes/request_replayer.inc | 11 +- ..._metadata_injection_from_valid_files.phpt} | 2 +- ...etadata_injection_from_invalid_files.phpt} | 2 +- tests/ext/request-replayer/CONFLICTS | 1 - .../dd_trace_span_link_with_exception.phpt | 2 + tests/ext/telemetry/composer.phpt | 1 + tests/ext/telemetry/config.phpt | 3 +- tests/ext/telemetry/integration.phpt | 1 + tests/ext/telemetry/metrics_logs_created.phpt | 1 + .../ext/telemetry/metrics_spans_created.phpt | 1 + tests/ext/telemetry/simple.phpt | 4 + tests/phpbench-opcache.json | 2 +- tests/phpbench.json | 2 +- tests/phpunit.xml | 2 +- ...oller_test.test_scenario_health_check.json | 2 +- ..._test.test_scenario_get_parameterized.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...est_scenario_get_to_missing_route_cgi.json | 2 +- ...test.test_scenario_get_with_exception.json | 10 +- ....test_scenario_get_with_exception_cgi.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ...ter.v3_1.exit_test.test_scenario_exit.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 6 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 6 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 6 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ...zzle_integration_test.test_multi_exec.json | 4 +- ....v1_9.rest_test.test_scenario_rest2xx.json | 2 +- ....v1_9.rest_test.test_scenario_rest4xx.json | 4 +- ....v1_9.rest_test.test_scenario_rest5xx.json | 14 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...test.test_scenario_get_with_exception.json | 18 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...test.test_scenario_get_with_exception.json | 18 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_dynamic_route.json | 4 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 4 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 4 +- ..._test.test_scenario_get_dynamic_route.json | 4 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 4 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 4 +- ..._test.test_scenario_get_dynamic_route.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 4 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 4 +- ..._test.test_scenario_get_dynamic_route.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 4 +- ...test.test_scenario_get_with_exception.json | 12 +- ...rios_test.test_scenario_get_with_view.json | 4 +- ..._test.test_scenario_get_dynamic_route.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 4 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 4 +- ...aravel.v8_x.queue_test.test_broadcast.json | 2 +- ..._test.test_scenario_get_dynamic_route.json | 4 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 4 +- ...test.test_scenario_get_with_exception.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 4 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...test.test_scenario_get_with_exception.json | 26 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...test.test_scenario_get_with_exception.json | 26 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ...ate_chat_completion_stream_with_error.json | 4 +- ...pletions_with_multiple_error_messages.json | 4 +- ...n_ai_test.test_list_models_with_error.json | 4 +- ...test_list_models_with_null_error_type.json | 4 +- ..._test.test_scenario_get_return_string.json | 2 +- ...est_scenario_get_return_string_apache.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 6 +- ..._scenario_get_to_missing_route_apache.json | 6 +- ...test.test_scenario_get_with_exception.json | 10 +- ...st_scenario_get_with_exception_apache.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ...st.test_scenario_get_with_view_apache.json | 2 +- ...ame_test.test_resource_to_uri_mapping.json | 2 +- ...t.test_resource_to_uri_mapping_apache.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...est_scenario_get_return_string_apache.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 6 +- ..._scenario_get_to_missing_route_apache.json | 6 +- ...test.test_scenario_get_with_exception.json | 10 +- ...st_scenario_get_with_exception_apache.json | 10 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ...st.test_scenario_get_with_view_apache.json | 2 +- ...ame_test.test_resource_to_uri_mapping.json | 2 +- ...t.test_resource_to_uri_mapping_apache.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...test.test_scenario_get_with_exception.json | 14 +- ...acks_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...test.test_scenario_get_with_exception.json | 14 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...test.test_scenario_get_with_exception.json | 14 +- ...acks_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...test.test_scenario_get_with_exception.json | 14 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 4 +- ...test.test_scenario_get_with_exception.json | 14 +- ...acks_test.test_scenario_get_with_view.json | 4 +- ..._test.test_scenario_get_return_string.json | 2 +- ...st.test_scenario_get_to_missing_route.json | 2 +- ...test.test_scenario_get_with_exception.json | 14 +- ...rios_test.test_scenario_get_with_view.json | 2 +- ..._test.test_scenario_get_return_string.json | 4 +- ...test.test_scenario_get_with_exception.json | 16 +- ...acks_test.test_scenario_get_with_view.json | 4 +- ..._test.test_scenario_get_return_string.json | 2 +- ...test.test_scenario_get_with_exception.json | 14 +- ...rios_test.test_scenario_get_with_view.json | 2 +- 430 files changed, 2034 insertions(+), 10856 deletions(-) create mode 100644 dockerfiles/ci/buster/php-8.0/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch create mode 100644 dockerfiles/ci/buster/php-8.1/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch create mode 100644 dockerfiles/ci/buster/php-8.2/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch create mode 100644 dockerfiles/ci/buster/php-8.3/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch create mode 100644 dockerfiles/services/mysql/Dockerfile create mode 100644 dockerfiles/services/mysql/init.sql create mode 100644 tests/Benchmarks/composer.json delete mode 100644 tests/Frameworks/Drupal/Version_9_5/composer.lock rename tests/Integrations/AMQP/{ => V2}/AMQPTest.php (98%) create mode 100644 tests/Integrations/AMQP/V2/composer.json create mode 100644 tests/Integrations/AMQP/V3_5/AMQPTest.php create mode 100644 tests/Integrations/AMQP/V3_5/composer.json create mode 100644 tests/Integrations/Elasticsearch/V1/composer.json create mode 100644 tests/Integrations/Elasticsearch/V7/ElasticSearchIntegrationTest.php create mode 100644 tests/Integrations/Elasticsearch/V7/composer.json create mode 100644 tests/Integrations/Elasticsearch/V8/composer.json create mode 100644 tests/Integrations/Guzzle/V5/composer.json create mode 100644 tests/Integrations/Guzzle/V6/composer.json create mode 100644 tests/Integrations/Guzzle/V7/composer.json create mode 100644 tests/Integrations/Logs/LaminasLogV2/composer.json create mode 100644 tests/Integrations/Logs/MonologV1/composer.json create mode 100644 tests/Integrations/Logs/MonologV2/composer.json create mode 100644 tests/Integrations/Logs/MonologV3/composer.json create mode 100644 tests/Integrations/MongoDB/composer.json create mode 100644 tests/Integrations/OpenAI/composer.json create mode 100644 tests/Integrations/Predis/composer.json create mode 100644 tests/OpenTelemetry/composer.json create mode 100644 tests/OpenTracer1Unit/composer.json delete mode 100644 tests/OpenTracerUnit/GlobalTracerTest.php delete mode 100644 tests/OpenTracerUnit/ScopeManagerTest.php delete mode 100644 tests/OpenTracerUnit/ScopeTest.php delete mode 100644 tests/OpenTracerUnit/SpanContextTest.php delete mode 100644 tests/OpenTracerUnit/SpanTest.php delete mode 100644 tests/OpenTracerUnit/TracerTest.php delete mode 100644 tests/bootstrap.php rename tests/{bootstrap_utils.php => bootstrap_common.php} (68%) create mode 100644 tests/bootstrap_phpbench.php create mode 100644 tests/bootstrap_phpunit.php delete mode 100755 tests/clean-composer-scenario-locks.sh delete mode 100644 tests/ext/background-sender/CONFLICTS create mode 100644 tests/ext/includes/clear_skipif_telemetry.inc rename tests/ext/integrations/source_code/{001-git_metadata_injection_from_valid_files.phpt => 001/git_metadata_injection_from_valid_files.phpt} (96%) rename tests/ext/integrations/source_code/{002-git_metadata_injection_from_invalid_files.phpt => 002/git_metadata_injection_from_invalid_files.phpt} (97%) delete mode 100644 tests/ext/request-replayer/CONFLICTS diff --git a/.circleci/config.yml b/.circleci/config.yml index ac2c2b0922..7f00bc8cd2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,8 +10,82 @@ workflows: setup-workflows: jobs: - path-filtering/filter: + pre-steps: + - checkout + - run: + name: Generate custom steps for test_web and test_integrations + command: | + cp .circleci/continue_config.yml ../continue_config.yml + + for testsuite in integration_snapshots integration_tests; do + START=$(grep -Pzom1 '\A(.*+\n)+?\s++'"$testsuite"':(.*+\n)+?(?=(\s++)- run.*\n(\3.*+\n)*?\s++name: Run.*tests)' ../continue_config.yml) + END=$(grep -Pzom1 "$testsuite"':[^\0]+?(\s+)- run:(\1.+)+?name: Run.*tests[^\0]+?\K(?=\1\S)(.*\n)++' ../continue_config.yml) + + command=$(grep -Pzo "$testsuite"':[^\0]+?(\s+)- run:(\1.+)+?name: Run.*tests\K[^\0]+?(?=\1\S)' ../continue_config.yml) + command=${command//$'\n'/$'\n '} + + { + echo "$START" + + for type in WEB INTEGRATIONS; do + eval "$(grep -Pzo 'TEST_'"${type}"'_[0-9]+[^\0]+?\n(?![\t#])' Makefile | grep -avE '^#' | sed -e 's/\s*:= */=(/g' -e 's/\x0/)\n/g' -e 's/\s*\\//g')" + PHP_VERSIONS=($(grep -Po "(?<=TEST_${type}_)[0-9]+" Makefile)) + ALL_TASKS=($(grep -Pzo 'TEST_'"${type}"'_[0-9]+[^\0]+?\n(?![\t#])' Makefile | grep -avE '^#' | grep -a '\t' | sed -e 's/\t//g' -e 's/\s*\\//g' | sort | uniq)) + + for task in "${ALL_TASKS[@]}"; do + targeted_command=${command//\<< parameters.make_target >>/$task} + targeted_command=${targeted_command/$'|\n'/$'|\n echo $$ >/tmp/background-'$task$'.pid; trap \'rv=$?; [ $rv -eq 0 ] || touch /tmp/background-error; exit $rv\' INT TERM EXIT\n'} + cat \<> ]" + fi + done) + - equal: [ "test_${type,,}", \<< parameters.make_target >> ] + steps: + - run: + background: true + name: Run $task$targeted_command + EOT + done + done + + cat \<> ] + - equal: [ "test_integrations", \<< parameters.make_target >> ] + steps: + - run: + name: Await all tests + command: | + sleep 10 # wait for all background tasks to have launched + for f in \$(echo /tmp/background-*); do + tail --pid=\$(cat \$f) -f /dev/null || true + done + ! [ -f /tmp/background-error ] + - when: + condition: + not: + or: + - equal: [ "test_web", \<< parameters.make_target >> ] + - equal: [ "test_integrations", \<< parameters.make_target >> ] + steps: + - run: + name: Run tests${command} + EOT + echo "$END" + } > ../continue_config.yml + done + base-revision: master - config-path: .circleci/continue_config.yml + config-path: ../continue_config.yml # mapping: | components/.* components-c true diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 5323c18e94..aa9362a979 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -267,6 +267,14 @@ aliases: echo "export DD_TRACE_ASSUME_COMPILED=1" >> $BASH_ENV fi + - &STEP_SUBSTITUTE_SNAPSHOT_DIR + run: + name: Replace /home/circleci/app by /home/circleci/datadog in snapshot results + command: | + for f in $(find tests/snapshots -type f); do + sed -i 's/\/home\/circleci\/app\//\/home\/circleci\/datadog\//g' $f + done + - &STEP_STORE_TEST_RESULTS store_test_results: path: test-results @@ -1647,7 +1655,7 @@ jobs: - equal: [ "8.0", << parameters.php_major_minor >> ] - equal: [ "8.1", << parameters.php_major_minor >> ] - not: - equal: [ "test_composer", << parameters.make_target >> ] + equal: [ "test_composer", << parameters.make_target >> ] steps: - run: name: Updating composer to v2 @@ -1662,6 +1670,7 @@ jobs: equal: [ false, << parameters.coverage >> ] steps: - <<: *STEP_DISABLE_XDEBUG + - <<: *STEP_SUBSTITUTE_SNAPSHOT_DIR - <<: *STEP_AWAIT_LINK_EXTENSION - install_extension - <<: *STEP_WAIT_MYSQL @@ -1672,7 +1681,7 @@ jobs: command: | set -euo pipefail <<# parameters.coverage >>unset CI && unset CIRCLECI && export DD_AUTOLOAD_NO_COMPILE=true<> - DD_TRACE_AGENT_TIMEOUT=1000 <<# parameters.disable_runner_distributed_tracing >> DD_DISTRIBUTED_TRACING=false <> DD_TRACE_TEST_SAPI=<< parameters.sapi >> make << parameters.make_target >> PHPUNIT_OPTS="--log-junit test-results/php-composer/results.xml" + DD_TRACE_AGENT_TIMEOUT=1000 <<# parameters.disable_runner_distributed_tracing >> DD_DISTRIBUTED_TRACING=false <> DD_TRACE_TEST_SAPI=<< parameters.sapi >> make << parameters.make_target >> PHPUNIT_OPTS="--log-junit test-results/php-composer/results-<< parameters.make_target >>.xml" - <<: *STEP_CODE_COVERAGE - run: command: | @@ -1736,7 +1745,7 @@ jobs: - equal: [ "8.0", << parameters.php_major_minor >> ] - equal: [ "8.1", << parameters.php_major_minor >> ] - not: - equal: [ "test_composer", << parameters.make_target >> ] + equal: [ "test_composer", << parameters.make_target >> ] steps: - run: name: Updating composer to v2 @@ -1751,6 +1760,7 @@ jobs: equal: [ false, << parameters.coverage >> ] steps: - <<: *STEP_DISABLE_XDEBUG + - <<: *STEP_SUBSTITUTE_SNAPSHOT_DIR - <<: *STEP_AWAIT_LINK_EXTENSION - install_extension - when: @@ -1868,7 +1878,7 @@ jobs: type: string docker_image: type: string - integration_testsuite: + make_target: type: string lib_curl_command: type: string @@ -1917,6 +1927,7 @@ jobs: - <<: *STEP_COMPOSER_TESTS_UPDATE - <<: *STEP_EXPORT_CI_ENV - <<: *STEP_DISABLE_XDEBUG + - <<: *STEP_SUBSTITUTE_SNAPSHOT_DIR - <<: *STEP_AWAIT_LINK_EXTENSION - install_extension: lib_curl_command: << parameters.lib_curl_command >> @@ -1924,8 +1935,8 @@ jobs: - <<: *STEP_WAIT_REQUEST_REPLAYER - <<: *STEP_WAIT_TEST_AGENT - run: - name: Run << parameters.integration_testsuite >> integration test - command: DD_TRACE_AGENT_TIMEOUT=1000 <<# parameters.disable_runner_distributed_tracing >> DD_DISTRIBUTED_TRACING=false <> DD_TRACE_TEST_SAPI=<< parameters.sapi >> make << parameters.integration_testsuite >> RUST_DEBUG_BUILD=1 + name: Run << parameters.make_target >> integration tests + command: DD_TRACE_AGENT_TIMEOUT=1000 <<# parameters.disable_runner_distributed_tracing >> DD_DISTRIBUTED_TRACING=false <> DD_TRACE_TEST_SAPI=<< parameters.sapi >> make << parameters.make_target >> RUST_DEBUG_BUILD=1 - run: command: | mkdir -p /tmp/artifacts @@ -3172,7 +3183,7 @@ jobs: - run: name: cargo fetch command: | - SUDO=$(! command -v sudo >/dev/null || echo "sudo") + SUDO=$(! command -v sudo >/dev/null || echo "sudo -E") # On occasion, we've observed the .package-cache being in the cache. # If it's there, it will cause commands to stall, waiting for the file to be released. if [ -e '/rust/cargo/.package-cache' ] ; then @@ -3867,8 +3878,7 @@ jobs: - git_checkout - run: name: Install cbindgen - # cbindgen 0.27 requires Rust 1.74+ - command: cargo install cbindgen@0.26.0 + command: cargo install --version "^0.26" cbindgen - run: name: Regenerate cbindgen headers and command: | @@ -5322,33 +5332,12 @@ workflows: make_target: - test_distributed_tracing - - integration: - # NOTE: test_integrations_phpredis5 is not included in the PHP 8.0 integrations tests because of this bug that - # only shows up in debug builds of PHP (https://github.com/phpredis/phpredis/issues/1869). - # Since we run tests using php debug builds, we have to run test_integrations_phpredis5 in a separate runner - # and switch to a non-debug PHP build. - # Once the fix for https://github.com/phpredis/phpredis/issues/1869 is released, we can remove this additional - # runner and add back again test_integrations_phpredis5 to the PHP 8.0 test suite. - requires: [ 'Prepare Code' ] - with_executor: 'with_redis' - matrix: - parameters: - php_major_minor: - - '8.0' - - '8.1' - - '8.2' - - '8.3' - switch_php_version: - - nts - make_target: - - test_integrations_phpredis5 - - integration_tests: requires: [ 'Prepare Code', 'Compile 7.2 extension for testing', 'Compile rust code for testing' ] name: "PHP 72 web tests with apache (+ opcache)" resource_class: medium+ sapi: apache2handler - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-7.2_buster" php_major_minor: "7.2" - integration_tests: @@ -5356,7 +5345,7 @@ workflows: name: "PHP 72 web tests with nginx + FastCGI" resource_class: medium+ sapi: cgi-fcgi - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-7.2_buster" php_major_minor: "7.2" - integration_tests: @@ -5364,7 +5353,7 @@ workflows: name: "PHP 73 web tests with nginx + FastCGI" resource_class: medium+ sapi: cgi-fcgi - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-7.3_buster" php_major_minor: "7.3" - integration_tests: @@ -5372,7 +5361,7 @@ workflows: name: "PHP 74 web tests with apache (+ opcache)" resource_class: medium+ sapi: apache2handler - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-7.4_buster" php_major_minor: "7.4" - integration_tests: @@ -5380,7 +5369,7 @@ workflows: name: "PHP 74 web tests with nginx + FastCGI" resource_class: medium+ sapi: cgi-fcgi - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-7.4_buster" php_major_minor: "7.4" - integration_tests: @@ -5388,7 +5377,7 @@ workflows: name: "PHP 80 web tests with apache (+ opcache)" resource_class: medium+ sapi: apache2handler - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-8.0_buster" php_major_minor: "8.0" - integration_tests: @@ -5396,7 +5385,7 @@ workflows: name: "PHP 80 web tests with nginx + FastCGI" resource_class: medium+ sapi: cgi-fcgi - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-8.0_buster" php_major_minor: "8.0" - integration_tests: @@ -5404,7 +5393,7 @@ workflows: name: "PHP 81 web tests with nginx + FastCGI" resource_class: medium+ sapi: cgi-fcgi - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-8.1_buster" php_major_minor: "8.1" - integration_tests: @@ -5412,7 +5401,7 @@ workflows: name: "PHP 82 web tests with apache (+ opcache)" resource_class: medium+ sapi: apache2handler - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-8.2_buster" php_major_minor: "8.2" - integration_tests: @@ -5420,7 +5409,7 @@ workflows: name: "PHP 83 web tests with apache (+ opcache)" resource_class: medium+ sapi: apache2handler - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-8.3_buster" php_major_minor: "8.3" - integration_tests: @@ -5428,7 +5417,7 @@ workflows: name: "PHP 83 web tests with nginx + FastCGI" resource_class: medium+ sapi: cgi-fcgi - integration_testsuite: "test_web" + make_target: "test_web" docker_image: "datadog/dd-trace-ci:php-8.3_buster" php_major_minor: "8.3" - integration_tests: @@ -5436,7 +5425,7 @@ workflows: name: "PHP 74 custom autoloaded web tests with nginx + PHP-FPM" resource_class: medium+ sapi: fpm-fcgi - integration_testsuite: "test_web_custom" + make_target: "test_web_custom" docker_image: "datadog/dd-trace-ci:php-7.4_buster" php_major_minor: "7.4" diff --git a/Cargo.lock b/Cargo.lock index 4acfc3da8b..6f23e30ea3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1434,6 +1434,8 @@ name = "datadog-trace-utils" version = "0.0.1" dependencies = [ "anyhow", + "bolero", + "bolero-generator", "bytes", "cargo_metadata", "criterion", diff --git a/Makefile b/Makefile index ade1b2d0c8..074f046978 100644 --- a/Makefile +++ b/Makefile @@ -34,11 +34,13 @@ INI_FILE := $(shell ASAN_OPTIONS=detect_leaks=0 php -i | awk -F"=>" '/Scan this RUN_TESTS_IS_PARALLEL ?= $(shell test $(PHP_MAJOR_MINOR) -ge 74 && echo 1) -RUN_TESTS_CMD := REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJECT_ROOT) USE_TRACKED_ALLOC=1 php -n -d 'memory_limit=-1' $(BUILD_DIR)/run-tests.php $(if $(QUIET_TESTS),,-g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP) $(if $(ASAN), --asan) --show-diff -n -p $(shell which php) -q $(if $(RUN_TESTS_IS_PARALLEL), -j$(shell nproc)) +# shuffle parallel tests to evenly distribute test load, avoiding a batch of 32 tests being request-replayer tests +RUN_TESTS_CMD := REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJECT_ROOT) USE_TRACKED_ALLOC=1 php -n -d 'memory_limit=-1' $(BUILD_DIR)/run-tests.php $(if $(QUIET_TESTS),,-g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP) $(if $(ASAN), --asan) --show-diff -n -p $(shell which php) -q $(if $(RUN_TESTS_IS_PARALLEL), --shuffle -j$(shell nproc)) C_FILES = $(shell find components components-rs ext src/dogstatsd zend_abstract_interface -name '*.c' -o -name '*.h' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_FILES = $(shell find tests/ext -name '*.php*' -o -name '*.inc' -o -name '*.json' -o -name 'CONFLICTS' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) RUST_FILES = $(BUILD_DIR)/Cargo.toml $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{alloc,build-common,ddcommon,ddcommon-ffi,ddsketch,ddtelemetry,ddtelemetry-ffi,ipc,sidecar,sidecar-ffi,spawn_worker,tools/{cc_utils,sidecar_mockgen},trace-*,Cargo.toml} -type f \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/ipc/build.rs" -not -path "*/sidecar-ffi/build.rs") +ALL_OBJECT_FILES = $(C_FILES) $(RUST_FILES) $(BUILD_DIR)/Makefile TEST_OPCACHE_FILES = $(shell find tests/opcache -name '*.php*' -o -name '.gitkeep' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_STUB_FILES = $(shell find tests/ext -type d -name 'stubs' -exec find '{}' -type f \; | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) INIT_HOOK_TEST_FILES = $(shell find tests/C2PHP -name '*.phpt' -o -name '*.inc' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) @@ -104,12 +106,10 @@ $(BUILD_DIR)/run-tests.php: $(if $(ASSUME_COMPILED),, $(BUILD_DIR)/configure) $(BUILD_DIR)/Makefile: $(BUILD_DIR)/configure $(Q) (cd $(BUILD_DIR); ./configure --$(if $(RUST_DEBUG_BUILD),enable,disable)-ddtrace-rust-debug $(if $(ASAN), --enable-ddtrace-sanitize) $(EXTRA_CONFIGURE_OPTIONS)) -all_object_files: $(C_FILES) $(RUST_FILES) $(BUILD_DIR)/Makefile - -$(SO_FILE): $(if $(ASSUME_COMPILED),, all_object_files $(BUILD_DIR)/compile_rust.sh) +$(SO_FILE): $(if $(ASSUME_COMPILED),, $(ALL_OBJECT_FILES) $(BUILD_DIR)/compile_rust.sh) $(if $(ASSUME_COMPILED),,$(Q) $(MAKE) -C $(BUILD_DIR) -j) -$(AR_FILE): all_object_files +$(AR_FILE): $(ALL_OBJECT_FILES) $(Q) $(MAKE) -C $(BUILD_DIR) -j ./modules/ddtrace.a all $(PHP_EXTENSION_DIR)/ddtrace.so: $(SO_FILE) @@ -336,7 +336,7 @@ clean: if [[ -f "$(BUILD_DIR)/Makefile" ]]; then $(MAKE) -C $(BUILD_DIR) clean; fi rm -f $(BUILD_DIR)/configure* rm -f $(SO_FILE) - rm -f composer.lock + rm -f composer.lock composer.lock-php$(PHP_MAJOR_MINOR) echo $(ZAI_BUILD_DIR) sudo: @@ -496,7 +496,6 @@ TEST_EXTRA_ENV ?= ### DDTrace tests ### TESTS_ROOT = ./tests COMPOSER = $(if $(ASAN), ASAN_OPTIONS=detect_leaks=0) COMPOSER_MEMORY_LIMIT=-1 composer --no-interaction -COMPOSER_TESTS = $(COMPOSER) --working-dir=$(TESTS_ROOT) DDPROF_IDENTIFIER ?= PHPUNIT_OPTS ?= PHPUNIT = $(TESTS_ROOT)/vendor/bin/phpunit $(PHPUNIT_OPTS) --config=$(TESTS_ROOT)/phpunit.xml @@ -524,8 +523,7 @@ TEST_INTEGRATIONS_70 := \ test_integrations_phpredis4 \ test_integrations_phpredis5 \ test_integrations_predis1 \ - test_integrations_sqlsrv \ - test_opentracing_beta5 + test_integrations_sqlsrv TEST_WEB_70 := \ test_metrics \ @@ -568,8 +566,6 @@ TEST_INTEGRATIONS_71 := \ test_integrations_phpredis5 \ test_integrations_predis1 \ test_integrations_sqlsrv \ - test_opentracing_beta5 \ - test_opentracing_beta6 \ test_opentracing_10 TEST_WEB_71 := \ @@ -623,8 +619,6 @@ TEST_INTEGRATIONS_72 := \ test_integrations_phpredis5 \ test_integrations_predis1 \ test_integrations_sqlsrv \ - test_opentracing_beta5 \ - test_opentracing_beta6 \ test_opentracing_10 TEST_WEB_72 := \ @@ -683,8 +677,6 @@ TEST_INTEGRATIONS_73 :=\ test_integrations_phpredis5 \ test_integrations_predis1 \ test_integrations_sqlsrv \ - test_opentracing_beta5 \ - test_opentracing_beta6 \ test_opentracing_10 TEST_WEB_73 := \ @@ -745,8 +737,6 @@ TEST_INTEGRATIONS_74 := \ test_integrations_predis1 \ test_integrations_roadrunner \ test_integrations_sqlsrv \ - test_opentracing_beta5 \ - test_opentracing_beta6 \ test_opentracing_10 TEST_WEB_74 := \ @@ -787,8 +777,6 @@ TEST_WEB_74 := \ # NOTE: test_integrations_phpredis5 is not included in the PHP 8.0 integrations tests because of this bug that only # shows up in debug builds of PHP (https://github.com/phpredis/phpredis/issues/1869). # Since we run tests in CI using php debug builds, we run test_integrations_phpredis5 in a separate non-debug container. -# Once the fix for https://github.com/phpredis/phpredis/issues/1869 is released, we can remove that additional container -# and add back again test_integrations_phpredis5 to the PHP 8.0 test suite. TEST_INTEGRATIONS_80 := \ test_integrations_deferred_loading \ test_integrations_amqp2 \ @@ -808,6 +796,7 @@ TEST_INTEGRATIONS_80 := \ test_integrations_guzzle6 \ test_integrations_guzzle7 \ test_integrations_pcntl \ + test_integrations_phpredis5 \ test_integrations_predis1 \ test_integrations_sqlsrv \ test_integrations_swoole_5 \ @@ -858,6 +847,7 @@ TEST_INTEGRATIONS_81 := \ test_integrations_pcntl \ test_integrations_pdo \ test_integrations_elasticsearch7 \ + test_integrations_phpredis5 \ test_integrations_predis1 \ test_integrations_sqlsrv \ test_integrations_swoole_5 \ @@ -910,6 +900,7 @@ TEST_INTEGRATIONS_82 := \ test_integrations_pdo \ test_integrations_elasticsearch7 \ test_integrations_elasticsearch8 \ + test_integrations_phpredis5 \ test_integrations_predis1 \ test_integrations_frankenphp \ test_integrations_roadrunner \ @@ -969,6 +960,7 @@ TEST_INTEGRATIONS_83 := \ test_integrations_pdo \ test_integrations_elasticsearch7 \ test_integrations_elasticsearch8 \ + test_integrations_phpredis5 \ test_integrations_predis1 \ test_integrations_frankenphp \ test_integrations_roadrunner \ @@ -1042,13 +1034,19 @@ endef define run_benchmarks - $(ENV_OVERRIDE) php $(TEST_EXTRA_INI) $(TRACER_SOURCES_INI) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console + $(ENV_OVERRIDE) php -d extension=redis-5.3.7.so $(TEST_EXTRA_INI) $(TRACER_SOURCES_INI) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console endef define run_benchmarks_with_ddprof - $(ENV_OVERRIDE) ddprof -S $(DDPROF_IDENTIFIER) php $(TEST_EXTRA_INI) $(REQUEST_INIT_HOOK) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console + $(ENV_OVERRIDE) ddprof -S $(DDPROF_IDENTIFIER) php -d extension=redis-5.3.7.so $(TEST_EXTRA_INI) $(REQUEST_INIT_HOOK) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console endef +define run_composer_with_lock + rm $1/composer.lock-php* 2>/dev/null || true + $(call run_composer_with_retry,$1,) + find $1/vendor* \( -name Tests -prune -o -name tests -prune \) -exec rm -rf '{}' \; + touch $1/composer.lock-php$(PHP_MAJOR_MINOR) +endef # use this as the first target if you want to use uncompiled files instead of the _generated_*.php compiled file. dev: @@ -1059,25 +1057,14 @@ use_generated: $(Q) : $(Q) $(eval ENV_OVERRIDE:=$(ENV_OVERRIDE) DD_AUTOLOAD_NO_COMPILE=) -clean_test: clean_test_scenarios - rm -rf $(TESTS_ROOT)/composer.lock $(TESTS_ROOT)/.scenarios.lock $(TESTS_ROOT)/vendor +clean_test: + find $(TESTS_ROOT)/ -not \( -name "Frameworks" -prune \) -not \( -name "ext" -prune \) -not \( -name "randomized" -prune \) -name "composer.lock" -o -name "vendor" -print -exec rm -rf {} \; find $(TESTS_ROOT)/Frameworks/ -path "*/vendor/*" -prune -o -wholename "*/cache/*.php" -print -exec rm -rf {} \; -clean_test_scenarios: - $(TESTS_ROOT)/clean-composer-scenario-locks.sh - -COMPOSER_PHP_LOCK = $(TESTS_ROOT)/composer.lock.php$(PHP_MAJOR_MINOR) -$(COMPOSER_PHP_LOCK): - $(Q) touch $(COMPOSER_PHP_LOCK) - -$(TESTS_ROOT)/composer.lock: $(TESTS_ROOT)/composer.json $(COMPOSER_PHP_LOCK) - $(Q) find "$(TESTS_ROOT)" -maxdepth 1 -name 'composer.lock*' -not -wholename "$(COMPOSER_PHP_LOCK)" -delete - $(COMPOSER_TESTS) update - composer_tests_update: - $(COMPOSER_TESTS) update + $(call run_composer_with_lock,$(TESTS_ROOT)) -global_test_run_dependencies: install_all $(TESTS_ROOT)/composer.lock +global_test_run_dependencies: install_all $(TESTS_ROOT)/./composer.lock-php$(PHP_MAJOR_MINOR) test_all: \ test_unit \ @@ -1122,12 +1109,8 @@ test_distributed_tracing_coverage: test_metrics: global_test_run_dependencies $(call run_tests,--testsuite=metrics $(TESTS)) -benchmarks_run_dependencies: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_5_2,) +benchmarks_run_dependencies: global_test_run_dependencies tests/Frameworks/Symfony/Version_5_2/composer.lock-php$(PHP_MAJOR_MINOR) tests/Frameworks/Laravel/Version_8_x/composer.lock-php$(PHP_MAJOR_MINOR) tests/Benchmarks/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_5_2/bin/console cache:clear --no-warmup --env=prod - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_8_x,) - rm -f tests/.scenarios.lock/benchmarks/composer.lock - $(MAKE) test_scenario_benchmarks call_benchmarks: if [ -n "$(DDPROF_IDENTIFIER)" ]; then \ @@ -1147,25 +1130,12 @@ benchmarks: benchmarks_run_dependencies call_benchmarks benchmarks_opcache: benchmarks_run_dependencies call_benchmarks_opcache -test_opentelemetry_1: global_test_run_dependencies - rm -f tests/.scenarios.lock/opentelemetry1/composer.lock - $(MAKE) test_scenario_opentelemetry1 - $(call run_composer_with_retry,tests/Frameworks/Custom/OpenTelemetry,) +test_opentelemetry_1: global_test_run_dependencies tests/Frameworks/Custom/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) tests/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) $(eval TEST_EXTRA_ENV=$(shell [ $(PHP_MAJOR_MINOR) -ge 81 ] && echo "OTEL_PHP_FIBERS_ENABLED=1" || echo '') DD_TRACE_OTEL_ENABLED=1 DD_TRACE_GENERATE_ROOT_SPAN=0) $(call run_tests,--testsuite=opentelemetry1 $(TESTS)) $(eval TEST_EXTRA_ENV=) -test_opentracing_beta5: global_test_run_dependencies - $(MAKE) test_scenario_opentracing_beta5 - $(call run_tests,tests/OpenTracerUnit) - -test_opentracing_beta6: global_test_run_dependencies - $(MAKE) test_scenario_opentracing_beta6 - $(call run_tests,tests/OpenTracerUnit) - -test_opentracing_10: global_test_run_dependencies - $(MAKE) test_scenario_opentracing10 - $(call run_composer_with_retry,tests/Frameworks/Custom/OpenTracing,) +test_opentracing_10: global_test_run_dependencies tests/OpenTracer1Unit/composer.lock-php$(PHP_MAJOR_MINOR) tests/Frameworks/Custom/OpenTracing/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests,tests/OpenTracer1Unit) $(call run_tests,tests/OpenTracing) @@ -1177,200 +1147,144 @@ test_web_coverage: test_integrations_coverage: PHPUNIT_COVERAGE=1 $(MAKE) test_integrations -test_integrations_amqp2: global_test_run_dependencies - $(MAKE) test_scenario_amqp2 - $(call run_tests_debug,tests/Integrations/AMQP) -test_integrations_amqp35: global_test_run_dependencies - $(MAKE) test_scenario_amqp35 - $(call run_tests_debug,tests/Integrations/AMQP) -test_integrations_deferred_loading: global_test_run_dependencies - $(MAKE) test_scenario_predis1 +test_integrations_amqp2: global_test_run_dependencies tests/Integrations/AMQP/V2/composer.lock-php$(PHP_MAJOR_MINOR) + $(call run_tests_debug,tests/Integrations/AMQP/V2) +test_integrations_amqp35: global_test_run_dependencies tests/Integrations/AMQP/V3_5/composer.lock-php$(PHP_MAJOR_MINOR) + $(call run_tests_debug,tests/Integrations/AMQP/V3_5) +test_integrations_deferred_loading: global_test_run_dependencies tests/Integrations/Predis/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/DeferredLoading) test_integrations_curl: global_test_run_dependencies $(call run_tests_debug,tests/Integrations/Curl) -test_integrations_elasticsearch1: global_test_run_dependencies - $(MAKE) test_scenario_elasticsearch1 +test_integrations_elasticsearch1: global_test_run_dependencies tests/Integrations/Elasticsearch/V1/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Elasticsearch/V1) -test_integrations_elasticsearch7: global_test_run_dependencies - $(MAKE) test_scenario_elasticsearch7 - $(call run_tests_debug,tests/Integrations/Elasticsearch/V1) -test_integrations_elasticsearch8: global_test_run_dependencies - $(MAKE) test_scenario_elasticsearch8 +test_integrations_elasticsearch7: global_test_run_dependencies tests/Integrations/Elasticsearch/V7/composer.lock-php$(PHP_MAJOR_MINOR) + $(call run_tests_debug,tests/Integrations/Elasticsearch/V7) +test_integrations_elasticsearch8: global_test_run_dependencies tests/Integrations/Elasticsearch/V8/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Elasticsearch/V8) -test_integrations_guzzle5: global_test_run_dependencies - $(MAKE) test_scenario_guzzle5 +test_integrations_guzzle5: global_test_run_dependencies tests/Integrations/Guzzle/V5/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Guzzle/V5) -test_integrations_guzzle6: global_test_run_dependencies - $(MAKE) test_scenario_guzzle6 +test_integrations_guzzle6: global_test_run_dependencies tests/Integrations/Guzzle/V6/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Guzzle/V6) -test_integrations_guzzle7: global_test_run_dependencies - $(MAKE) test_scenario_guzzle7 +test_integrations_guzzle7: global_test_run_dependencies tests/Integrations/Guzzle/V7/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Guzzle/V7) -test_integrations_laminaslog2: global_test_run_dependencies - $(MAKE) test_scenario_laminaslog2 +test_integrations_laminaslog2: global_test_run_dependencies tests/Integrations/Logs/LaminasLogV2/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Logs/LaminasLogV2) test_integrations_memcached: global_test_run_dependencies - $(MAKE) test_scenario_default $(call run_tests_debug,tests/Integrations/Memcached) test_integrations_memcache: global_test_run_dependencies - $(MAKE) test_scenario_default $(call run_tests_debug,tests/Integrations/Memcache) -test_integrations_monolog1: global_test_run_dependencies - $(MAKE) test_scenario_monolog1 +test_integrations_monolog1: global_test_run_dependencies tests/Integrations/Logs/MonologV1/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Logs/MonologV1) -test_integrations_monolog2: global_test_run_dependencies - $(MAKE) test_scenario_monolog2 +test_integrations_monolog2: global_test_run_dependencies tests/Integrations/Logs/MonologV2/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Logs/MonologV2) -test_integrations_monolog3: global_test_run_dependencies - $(MAKE) test_scenario_monolog3 +test_integrations_monolog3: global_test_run_dependencies tests/Integrations/Logs/MonologV3/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Logs/MonologV3) test_integrations_mysqli: global_test_run_dependencies - $(MAKE) test_scenario_default $(call run_tests_debug,tests/Integrations/Mysqli) test_integrations_mongo: global_test_run_dependencies - $(MAKE) test_scenario_default $(call run_tests_debug,tests/Integrations/Mongo) -test_integrations_mongodb1: - $(MAKE) test_scenario_mongodb1 +test_integrations_mongodb1: global_test_run_dependencies tests/Integrations/MongoDB/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/MongoDB) -test_integrations_openai: - $(MAKE) test_scenario_openai +test_integrations_openai: global_test_run_dependencies tests/Integrations/OpenAI/composer.lock-php$(PHP_MAJOR_MINOR) $(eval TELEMETRY_ENABLED=1) $(call run_tests_debug,tests/Integrations/OpenAI) $(eval TELEMETRY_ENABLED=0) test_integrations_pcntl: global_test_run_dependencies $(call run_tests_debug,tests/Integrations/PCNTL) test_integrations_pdo: global_test_run_dependencies - $(MAKE) test_scenario_default $(call run_tests_debug,tests/Integrations/PDO) test_integrations_phpredis3: global_test_run_dependencies - $(MAKE) test_scenario_phpredis3 + $(eval TEST_EXTRA_INI=-d extension=redis-3.1.6.so) $(call run_tests_debug,tests/Integrations/PHPRedis/V3) + $(eval TEST_EXTRA_INI=) test_integrations_phpredis4: global_test_run_dependencies - $(MAKE) test_scenario_phpredis4 + $(eval TEST_EXTRA_INI=-d extension=redis-4.3.0.so) $(call run_tests_debug,tests/Integrations/PHPRedis/V4) + $(eval TEST_EXTRA_INI=) test_integrations_phpredis5: global_test_run_dependencies - $(MAKE) test_scenario_phpredis5 + $(eval TEST_EXTRA_ENV=DD_IGNORE_ARGINFO_ZPP_CHECK=1) + $(eval TEST_EXTRA_INI=-d extension=redis-5.3.7.so) $(call run_tests_debug,tests/Integrations/PHPRedis/V5) -test_integrations_predis1: global_test_run_dependencies - $(MAKE) test_scenario_predis1 + $(eval TEST_EXTRA_INI=) + $(eval TEST_EXTRA_ENV=) +test_integrations_predis1: global_test_run_dependencies tests/Integrations/Predis/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Predis) test_integrations_frankenphp: global_test_run_dependencies - $(MAKE) test_scenario_default $(call run_tests_debug,--testsuite=frankenphp-test) -test_integrations_roadrunner: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Roadrunner/Version_2,) +test_integrations_roadrunner: global_test_run_dependencies tests/Frameworks/Roadrunner/Version_2/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Roadrunner/V2) test_integrations_sqlsrv: global_test_run_dependencies - $(MAKE) test_scenario_default $(call run_tests_debug,tests/Integrations/SQLSRV) test_integrations_swoole_5: global_test_run_dependencies - $(MAKE) test_scenario_swoole5 $(call run_tests_debug,--testsuite=swoole-test) -test_web_cakephp_28: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/CakePHP/Version_2_8,) +test_web_cakephp_28: global_test_run_dependencies tests/Frameworks/CakePHP/Version_2_8/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=cakephp-28-test) -test_web_cakephp_310: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/CakePHP/Version_3_10,) +test_web_cakephp_310: global_test_run_dependencies tests/Frameworks/CakePHP/Version_3_10/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=cakephp-310-test) -test_web_cakephp_45: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/CakePHP/Version_4_5,) +test_web_cakephp_45: global_test_run_dependencies tests/Frameworks/CakePHP/Version_4_5/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=cakephp-45-test) -test_web_cakephp_50: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/CakePHP/Version_5_0,) +test_web_cakephp_50: global_test_run_dependencies tests/Frameworks/CakePHP/Version_5_0/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=cakephp-50-test) test_web_codeigniter_22: global_test_run_dependencies $(call run_tests_debug,--testsuite=codeigniter-22-test) -test_web_codeigniter_31: global_test_run_dependencies - $(COMPOSER) --working-dir=tests/Frameworks/CodeIgniter/Version_3_1 update +test_web_codeigniter_31: global_test_run_dependencies tests/Frameworks/CodeIgniter/Version_3_1/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=codeigniter-31-test) -test_web_drupal_89: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Drupal/Version_8_9/core,--ignore-platform-reqs) - $(call run_composer_with_retry,tests/Frameworks/Drupal/Version_8_9,--ignore-platform-reqs) +test_web_drupal_89: global_test_run_dependencies tests/Frameworks/Drupal/Version_8_9/core/composer.lock-php tests/Frameworks/Drupal/Version_8_9/composer.lock-php $(call run_tests_debug,tests/Integrations/Drupal/V8_9) -test_web_drupal_95: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Drupal/Version_9_5/core,--ignore-platform-reqs) - $(call run_composer_with_retry,tests/Frameworks/Drupal/Version_9_5,--ignore-platform-reqs) +test_web_drupal_95: global_test_run_dependencies tests/Frameworks/Drupal/Version_9_5/core/composer.lock-php tests/Frameworks/Drupal/Version_9_5/composer.lock-php $(call run_tests_debug,tests/Integrations/Drupal/V9_5) -test_web_drupal_101: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Drupal/Version_10_1/core,--ignore-platform-reqs) - $(call run_composer_with_retry,tests/Frameworks/Drupal/Version_10_1,--ignore-platform-reqs) +test_web_drupal_101: global_test_run_dependencies tests/Frameworks/Drupal/Version_10_1/core/composer.lock-php tests/Frameworks/Drupal/Version_10_1/composer.lock-php $(call run_tests_debug,tests/Integrations/Drupal/V10_1) -test_web_laminas_rest_19: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laminas/ApiTools/Version_1_9,) +test_web_laminas_rest_19: global_test_run_dependencies tests/Frameworks/Laminas/ApiTools/Version_1_9/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Laminas/ApiTools/V1_9) -test_web_laminas_14: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laminas/Version_1_4,) +test_web_laminas_14: global_test_run_dependencies tests/Frameworks/Laminas/Version_1_4/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Laminas/V1_4) -test_web_laminas_20: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laminas/Version_2_0,) +test_web_laminas_20: global_test_run_dependencies tests/Frameworks/Laminas/Version_2_0/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Laminas/V2_0) -test_web_laravel_42: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_4_2,) +test_web_laravel_42: global_test_run_dependencies tests/Frameworks/Laravel/Version_4_2/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Laravel/Version_4_2/artisan optimize $(call run_tests_debug,tests/Integrations/Laravel/V4) -test_web_laravel_57: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_5_7,) +test_web_laravel_57: global_test_run_dependencies tests/Frameworks/Laravel/Version_5_7/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Laravel/V5_7) -test_web_laravel_58: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_5_8,) +test_web_laravel_58: global_test_run_dependencies tests/Frameworks/Laravel/Version_5_8/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=laravel-58-test) -test_web_laravel_8x: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_8_x,) +test_web_laravel_8x: global_test_run_dependencies tests/Frameworks/Laravel/Version_8_x/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=laravel-8x-test) -test_web_laravel_9x: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_9_x,) +test_web_laravel_9x: global_test_run_dependencies tests/Frameworks/Laravel/Version_9_x/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=laravel-9x-test) -test_web_laravel_10x: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_10_x,) +test_web_laravel_10x: global_test_run_dependencies tests/Frameworks/Laravel/Version_10_x/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=laravel-10x-test) -test_web_laravel_11x: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Laravel/Version_11_x,) +test_web_laravel_11x: global_test_run_dependencies tests/Frameworks/Laravel/Version_11_x/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=laravel-11x-test) -test_web_laravel_octane: global_test_run_dependencies - $(MAKE) test_scenario_swoole5 - $(call run_composer_with_retry,tests/Frameworks/Laravel/Octane,) +test_web_laravel_octane: global_test_run_dependencies tests/Frameworks/Laravel/Octane/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=laravel-octane-test) -test_web_lumen_52: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Lumen/Version_5_2,) +test_web_lumen_52: global_test_run_dependencies tests/Frameworks/Lumen/Version_5_2/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Lumen/V5_2) -test_web_lumen_56: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Lumen/Version_5_6,) +test_web_lumen_56: global_test_run_dependencies tests/Frameworks/Lumen/Version_5_6/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Lumen/V5_6) -test_web_lumen_58: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Lumen/Version_5_8,) +test_web_lumen_58: global_test_run_dependencies tests/Frameworks/Lumen/Version_5_8/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Lumen/V5_8) -test_web_lumen_81: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Lumen/Version_8_1,) +test_web_lumen_81: global_test_run_dependencies tests/Frameworks/Lumen/Version_8_1/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Lumen/V8_1) -test_web_lumen_90: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Lumen/Version_9_0,) +test_web_lumen_90: global_test_run_dependencies tests/Frameworks/Lumen/Version_9_0/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Lumen/V9_0) -test_web_lumen_100: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Lumen/Version_10_0,) +test_web_lumen_100: global_test_run_dependencies tests/Frameworks/Lumen/Version_10_0/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Lumen/V10_0) -test_web_slim_312: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Slim/Version_3_12,) +test_web_slim_312: global_test_run_dependencies tests/Frameworks/Slim/Version_3_12/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=slim-312-test) -test_web_slim_4: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Slim/Version_4,) +test_web_slim_4: global_test_run_dependencies tests/Frameworks/Slim/Version_4/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=slim-4-test) -test_web_symfony_23: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_2_3,) +test_web_symfony_23: global_test_run_dependencies tests/Frameworks/Symfony/Version_2_3/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Symfony/V2_3) -test_web_symfony_28: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_2_8,) +test_web_symfony_28: global_test_run_dependencies tests/Frameworks/Symfony/Version_2_8/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Symfony/V2_8) -test_web_symfony_30: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_3_0,) +test_web_symfony_30: global_test_run_dependencies tests/Frameworks/Symfony/Version_3_0/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_3_0/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,tests/Integrations/Symfony/V3_0) -test_web_symfony_33: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_3_3,) +test_web_symfony_33: global_test_run_dependencies tests/Frameworks/Symfony/Version_3_3/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_3_3/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,tests/Integrations/Symfony/V3_3) -test_web_symfony_34: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_3_4,) +test_web_symfony_34: global_test_run_dependencies tests/Frameworks/Symfony/Version_3_4/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_3_4/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,tests/Integrations/Symfony/V3_4) test_web_symfony_40: global_test_run_dependencies @@ -1379,32 +1293,26 @@ test_web_symfony_40: global_test_run_dependencies $(COMPOSER) --working-dir=tests/Frameworks/Symfony/Version_4_0 install --no-dev php tests/Frameworks/Symfony/Version_4_0/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,tests/Integrations/Symfony/V4_0) -test_web_symfony_42: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_4_2,) +test_web_symfony_42: global_test_run_dependencies tests/Frameworks/Symfony/Version_4_2/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_4_2/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,tests/Integrations/Symfony/V4_2) -test_web_symfony_44: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_4_4,) +test_web_symfony_44: global_test_run_dependencies tests/Frameworks/Symfony/Version_4_4/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_4_4/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,--testsuite=symfony-44-test) test_web_symfony_50: global_test_run_dependencies $(COMPOSER) --working-dir=tests/Frameworks/Symfony/Version_5_0 install # EOL; install from lock php tests/Frameworks/Symfony/Version_5_0/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,tests/Integrations/Symfony/V5_0) -test_web_symfony_51: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_5_1,) +test_web_symfony_51: global_test_run_dependencies tests/Frameworks/Symfony/Version_5_1/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_5_1/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,tests/Integrations/Symfony/V5_1) -test_web_symfony_52: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_5_2,) +test_web_symfony_52: global_test_run_dependencies tests/Frameworks/Symfony/Version_5_2/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_5_2/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,--testsuite=symfony-52-test) -test_web_symfony_62: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_6_2,) +test_web_symfony_62: global_test_run_dependencies tests/Frameworks/Symfony/Version_6_2/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_6_2/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,--testsuite=symfony-62-test) -test_web_symfony_70: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Symfony/Version_7_0,) +test_web_symfony_70: global_test_run_dependencies tests/Frameworks/Symfony/Version_7_0/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_7_0/bin/console cache:clear --no-warmup --env=prod $(call run_tests_debug,--testsuite=symfony-70-test) test_web_wordpress_48: global_test_run_dependencies @@ -1415,31 +1323,29 @@ test_web_wordpress_59: global_test_run_dependencies $(call run_tests_debug,tests/Integrations/WordPress/V5_9) test_web_wordpress_61: global_test_run_dependencies $(call run_tests_debug,tests/Integrations/WordPress/V6_1) -test_web_yii_2: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Yii/Version_2_0,) +test_web_yii_2: global_test_run_dependencies tests/Frameworks/Yii/Version_2_0/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Yii/V2_0) -test_web_magento_23: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Magento/Version_2_3,) +test_web_magento_23: global_test_run_dependencies tests/Frameworks/Magento/Version_2_3/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Magento/V2_3) -test_web_magento_24: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Magento/Version_2_4,) +test_web_magento_24: global_test_run_dependencies tests/Frameworks/Magento/Version_2_4/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Magento/V2_4) -test_web_nette_24: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Nette/Version_2_4,) +test_web_nette_24: global_test_run_dependencies tests/Frameworks/Nette/Version_2_4/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Nette/V2_4) -test_web_nette_30: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Nette/Version_3_0,) +test_web_nette_30: global_test_run_dependencies tests/Frameworks/Nette/Version_3_0/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/Nette/V3_0) test_web_zend_1: global_test_run_dependencies $(call run_tests_debug,tests/Integrations/ZendFramework/V1) test_web_zend_1_21: global_test_run_dependencies $(call run_tests_debug,tests/Integrations/ZendFramework/V1_21) -test_web_custom: global_test_run_dependencies - $(call run_composer_with_retry,tests/Frameworks/Custom/Version_Autoloaded,) +test_web_custom: global_test_run_dependencies tests/Frameworks/Custom/Version_Autoloaded/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,--testsuite=custom-framework-autoloading-test) -test_scenario_%: - $(Q) $(COMPOSER_TESTS) scenario $* +tests/Frameworks/Drupal/%/composer.lock-php: tests/Frameworks/Drupal/%/composer.json + $(call run_composer_with_retry,tests/Frameworks/Drupal/$*,--ignore-platform-reqs) + touch tests/Frameworks/Drupal/$(*)/composer.lock-php + +tests/%/composer.lock-php$(PHP_MAJOR_MINOR): tests/%/composer.json + $(call run_composer_with_lock,tests/$(*)) merge_coverage_reports: php -d memory_limit=-1 $(PHPCOV) merge --clover reports/coverage.xml reports/cov @@ -1455,7 +1361,7 @@ test_internal_api_randomized: $(SO_FILE) $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) php -n -ddatadog.trace.cli_enabled=1 -d extension=$(SO_FILE) tests/internal-api-stress-test.php 2> >(grep -A 1000 ==============) composer.lock: composer.json - $(Q) $(COMPOSER) update + $(call run_composer_with_retry,,) .PHONY: dev dist_clean clean cores all clang_format_check clang_format_fix install sudo_install test_c test_c_mem test_extension_ci test_zai test_zai_asan test install_ini install_all \ .apk .rpm .deb .tar.gz sudo debug prod strict run-tests.php verify_pecl_file_definitions verify_package_xml cbindgen cbindgen_binary diff --git a/components-rs/common.h b/components-rs/common.h index 63f9792a63..c1ab4e455b 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -514,6 +514,8 @@ struct ddog_Error *ddog_endpoint_from_api_key_and_site(ddog_CharSlice api_key, void ddog_endpoint_set_timeout(struct ddog_Endpoint *endpoint, uint64_t millis); +void ddog_endpoint_set_test_token(struct ddog_Endpoint *endpoint, ddog_CharSlice token); + void ddog_endpoint_drop(struct ddog_Endpoint*); struct ddog_Option_U32 ddog_Option_U32_some(uint32_t v); diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index 9b6696ca82..bb36a55a31 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -229,6 +229,13 @@ ddog_MaybeError ddog_sidecar_dogstatsd_set(struct ddog_SidecarTransport **transp int64_t value, const struct ddog_Vec_Tag *tags); +/** + * Sets x-datadog-test-session-token on all requests for the given session. + */ +ddog_MaybeError ddog_sidecar_set_test_session_token(struct ddog_SidecarTransport **transport, + ddog_CharSlice session_id, + ddog_CharSlice token); + /** * This function creates a new transport using the provided callback function when the current * transport is closed. diff --git a/docker-compose.yml b/docker-compose.yml index 7f7d7c72d5..77916a295f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ x-aliases: - .scenarios.lock:/home/circleci/app/.scenarios.lock - agent-socket:/var/run/datadog - .cargo-registry:/rust/cargo/registry - tmpfs: [ '/home/circleci/app/tmp:uid=3434,gid=3434,exec', '/home/circleci/app/tests/vendor:uid=3434,gid=3434,exec' ] + tmpfs: [ '/home/circleci/app/tmp:uid=3434,gid=3434,exec' ] depends_on: - agent - ddagent_integration @@ -115,6 +115,8 @@ services: command: --default-authentication-plugin=mysql_native_password --sql-mode="NO_ENGINE_SUBSTITUTION" ports: - "3306:3306" + volumes: + - ./dockerfiles/services/mysql:/docker-entrypoint-initdb.d environment: - MYSQL_ROOT_PASSWORD=test - MYSQL_PASSWORD=test diff --git a/dockerfiles/ci/buster/build-extensions.sh b/dockerfiles/ci/buster/build-extensions.sh index 670705aa8b..cd912e1f31 100755 --- a/dockerfiles/ci/buster/build-extensions.sh +++ b/dockerfiles/ci/buster/build-extensions.sh @@ -5,6 +5,7 @@ set -eux SHARED_BUILD=$(if php -i | grep -q enable-pcntl=shared; then echo 1; else echo 0; fi) PHP_VERSION_ID=$(php -r 'echo PHP_MAJOR_VERSION . PHP_MINOR_VERSION;') PHP_ZTS=$(php -r 'echo PHP_ZTS;') +EXTENSION_DIR=$(php-config --extension-dir) XDEBUG_VERSIONS=(-3.1.2) if [[ $PHP_VERSION_ID -le 70 ]]; then @@ -81,14 +82,14 @@ if [[ $SHARED_BUILD -ne 0 ]]; then phpize ./configure make - mv ./modules/*.so $(php-config --extension-dir) + mv ./modules/*.so $EXTENSION_DIR make clean for curlVer in ${CURL_VERSIONS}; do PKG_CONFIG_PATH=/opt/curl/${curlVer}/lib/pkgconfig/ ./configure make - mv ./modules/curl.so $(php-config --extension-dir)/curl-${curlVer}.so + mv ./modules/curl.so $EXTENSION_DIR/curl-${curlVer}.so make clean done phpize --clean @@ -114,8 +115,6 @@ else yes 'no' | pecl install memcached; echo "extension=memcached.so" >> ${iniDir}/memcached.ini; yes '' | pecl install memcache$MEMCACHE_VERSION; echo "extension=memcache.so" >> ${iniDir}/memcache.ini; pecl install mongodb$MONGODB_VERSION; echo "extension=mongodb.so" >> ${iniDir}/mongodb.ini; - # Redis 6.0.0 dropped support for PHP 7.1 and below - pecl install redis$(if [[ $PHP_VERSION_ID -le 71 ]]; then echo -5.3.7; fi); echo "extension=redis.so" >> ${iniDir}/redis.ini; pecl install sqlsrv$SQLSRV_VERSION; echo "extension=sqlsrv.so" >> ${iniDir}/sqlsrv.ini; # Xdebug is disabled by default for VERSION in "${XDEBUG_VERSIONS[@]}"; do @@ -125,9 +124,31 @@ else done echo "zend_extension=opcache.so" >> ${iniDir}/../php-apache2handler.ini; - # ext-parallel needs PHP 8 + # ext-parallel needs PHP 8 ZTS if [[ $PHP_VERSION_ID -ge 80 && $PHP_ZTS -eq 1 ]]; then pecl install parallel; echo "extension=parallel" >> ${iniDir}/parallel.ini; fi + + # ext-swoole needs PHP 8 + if [[ $PHP_VERSION_ID -ge 80 ]]; then + pecl install swoole-5.1.2; # we don't install swoole here + fi + + # We don't install any redis.so to inis, but allow selection at runtime. + if [[ $PHP_VERSION_ID -lt 80 ]]; then + pecl install redis-3.1.6 + mv $EXTENSION_DIR/redis.so $EXTENSION_DIR/redis-3.1.6.so + pecl install redis-4.3.0 + mv $EXTENSION_DIR/redis.so $EXTENSION_DIR/redis-4.3.0.so + fi + pecl install redis-5.3.7 + + # Redis 6.0.0 dropped support for PHP 7.1 and below + if [[ $PHP_VERSION_ID -gt 71 ]]; then + mv $EXTENSION_DIR/redis.so $EXTENSION_DIR/redis-5.3.7.so + pecl install redis + else + ln -s $EXTENSION_DIR/redis.so $EXTENSION_DIR/redis-5.3.7.so + fi fi diff --git a/dockerfiles/ci/buster/php-8.0/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch b/dockerfiles/ci/buster/php-8.0/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch new file mode 100644 index 0000000000..13532cd62c --- /dev/null +++ b/dockerfiles/ci/buster/php-8.0/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch @@ -0,0 +1,27 @@ +From 79314c03bca9b9aff892b3cc1a099b6ae47776fe Mon Sep 17 00:00:00 2001 +From: Bob Weinand +Date: Wed, 7 Aug 2024 22:53:39 +0200 +Subject: [PATCH] Introduce DD_IGNORE_ARGINFO_ZPP_CHECK env var to skip these + assertions in testing + +See e.g. https://github.com/phpredis/phpredis/issues/1869, where phpredis 5 fails these assertions, and it likely will never be fixed there +This allows a simpler approach than having an extra nts-runner just for that test. +--- + Zend/zend_execute.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c +index d5e35898f9..e4df9fa149 100644 +--- a/Zend/zend_execute.c ++++ b/Zend/zend_execute.c +@@ -1142,6 +1142,7 @@ static zend_always_inline zend_bool zend_internal_call_should_throw(zend_functio + + static ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc) + { ++ if (getenv("DD_IGNORE_ARGINFO_ZPP_CHECK")) return; + zend_error(E_ERROR, "Arginfo / zpp mismatch during call of %s%s%s()", + fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", + fbc->common.scope ? "::" : "", +-- +2.41.0 + diff --git a/dockerfiles/ci/buster/php-8.0/Dockerfile b/dockerfiles/ci/buster/php-8.0/Dockerfile index 736c4b8d9b..db8142a801 100644 --- a/dockerfiles/ci/buster/php-8.0/Dockerfile +++ b/dockerfiles/ci/buster/php-8.0/Dockerfile @@ -3,6 +3,7 @@ ARG phpTarGzUrl ARG phpSha256Hash COPY 0001-Better-support-for-cross-compilation.patch /home/circleci COPY 0001-Fix-GH-10611-fpm_env_init_main-leaks-environ.patch /home/circleci +COPY php-8.0/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch /home/circleci RUN set -eux; \ curl -fsSL -o /tmp/php.tar.gz "${phpTarGzUrl}"; \ (echo "${phpSha256Hash} /tmp/php.tar.gz" | sha256sum -c -); \ @@ -10,6 +11,7 @@ RUN set -eux; \ rm -f /tmp/php.tar.gz; \ cd ${PHP_SRC_DIR}; \ git apply /home/circleci/0001-Better-support-for-cross-compilation.patch; \ + git apply /home/circleci/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch; \ patch -p1 /home/circleci/0001-Fix-GH-10611-fpm_env_init_main-leaks-environ.patch; \ ./buildconf --force diff --git a/dockerfiles/ci/buster/php-8.1/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch b/dockerfiles/ci/buster/php-8.1/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch new file mode 100644 index 0000000000..00c13009f4 --- /dev/null +++ b/dockerfiles/ci/buster/php-8.1/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch @@ -0,0 +1,27 @@ +From 90936c193e6c95c1b7acf926434ab2e2adca06a4 Mon Sep 17 00:00:00 2001 +From: Bob Weinand +Date: Wed, 7 Aug 2024 22:53:39 +0200 +Subject: [PATCH] Introduce DD_IGNORE_ARGINFO_ZPP_CHECK env var to skip these + assertions in testing + +See e.g. https://github.com/phpredis/phpredis/issues/1869, where phpredis 5 fails these assertions, and it likely will never be fixed there +This allows a simpler approach than having an extra nts-runner just for that test. +--- + Zend/zend_execute.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c +index 5c9a59bb95..8e3c9d0ca7 100644 +--- a/Zend/zend_execute.c ++++ b/Zend/zend_execute.c +@@ -1196,6 +1196,7 @@ ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_d + + ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc) + { ++ if (getenv("DD_IGNORE_ARGINFO_ZPP_CHECK")) return; + zend_error(E_ERROR, "Arginfo / zpp mismatch during call of %s%s%s()", + fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", + fbc->common.scope ? "::" : "", +-- +2.41.0 + diff --git a/dockerfiles/ci/buster/php-8.1/Dockerfile b/dockerfiles/ci/buster/php-8.1/Dockerfile index dc55cb9091..e2bab90455 100644 --- a/dockerfiles/ci/buster/php-8.1/Dockerfile +++ b/dockerfiles/ci/buster/php-8.1/Dockerfile @@ -6,12 +6,14 @@ ENV PHP_VERSION=${phpVersion} FROM --platform=$BUILDPLATFORM datadog/dd-trace-ci:buster as src ARG phpTarGzUrl ARG phpSha256Hash +COPY php-8.1/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch /home/circleci RUN set -eux; \ curl -fsSL -o /tmp/php.tar.gz "${phpTarGzUrl}"; \ (echo "${phpSha256Hash} /tmp/php.tar.gz" | sha256sum -c -); \ tar xf /tmp/php.tar.gz -C "${PHP_SRC_DIR}" --strip-components=1; \ rm -f /tmp/php.tar.gz; \ cd ${PHP_SRC_DIR}; \ + git apply /home/circleci/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch; \ ./buildconf --force; FROM --platform=$BUILDPLATFORM datadog/dd-trace-ci:buster AS build diff --git a/dockerfiles/ci/buster/php-8.2/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch b/dockerfiles/ci/buster/php-8.2/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch new file mode 100644 index 0000000000..fa60fbf5fa --- /dev/null +++ b/dockerfiles/ci/buster/php-8.2/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch @@ -0,0 +1,27 @@ +From 1931d38a5e9234bebae83fa73d8e99a187e05dfb Mon Sep 17 00:00:00 2001 +From: Bob Weinand +Date: Wed, 7 Aug 2024 22:53:39 +0200 +Subject: [PATCH] Introduce DD_IGNORE_ARGINFO_ZPP_CHECK env var to skip these + assertions in testing + +See e.g. https://github.com/phpredis/phpredis/issues/1869, where phpredis 5 fails these assertions, and it likely will never be fixed there +This allows a simpler approach than having an extra nts-runner just for that test. +--- + Zend/zend_execute.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c +index 3de48fb135..6e5a4062e1 100644 +--- a/Zend/zend_execute.c ++++ b/Zend/zend_execute.c +@@ -1237,6 +1237,7 @@ ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_d + + ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc) + { ++ if (getenv("DD_IGNORE_ARGINFO_ZPP_CHECK")) return; + zend_error(E_ERROR, "Arginfo / zpp mismatch during call of %s%s%s()", + fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", + fbc->common.scope ? "::" : "", +-- +2.41.0 + diff --git a/dockerfiles/ci/buster/php-8.2/Dockerfile b/dockerfiles/ci/buster/php-8.2/Dockerfile index 1ade036ba8..7fbb88ba7c 100644 --- a/dockerfiles/ci/buster/php-8.2/Dockerfile +++ b/dockerfiles/ci/buster/php-8.2/Dockerfile @@ -7,6 +7,7 @@ FROM --platform=$BUILDPLATFORM datadog/dd-trace-ci:buster as src ARG phpTarGzUrl ARG phpSha256Hash COPY 0001-Delete-timers-on-fork.patch /home/circleci +COPY php-8.2/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch /home/circleci RUN set -eux; \ curl -fsSL -o /tmp/php.tar.gz "${phpTarGzUrl}"; \ (echo "${phpSha256Hash} /tmp/php.tar.gz" | sha256sum -c -); \ @@ -14,6 +15,7 @@ RUN set -eux; \ rm -f /tmp/php.tar.gz; \ cd ${PHP_SRC_DIR}; \ ./buildconf --force; \ + git apply /home/circleci/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch; \ patch Zend/zend_max_execution_timer.c /home/circleci/0001-Delete-timers-on-fork.patch; FROM --platform=$BUILDPLATFORM datadog/dd-trace-ci:buster AS build diff --git a/dockerfiles/ci/buster/php-8.3/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch b/dockerfiles/ci/buster/php-8.3/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch new file mode 100644 index 0000000000..d7d34ab937 --- /dev/null +++ b/dockerfiles/ci/buster/php-8.3/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch @@ -0,0 +1,27 @@ +From 16d2a319a36e90cf1bd3f0d3f3732c8035e81c02 Mon Sep 17 00:00:00 2001 +From: Bob Weinand +Date: Wed, 7 Aug 2024 22:53:39 +0200 +Subject: [PATCH] Introduce DD_IGNORE_ARGINFO_ZPP_CHECK env var to skip these + assertions in testing + +See e.g. https://github.com/phpredis/phpredis/issues/1869, where phpredis 5 fails these assertions, and it likely will never be fixed there +This allows a simpler approach than having an extra nts-runner just for that test. +--- + Zend/zend_execute.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c +index 8da2d37a25..869c547be0 100644 +--- a/Zend/zend_execute.c ++++ b/Zend/zend_execute.c +@@ -1297,6 +1297,7 @@ ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_d + + ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc) + { ++ if (getenv("DD_IGNORE_ARGINFO_ZPP_CHECK")) return; + zend_error(E_ERROR, "Arginfo / zpp mismatch during call of %s%s%s()", + fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", + fbc->common.scope ? "::" : "", +-- +2.41.0 + diff --git a/dockerfiles/ci/buster/php-8.3/Dockerfile b/dockerfiles/ci/buster/php-8.3/Dockerfile index 5a14e63534..18e9f1fc77 100644 --- a/dockerfiles/ci/buster/php-8.3/Dockerfile +++ b/dockerfiles/ci/buster/php-8.3/Dockerfile @@ -9,12 +9,14 @@ COPY php-8.3/suppr.txt /home/circleci/suppr.txt FROM --platform=$BUILDPLATFORM datadog/dd-trace-ci:buster as src ARG phpTarGzUrl ARG phpSha256Hash +COPY php-8.3/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch /home/circleci RUN set -eux; \ curl -fsSL -o /tmp/php.tar.gz "${phpTarGzUrl}"; \ (echo "${phpSha256Hash} /tmp/php.tar.gz" | sha256sum -c -); \ tar xf /tmp/php.tar.gz -C "${PHP_SRC_DIR}" --strip-components=1; \ rm -f /tmp/php.tar.gz; \ cd ${PHP_SRC_DIR}; \ + git apply /home/circleci/0001-Introduce-DD_IGNORE_ARGINFO_ZPP_CHECK-env-var-to-ski.patch; \ ./buildconf --force; FROM --platform=$BUILDPLATFORM datadog/dd-trace-ci:buster AS build diff --git a/dockerfiles/services/Makefile b/dockerfiles/services/Makefile index 4161c622ad..281af0a546 100644 --- a/dockerfiles/services/Makefile +++ b/dockerfiles/services/Makefile @@ -1,5 +1,6 @@ Q := @ REDIS_IMAGE := datadog/dd-trace-ci:php-redis-5.0 +MYSQL_IMAGE := datadog/dd-trace-ci:php-mysql-dev-5.6 redis_build: $(Q) docker build -t $(REDIS_IMAGE) redis @@ -7,6 +8,9 @@ redis_build: redis_publish: redis_build $(Q) docker push $(REDIS_IMAGE) +mysql_publish: redis_build + docker buildx build --platform=linux/amd64 -t $(MYSQL_IMAGE) mysql --push + # It requires buildx to be able to build cross-architecture images request-replayer_linux_push: docker buildx build --platform=linux/arm64,linux/amd64 -t datadog/dd-trace-ci:php-request-replayer-2.0 ./request-replayer -f request-replayer/linux.Dockerfile --push diff --git a/dockerfiles/services/mysql/Dockerfile b/dockerfiles/services/mysql/Dockerfile new file mode 100644 index 0000000000..d8b4bdb522 --- /dev/null +++ b/dockerfiles/services/mysql/Dockerfile @@ -0,0 +1,3 @@ +FROM mysql:5.6 + +COPY init.sql /docker-entrypoint-initdb.d/init.sql diff --git a/dockerfiles/services/mysql/init.sql b/dockerfiles/services/mysql/init.sql new file mode 100644 index 0000000000..05e4a16192 --- /dev/null +++ b/dockerfiles/services/mysql/init.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS test; +create table test.users ( + id integer not null primary key auto_increment, + email varchar(100) not null unique, + name varchar(100), + password varchar(100), + remember_token varchar(100), + updated_at timestamp, + created_at timestamp +); + +GRANT ALL PRIVILEGES ON *.* TO 'test'@'%' WITH GRANT OPTION; +FLUSH PRIVILEGES; diff --git a/dockerfiles/services/request-replayer/src/index.php b/dockerfiles/services/request-replayer/src/index.php index 092ecb4455..d1edcfda67 100644 --- a/dockerfiles/services/request-replayer/src/index.php +++ b/dockerfiles/services/request-replayer/src/index.php @@ -12,17 +12,27 @@ exit; } -define('REQUEST_LATEST_DUMP_FILE', getenv('REQUEST_LATEST_DUMP_FILE') ?: (sys_get_temp_dir() . '/dump.json')); -define('REQUEST_NEXT_RESPONSE_FILE', getenv('REQUEST_NEXT_RESPONSE_FILE') ?: (sys_get_temp_dir() . '/response.json')); -define('REQUEST_LOG_FILE', getenv('REQUEST_LOG_FILE') ?: (sys_get_temp_dir() . '/requests-log.txt')); +$temp_location = sys_get_temp_dir(); + +if ("" != $token = ($_SERVER["HTTP_X_DATADOG_TEST_SESSION_TOKEN"] ?? "")) { + $token = str_replace("/", "-", $token); + $temp_location .= "/token-$token"; + @mkdir($temp_location); +} + +define('REQUEST_LATEST_DUMP_FILE', getenv('REQUEST_LATEST_DUMP_FILE') ?: ("$temp_location/dump.json")); +define('REQUEST_NEXT_RESPONSE_FILE', getenv('REQUEST_NEXT_RESPONSE_FILE') ?: ("$temp_location/response.json")); +define('REQUEST_LOG_FILE', getenv('REQUEST_LOG_FILE') ?: ("$temp_location/requests-log.txt")); +define('REQUEST_RC_CONFIGS_FILE', getenv('REQUEST_RC_CONFIGS_FILE') ?: ("$temp_location/rc_configs.json")); function logRequest($message, $data = '') { + global $token; if (!empty($data)) { $message .= ":\n" . $data; } error_log( - sprintf('[%s | %s] %s', $_SERVER['REQUEST_URI'], REQUEST_LATEST_DUMP_FILE, $message) + sprintf('[%s | %s%s] %s', $_SERVER['REQUEST_URI'], REQUEST_LATEST_DUMP_FILE, $token == "" ? "" : " | $token", $message) ); } @@ -31,7 +41,9 @@ function logRequest($message, $data = '') trigger_error($message, $number); }); -switch ($_SERVER['REQUEST_URI']) { +$rc_configs = file_exists(REQUEST_RC_CONFIGS_FILE) ? json_decode(file_get_contents(REQUEST_RC_CONFIGS_FILE), true) : []; + +switch (explode("?", $_SERVER['REQUEST_URI'])[0]) { case '/replay': if (!file_exists(REQUEST_LATEST_DUMP_FILE)) { logRequest('Cannot replay last request; request log does not exist'); @@ -44,21 +56,77 @@ function logRequest($message, $data = '') logRequest('Returned last request and deleted request log', $request); break; case '/clear-dumped-data': - if (!file_exists(REQUEST_LATEST_DUMP_FILE)) { + if (!file_exists(REQUEST_LATEST_DUMP_FILE) && !file_exists(REQUEST_RC_CONFIGS_FILE)) { logRequest('Cannot delete request log; request log does not exist'); break; } - unlink(REQUEST_LATEST_DUMP_FILE); - unlink(REQUEST_LOG_FILE); - if (file_exists(REQUEST_NEXT_RESPONSE_FILE)) { - unlink(REQUEST_NEXT_RESPONSE_FILE); - } + if (file_exists(REQUEST_RC_CONFIGS_FILE)) { + unlink(REQUEST_RC_CONFIGS_FILE); + } + if (file_exists(REQUEST_LATEST_DUMP_FILE)) { + unlink(REQUEST_LATEST_DUMP_FILE); + unlink(REQUEST_LOG_FILE); + } + if (file_exists(REQUEST_NEXT_RESPONSE_FILE)) { + unlink(REQUEST_NEXT_RESPONSE_FILE); + } logRequest('Deleted request log'); break; case '/next-response': $raw = file_get_contents('php://input'); file_put_contents(REQUEST_NEXT_RESPONSE_FILE, $raw); break; + case '/add-rc-config-file': + $rc_configs[$_GET["path"]] = ["service" => $_GET["service"], "data" => file_get_contents('php://input')]; + file_put_contents(REQUEST_RC_CONFIGS_FILE, json_encode($rc_configs, JSON_UNESCAPED_SLASHES)); + break; + case '/del-rc-config-file': + unset($rc_configs[$_GET["path"]]); + file_put_contents(REQUEST_RC_CONFIGS_FILE, json_encode($rc_configs, JSON_UNESCAPED_SLASHES)); + break; + case '/v0.7/config': + $request = file_get_contents('php://input'); + logRequest("Requested remote config", $request); + $request = json_decode($request, true); + $recentUpdate = @filemtime(REQUEST_RC_CONFIGS_FILE) > time() - 2; + $response = [ + "roots" => [], + "targets" => [ + "signatures" => [], + "signed" => [ + "_type" => "targets", + "custom" => [ + "opaque_backend_state" => "foobarbaz", + "agent_refresh_interval" => ($recentUpdate ? 10 : 10000) * 1000000, // in ns + ], + "expires" => "9999-12-31T23:59:59Z", + "spec_version" => "1.0.0", + "targets" => new \StdClass, + "version" => 1, + ], + ], + "target_files" => [], + "client_configs" => [], + ]; + foreach ($rc_configs as $path => $config) { + if ($config["service"] == $request["client"]["client_tracer"]["service"]) { + $content = $config["data"]; + $response["targets"]["signed"]["targets"]->$path = [ + "custom" => ["v" => strlen($path)], + "hashes" => ["sha256" => hash("sha256", $content)], + "length" => strlen($content), + ]; + $response["target_files"][] = [ + "path" => $path, + "raw" => base64_encode($content), + ]; + $response["client_configs"][] = $path; + } + } + logRequest("Returned remote config", json_encode($response, JSON_UNESCAPED_SLASHES)); + $response["targets"] = base64_encode(json_encode($response["targets"], JSON_UNESCAPED_SLASHES)); + echo json_encode($response, JSON_UNESCAPED_SLASHES); + break; default: $headers = getallheaders(); if (isset($headers['X-Datadog-Diagnostic-Check']) || isset($headers['x-datadog-diagnostic-check'])) { @@ -66,52 +134,67 @@ function logRequest($message, $data = '') break; } - $raw = file_get_contents('php://input'); - if ((isset($headers['Content-Type']) && $headers['Content-Type'] === 'application/msgpack') - || (isset($headers['content-type']) && $headers['content-type'] === 'application/msgpack')) { - // We unpack in two phases: - // 1) using UnpackOptions::BIGINT_AS_GMP and only asserting that trace_id, span_id and parent_id are either - // integers (when <= PHP_INT_MAX) or GMP (when > PHP_INT_MAX); - // 2) using UnpackOptions::BIGINT_AS_STR and storing the actual result. - // We cannot use the first unpacked payload as when, later, we json_encode() the payload, GMPs larger than - // PHP_INT_MAX would be serialized to PHP_INT_MAX - $gmpUnpacker = new BufferUnpacker($raw, UnpackOptions::BIGINT_AS_GMP); - $gmpTraces = $gmpUnpacker->unpack(); - foreach (isset($gmpTraces["chunks"]) ? $gmpTraces["chunks"] : $gmpTraces as $trace) { - foreach (isset($trace["spans"]) ? $trace["spans"] : $trace as $span) { - foreach (['trace_id', 'span_id', 'parent_id'] as $field) { - if (!isset($span[$field])) { - continue; - } + $newIncomingRequest = [ + 'uri' => $_SERVER['REQUEST_URI'], + 'headers' => $headers, + ]; - $value = $span[$field]; - if (!is_int($value) && !is_a($value, 'GMP')) { - logRequest("Wrong type for $field: " . var_export($value, 1)); - exit(); + if (!empty($_FILES)) { + $newIncomingRequest["files"] = $_FILES; + foreach ($newIncomingRequest["files"] as &$file) { + $file["contents"] = file_get_contents($file["tmp_name"]); + unset($file["tmp_name"]); + } + } else { + $raw = file_get_contents('php://input'); + if ((isset($headers['Content-Type']) && $headers['Content-Type'] === 'application/msgpack') + || (isset($headers['content-type']) && $headers['content-type'] === 'application/msgpack')) { + // We unpack in two phases: + // 1) using UnpackOptions::BIGINT_AS_GMP and only asserting that trace_id, span_id and parent_id are either + // integers (when <= PHP_INT_MAX) or GMP (when > PHP_INT_MAX); + // 2) using UnpackOptions::BIGINT_AS_STR and storing the actual result. + // We cannot use the first unpacked payload as when, later, we json_encode() the payload, GMPs larger than + // PHP_INT_MAX would be serialized to PHP_INT_MAX + $gmpUnpacker = new BufferUnpacker($raw, UnpackOptions::BIGINT_AS_GMP); + $gmpTraces = $gmpUnpacker->unpack(); + foreach (isset($gmpTraces["chunks"]) ? $gmpTraces["chunks"] : $gmpTraces as $trace) { + foreach (isset($trace["spans"]) ? $trace["spans"] : $trace as $span) { + foreach (['trace_id', 'span_id', 'parent_id'] as $field) { + if (!isset($span[$field])) { + continue; + } + + $value = $span[$field]; + if (!is_int($value) && !is_a($value, 'GMP')) { + logRequest("Wrong type for $field: " . var_export($value, 1)); + exit(); + } } } } - } - $strUnpacker = new BufferUnpacker($raw, UnpackOptions::BIGINT_AS_STR); - $strTraces = $strUnpacker->unpack(); - $traces = isset($strTraces["chunks"]) ? [&$strTraces["chunks"]] : [&$strTraces]; - foreach ($traces[0] as &$trace) { - $spans = isset($trace["spans"]) ? [&$trace["spans"]] : [&$trace]; - foreach ($spans[0] as &$span) { - foreach (['trace_id', 'span_id', 'parent_id'] as $field) { - if (!isset($span[$field])) { - continue; + $strUnpacker = new BufferUnpacker($raw, UnpackOptions::BIGINT_AS_STR); + $strTraces = $strUnpacker->unpack(); + $traces = isset($strTraces["chunks"]) ? [&$strTraces["chunks"]] : [&$strTraces]; + foreach ($traces[0] as &$trace) { + $spans = isset($trace["spans"]) ? [&$trace["spans"]] : [&$trace]; + foreach ($spans[0] as &$span) { + foreach (['trace_id', 'span_id', 'parent_id'] as $field) { + if (!isset($span[$field])) { + continue; + } + + $span[$field] = (string)$span[$field]; } - - $span[$field] = (string)$span[$field]; } } + + $body = json_encode($strTraces); + } else { + $body = $raw; } - $body = json_encode($strTraces); - } else { - $body = $raw; + $newIncomingRequest["body"] = $body; } if (file_exists(REQUEST_LATEST_DUMP_FILE)) { $tracesStack = json_decode(file_get_contents(REQUEST_LATEST_DUMP_FILE), true); @@ -119,12 +202,6 @@ function logRequest($message, $data = '') $tracesStack = []; } - $newIncomingRequest = [ - 'uri' => $_SERVER['REQUEST_URI'], - 'headers' => $headers, - 'body' => $body, - ]; - $tracesStack[] = $newIncomingRequest; $newIncomingRequestJson = json_encode($newIncomingRequest); diff --git a/ext/coms.c b/ext/coms.c index 0ae0263677..d1c3fe4221 100644 --- a/ext/coms.c +++ b/ext/coms.c @@ -805,11 +805,15 @@ static void _dd_curl_set_headers(struct _writer_loop_data_t *writer, size_t trac headers = curl_slist_append(headers, "Transfer-Encoding: chunked"); headers = curl_slist_append(headers, "Content-Type: application/msgpack"); - char buffer[64]; + char buffer[300]; int bytes_written = snprintf(buffer, sizeof buffer, DD_TRACE_COUNT_HEADER "%zu", trace_count); if (bytes_written > ((int)sizeof(DD_TRACE_COUNT_HEADER)) - 1 && bytes_written < ((int)sizeof buffer)) { headers = curl_slist_append(headers, buffer); } + if (*ddtrace_coms_globals.test_session_token) { + sprintf(buffer, "x-datadog-test-session-token: %s", ddtrace_coms_globals.test_session_token); + headers = curl_slist_append(headers, buffer); + } _dd_curl_reset_headers(writer); @@ -1389,6 +1393,16 @@ bool ddtrace_in_writer_thread(void) { return (pthread_self() == writer->thread->self); } +void ddtrace_coms_set_test_session_token(const char *token, size_t token_len) { + if (token_len > 255) { + token_len = 255; + } + // We don't care too much about incorrectness caused by race-conditions here: it's just testing code + // And it won't ever crash as the 255th byte is always 0. + memcpy(ddtrace_coms_globals.test_session_token, token, token_len); + ddtrace_coms_globals.test_session_token[token_len] = 0; +} + /* for testing {{{ */ #define DDTRACE_NUMBER_OF_DATA_TO_WRITE 2000 #define DDTRACE_DATA_TO_WRITE "0123456789" diff --git a/ext/coms.h b/ext/coms.h index d1a521b616..cfb7e1c0aa 100644 --- a/ext/coms.h +++ b/ext/coms.h @@ -45,6 +45,8 @@ typedef struct ddtrace_coms_state_t { /* Whether to send fallback telemetry. */ bool bgs_fallback_telemetry; char initial_service_name[100]; + + char test_session_token[255]; } ddtrace_coms_state_t; inline bool ddtrace_coms_is_stack_unused(ddtrace_coms_stack_t *stack) { return atomic_load(&stack->refcount) == 0; } @@ -61,6 +63,7 @@ void ddtrace_coms_mshutdown(void); void ddtrace_coms_curl_shutdown(void); void ddtrace_coms_rshutdown(void); uint32_t ddtrace_coms_next_group_id(void); +void ddtrace_coms_set_test_session_token(const char *token, size_t token_len); bool ddtrace_coms_init_and_start_writer(void); bool ddtrace_coms_trigger_writer_flush(void); diff --git a/ext/configuration.c b/ext/configuration.c index 347dc9f907..f94763b768 100644 --- a/ext/configuration.c +++ b/ext/configuration.c @@ -5,6 +5,7 @@ #include "ip_extraction.h" #include "logging.h" #include "json/json.h" +#include "sidecar.h" #include #include diff --git a/ext/configuration.h b/ext/configuration.h index 919f5e5eda..3bf8ccfdd7 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -194,6 +194,7 @@ enum ddtrace_sampling_rules_format { CONFIG(INT, DD_TRACE_AGENT_MAX_PAYLOAD_SIZE, "52428800", .ini_change = zai_config_system_ini_change) \ CONFIG(INT, DD_TRACE_AGENT_STACK_INITIAL_SIZE, "131072", .ini_change = zai_config_system_ini_change) \ CONFIG(INT, DD_TRACE_AGENT_STACK_BACKLOG, "12", .ini_change = zai_config_system_ini_change) \ + CONFIG(STRING, DD_TRACE_AGENT_TEST_SESSION_TOKEN, "", .ini_change = ddtrace_alter_test_session_token) \ CONFIG(BOOL, DD_TRACE_PROPAGATE_USER_ID_DEFAULT, "false") \ CONFIG(CUSTOM(INT), DD_DBM_PROPAGATION_MODE, "disabled", .parser = dd_parse_dbm_mode) \ CONFIG(SET, DD_TRACE_WORDPRESS_ADDITIONAL_ACTIONS, "") \ diff --git a/ext/ddtrace.c b/ext/ddtrace.c index 0c85d32110..cc78f7b477 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -435,6 +435,10 @@ static void dd_activate_once(void) { get_global_DD_TRACE_AGENT_MAX_PAYLOAD_SIZE(), get_global_DD_TRACE_AGENT_STACK_BACKLOG(), bgs_fallback ? ZSTR_VAL(bgs_service) : NULL); + zend_string *testing_token = get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN(); + if (ZSTR_LEN(testing_token)) { + ddtrace_coms_set_test_session_token(ZSTR_VAL(testing_token), ZSTR_LEN(testing_token)); + } if (bgs_fallback) { zend_string_release(bgs_service); } diff --git a/ext/sidecar.c b/ext/sidecar.c index d35bd7286c..334df604ab 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -10,6 +10,9 @@ #include "sidecar.h" #include "telemetry.h" #include "serializer.h" +#ifndef _WIN32 +#include "coms.h" +#endif ddog_SidecarTransport *ddtrace_sidecar; ddog_Endpoint *ddtrace_endpoint; @@ -20,13 +23,17 @@ static uint8_t dd_sidecar_formatted_session_id[36]; static void ddtrace_set_non_resettable_sidecar_globals(void) { ddtrace_format_runtime_id(&dd_sidecar_formatted_session_id); - if (get_global_DD_TRACE_AGENTLESS() && ZSTR_LEN(get_global_DD_API_KEY())) { + if (get_global_DD_TRACE_AGENTLESS() && ZSTR_LEN(get_global_DD_API_KEY())) { ddtrace_endpoint = ddog_endpoint_from_api_key(dd_zend_string_to_CharSlice(get_global_DD_API_KEY())); } else { char *agent_url = ddtrace_agent_url(); ddtrace_endpoint = ddog_endpoint_from_url((ddog_CharSlice) {.ptr = agent_url, .len = strlen(agent_url)}); free(agent_url); } + + if (ZSTR_LEN(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())) { + ddog_endpoint_set_test_token(ddtrace_endpoint, dd_zend_string_to_CharSlice(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())); + } } // Set the globals that must be updated in case of fork @@ -53,6 +60,10 @@ ddog_SidecarTransport *dd_sidecar_connection_factory(void) { free(dogstatsd_url); } + if (ZSTR_LEN(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())) { + ddog_endpoint_set_test_token(dogstatsd_endpoint, dd_zend_string_to_CharSlice(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())); + } + #ifdef _WIN32 DDOG_PHP_FUNCTION = (const uint8_t *)zend_hash_func; #endif @@ -264,3 +275,15 @@ void ddtrace_sidecar_dogstatsd_set(zend_string *metric, zend_long value, zval *t ddog_sidecar_dogstatsd_set(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec); ddog_Vec_Tag_drop(vec); } + +bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value) { + UNUSED(old_value); + if (ddtrace_sidecar) { + ddog_CharSlice session_id = (ddog_CharSlice) {.ptr = (char *) dd_sidecar_formatted_session_id, .len = sizeof(dd_sidecar_formatted_session_id)}; + ddog_sidecar_set_test_session_token(&ddtrace_sidecar, session_id, dd_zend_string_to_CharSlice(Z_STR_P(new_value))); + } +#ifndef _WIN32 + ddtrace_coms_set_test_session_token(Z_STRVAL_P(new_value), Z_STRLEN_P(new_value)); +#endif + return true; +} diff --git a/ext/sidecar.h b/ext/sidecar.h index 4ccf350357..37a0498beb 100644 --- a/ext/sidecar.h +++ b/ext/sidecar.h @@ -16,6 +16,8 @@ void ddtrace_sidecar_dogstatsd_gauge(zend_string *metric, double value, zval *ta void ddtrace_sidecar_dogstatsd_histogram(zend_string *metric, double value, zval *tags); void ddtrace_sidecar_dogstatsd_set(zend_string *metric, zend_long value, zval *tags); +bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value); + static inline ddog_CharSlice dd_zend_string_to_CharSlice(zend_string *str) { return (ddog_CharSlice){ .len = str->len, .ptr = str->val }; } diff --git a/libdatadog b/libdatadog index 866b79d450..16528ffee4 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 866b79d450a7305f95d2792a9de199a13631a32d +Subproject commit 16528ffee456f7af5fe9ad80a6294fb5dcd38918 diff --git a/src/DDTrace/Integrations/Roadrunner/RoadrunnerIntegration.php b/src/DDTrace/Integrations/Roadrunner/RoadrunnerIntegration.php index 4f85e9c14b..27b2b8a0bf 100644 --- a/src/DDTrace/Integrations/Roadrunner/RoadrunnerIntegration.php +++ b/src/DDTrace/Integrations/Roadrunner/RoadrunnerIntegration.php @@ -111,7 +111,7 @@ function ($upload) { } private static function getHost(array $parsed_url) { - $port = $parsed_url['port']; + $port = $parsed_url['port'] ?? 80; $scheme = $parsed_url['scheme']; if ($scheme === 'https') { if ($port === 443) { diff --git a/tests/Appsec/Mock.php b/tests/Appsec/Mock.php index cc3002bb34..5333dab7ee 100644 --- a/tests/Appsec/Mock.php +++ b/tests/Appsec/Mock.php @@ -6,6 +6,7 @@ class AppsecStatus { private static $instance = null; + private $connection; protected function __construct() { } @@ -21,7 +22,12 @@ public static function getInstance() protected function getDbPdo() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + if (!isset($this->connection)) { + $pdo = new \PDO('mysql:host=mysql_integration', 'test', 'test'); + $pdo->exec("CREATE DATABASE IF NOT EXISTS test"); + $this->connection = new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + } + return $this->connection; } /** @@ -36,21 +42,15 @@ private function initiated() public function init() { - $this->getDbPdo()->exec("CREATE TABLE IF NOT EXISTS appsec_events (event varchar(1000))"); - } - - public function destroy() - { - $this->getDbPdo()->exec("DROP TABLE appsec_events"); + $this->getDbPdo()->exec("CREATE TABLE IF NOT EXISTS appsec_events (event varchar(1000), token varchar(100))"); } - public function setDefaults() { if (!$this->initiated()) { return; } - $this->getDbPdo()->exec("DELETE FROM appsec_events"); + $this->getDbPdo()->exec("DELETE FROM appsec_events WHERE token = '" . ini_get("datadog.trace.agent_test_session_token") . "'"); } public function addEvent(array $event, $eventName) @@ -60,7 +60,7 @@ public function addEvent(array $event, $eventName) } $event['eventName'] = $eventName; - $this->getDbPdo()->exec(sprintf("INSERT INTO appsec_events VALUES ('%s')", json_encode($event))); + $this->getDbPdo()->exec(sprintf("INSERT INTO appsec_events VALUES ('%s', '%s')", json_encode($event), ini_get("datadog.trace.agent_test_session_token"))); } public function getEvents(array $names = [], array $addresses = []) @@ -71,7 +71,7 @@ public function getEvents(array $names = [], array $addresses = []) return $result; } - $events = $this->getDbPdo()->query("SELECT * FROM appsec_events")->fetchAll(); + $events = $this->getDbPdo()->query("SELECT * FROM appsec_events WHERE token = '" . ini_get("datadog.trace.agent_test_session_token") . "'")->fetchAll(); foreach ($events as $event) { $new = json_decode($event['event'], true); @@ -158,4 +158,4 @@ function push_address($key, $value) { } AppsecStatus::getInstance()->addEvent([$key => $value], 'push_address'); } -} \ No newline at end of file +} diff --git a/tests/Benchmarks/composer.json b/tests/Benchmarks/composer.json new file mode 100644 index 0000000000..962f122d40 --- /dev/null +++ b/tests/Benchmarks/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "monolog/monolog": "~2.0", + "open-telemetry/sdk": "@stable", + "phpbench/phpbench": "^1.0" + } +} diff --git a/tests/Common/AppsecTestCase.php b/tests/Common/AppsecTestCase.php index fb9074c5dc..8edb6521cc 100644 --- a/tests/Common/AppsecTestCase.php +++ b/tests/Common/AppsecTestCase.php @@ -10,6 +10,8 @@ */ abstract class AppsecTestCase extends WebFrameworkTestCase { + private static $connection; + protected static function getEnvs() { return array_merge(parent::getEnvs(), [ @@ -19,7 +21,10 @@ protected static function getEnvs() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + if (!isset(self::$connection)) { + self::$connection = new \PDO('mysql:host=mysql_integration;dbname=' . static::$database, 'test', 'test'); + } + return self::$connection; } protected function databaseDump() @@ -45,7 +50,7 @@ public static function ddSetUpBeforeClass() public static function ddTearDownAfterClass() { - AppsecStatus::getInstance()->destroy(); + AppsecStatus::getInstance()->setDefaults(); parent::ddTearDownAfterClass(); } diff --git a/tests/Common/BaseTestCase.php b/tests/Common/BaseTestCase.php index c2aac693ed..d6b6d29ad9 100644 --- a/tests/Common/BaseTestCase.php +++ b/tests/Common/BaseTestCase.php @@ -18,12 +18,20 @@ */ abstract class BaseTestCase extends MultiPHPUnitVersionAdapter { + public static $activeResourceLock; + public static function ddSetUpBeforeClass() { + if (isset(static::$lockedResource)) { + $lock = fopen("/tmp/ddtrace-phpunit/lock-" . static::$lockedResource, "c+"); + flock($lock, LOCK_EX); + self::$activeResourceLock = $lock; + } } public static function ddTearDownAfterClass() { + self::$activeResourceLock = null; } protected function ddSetUp() @@ -102,7 +110,7 @@ public static function putEnvAndReloadConfig($putenvs = []) protected function assertStringContains($needle, $haystack, $message = '') { - if (PHPUNIT_MAJOR >= 9) { + if (PHPUNIT_MAJOR >= 8) { parent::assertStringContainsString($needle, $haystack, $message); } else { parent::assertContains($needle, $haystack, $message); @@ -111,7 +119,7 @@ protected function assertStringContains($needle, $haystack, $message = '') protected function assertStringNotContains($needle, $haystack, $message = '') { - if (PHPUNIT_MAJOR >= 9) { + if (PHPUNIT_MAJOR >= 8) { parent::assertStringNotContainsString($needle, $haystack, $message); } else { parent::assertNotContains($needle, $haystack, $message); diff --git a/tests/Common/CLITestCase.php b/tests/Common/CLITestCase.php index 59ca71cd13..2d86db2f84 100644 --- a/tests/Common/CLITestCase.php +++ b/tests/Common/CLITestCase.php @@ -33,7 +33,8 @@ protected static function getEnvs() 'DD_TEST_INTEGRATION' => 'true', 'DD_TRACE_EXEC_ENABLED' => 'false', 'DD_TRACE_SHUTDOWN_TIMEOUT' => '666666', // Arbitrarily high value to avoid flakiness - 'DD_TRACE_AGENT_RETRIES' => '3' + 'DD_TRACE_AGENT_RETRIES' => '3', + 'DD_TRACE_AGENT_TEST_SESSION_TOKEN' => ini_get("datadog.trace.agent_test_session_token"), ]; return $envs; } diff --git a/tests/Common/IntegrationTestCase.php b/tests/Common/IntegrationTestCase.php index c89c94a164..4131606733 100644 --- a/tests/Common/IntegrationTestCase.php +++ b/tests/Common/IntegrationTestCase.php @@ -14,6 +14,10 @@ abstract class IntegrationTestCase extends BaseTestCase use SpanAssertionTrait; private $errorReportingBefore; + public static $autoloadPath = null; + + public static $database = "test"; + private static $createdDatabases = ["test" => true]; public static function ddSetUpBeforeClass() { @@ -38,14 +42,21 @@ public static function ddSetUpBeforeClass() file_put_contents($artifactsDir . "/extension_versions.csv", $csv, FILE_APPEND); $csv = ''; - $output = shell_exec('DD_TRACE_ENABLED=0 composer --working-dir=./tests show -f json'); - $data = json_decode($output, true); - foreach ($data['installed'] as $package) { - $csv = $csv . $package['name'] . ";" . $package['version'] . "\n"; + if (self::$autoloadPath && file_exists(dirname(self::$autoloadPath). "/composer/installed.json")) { + $data = json_decode(file_get_contents(dirname(self::$autoloadPath). "/composer/installed.json"), true); + foreach ($data['packages'] as $package) { + $csv = $csv . $package['name'] . ";" . $package['version'] . "\n"; + } } file_put_contents($artifactsDir . "/composer_versions.csv", $csv, FILE_APPEND); + + if (isset(static::$database) && !isset(self::$createdDatabases[static::$database])) { + $pdo = new \PDO('mysql:host=mysql_integration', 'test', 'test'); + $pdo->exec("CREATE DATABASE IF NOT EXISTS " . static::$database); + self::$createdDatabases[static::$database] = true; + } } public static function ddTearDownAfterClass() @@ -57,6 +68,7 @@ public static function ddTearDownAfterClass() protected function ddSetUp() { $this->errorReportingBefore = error_reporting(); + $this->resetTracer(); // Needs reset so we can remove root span $this->putEnv("DD_TRACE_GENERATE_ROOT_SPAN=0"); parent::ddSetUp(); } diff --git a/tests/Common/SnapshotTestTrait.php b/tests/Common/SnapshotTestTrait.php index d8fbab1172..72e9588320 100644 --- a/tests/Common/SnapshotTestTrait.php +++ b/tests/Common/SnapshotTestTrait.php @@ -318,9 +318,12 @@ public function tracesFromWebRequestSnapshot( $token = $this->generateToken(); $this->startSnapshotSession($token); + $originalToken = ini_get("datadog.trace.agent_test_session_token"); + update_test_agent_session_token($token); $fn($tracer); + update_test_agent_session_token($originalToken); self::putEnv('DD_TRACE_SHUTDOWN_TIMEOUT'); self::putEnv('DD_TRACE_AGENT_RETRIES'); @@ -343,6 +346,8 @@ public function isolateTracerSnapshot( $token = $this->generateToken(); $this->startSnapshotSession($token, $snapshotMetrics, $logsFile); + $originalToken = ini_get("datadog.trace.agent_test_session_token"); + update_test_agent_session_token($token); $this->resetTracer($tracer, $config); @@ -352,14 +357,15 @@ public function isolateTracerSnapshot( } $fn($tracer); - self::putEnv('DD_TRACE_SHUTDOWN_TIMEOUT'); - self::putEnv('DD_TRACE_AGENT_RETRIES'); - $traces = $this->flushAndGetTraces($tracer); if (!empty($traces)) { $this->sendTracesToTestAgent($traces); } + update_test_agent_session_token($originalToken); + self::putEnv('DD_TRACE_SHUTDOWN_TIMEOUT'); + self::putEnv('DD_TRACE_AGENT_RETRIES'); + $this->stopAndCompareSnapshotSession( $token, $fieldsToIgnore, diff --git a/tests/Common/SpanChecker.php b/tests/Common/SpanChecker.php index 39912ace58..3153a8551b 100644 --- a/tests/Common/SpanChecker.php +++ b/tests/Common/SpanChecker.php @@ -21,8 +21,6 @@ function array_filter_by_key($fn, array $input) */ final class SpanChecker { - const DEFAULTS_ATTRIBUTES = ['_dd.p.tid']; - /** * Asserts a flame graph with parent child relations. * @@ -30,14 +28,8 @@ final class SpanChecker * @param SpanAssertion[] $expectedFlameGraph * @param bool $assertExactCount */ - public function assertFlameGraph(array $traces, array $expectedFlameGraph, bool $assertExactCount = true, bool $applyDefaults = true) + public function assertFlameGraph(array $traces, array $expectedFlameGraph, bool $assertExactCount = true) { - if ($applyDefaults) { - foreach ($expectedFlameGraph as $spanAssertion) { - $spanAssertion->withExistingTagsNames(self::DEFAULTS_ATTRIBUTES); - } - } - $flattenTraces = $this->flattenTraces($traces); $actualGraph = $this->buildSpansGraph($flattenTraces); if ($assertExactCount && \count($actualGraph) != \count($expectedFlameGraph)) { @@ -87,6 +79,7 @@ public static function dumpSpansGraph(array $spansGraph, int $indent = 0) $out .= "\n"; if (isset($span['meta'])) { unset($span['meta']['_dd.p.dm']); + unset($span['meta']['_dd.p.tid']); unset($span['meta']['http.client_ip']); foreach ($span['meta'] as $k => $v) { $out .= str_repeat(' ', $indent) . ' ' . $k . ' => ' . $v . "\n"; @@ -337,16 +330,9 @@ private function buildSpansGraph(array $flatSpans) * * @param $traces * @param SpanAssertion[] $expectedSpans - * @param bool $applyDefaults */ - public function assertSpans($traces, $expectedSpans, $applyDefaults = true) + public function assertSpans($traces, $expectedSpans) { - if ($applyDefaults) { - foreach ($expectedSpans as $spanAssertion) { - $spanAssertion->withExistingTagsNames(self::DEFAULTS_ATTRIBUTES); - } - } - $flattenTraces = $this->flattenTraces($traces); // The sandbox API pops closed spans off a stack so spans will be in reverse order $flattenTraces = array_reverse($flattenTraces); @@ -482,6 +468,10 @@ function ($key) use ($pattern) { if (!isset($expectedTags['_dd.p.dm'])) { unset($filtered['_dd.p.dm']); } + // Ignore _dd.p.tid unless explicitly tested + if (!isset($expectedTags['_dd.p.tid'])) { + unset($filtered['_dd.p.tid']); + } // Ignore runtime-id unless explicitly tested if (!isset($expectedTags['runtime-id'])) { unset($filtered['runtime-id']); diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index 746d26c489..debb1c6851 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -16,6 +16,8 @@ trait TracerTestTrait protected static $agentRequestDumperUrl = 'http://request-replayer'; protected static $testAgentUrl = 'http://test-agent:9126'; + protected static $webserverPort = 6666 + GLOBAL_PORT_OFFSET; + public function resetTracer($tracer = null, $config = []) { // Reset the current C-level array of generated spans @@ -36,7 +38,10 @@ public static function setResponse($content) "http" => [ "method" => "POST", "content" => json_encode($content), - "header" => "Content-Type: application/json" + "header" => [ + "Content-Type: application/json", + 'X-Datadog-Test-Session-Token: ' . ini_get("datadog.trace.agent_test_session_token"), + ], ] ])); } @@ -80,7 +85,8 @@ public function sendTracesToTestAgent($traces) 'Content-Type: application/json', 'Datadog-Meta-Lang: php', 'X-Datadog-Agent-Proxy-Disabled: true', - 'X-Datadog-Trace-Count: ' . count($traces) + 'X-Datadog-Trace-Count: ' . count($traces), + 'X-Datadog-Test-Session-Token: ' . ini_get("datadog.trace.agent_test_session_token"), ); // add environment variables to headers @@ -169,7 +175,7 @@ public function inWebServer($fn, $rootPath, $envs = [], $inis = [], &$curlInfo = self::putEnv('DD_TRACE_AGENT_RETRIES=3'); $this->resetTracer(); - $webServer = new WebServer($rootPath, '0.0.0.0', 6666); + $webServer = new WebServer($rootPath, '0.0.0.0', self::$webserverPort); $webServer->mergeEnvs($envs); $webServer->mergeInis($inis); $webServer->start(); @@ -177,7 +183,7 @@ public function inWebServer($fn, $rootPath, $envs = [], $inis = [], &$curlInfo = $fn(function (RequestSpec $request) use ($webServer, &$curlInfo) { if ($request instanceof GetSpec) { - $curl = curl_init('http://127.0.0.1:6666' . $request->getPath()); + $curl = curl_init('http://127.0.0.1:' . self::$webserverPort . $request->getPath()); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, $request->getHeaders()); $response = curl_exec($curl); @@ -237,9 +243,10 @@ public function executeCli($scriptPath, $customEnvs = [], $customInis = [], $arg $customEnvs )); + if (GLOBAL_AUTO_PREPEND_FILE) { + $customInis['auto_prepend_file'] = GLOBAL_AUTO_PREPEND_FILE; + } if (getenv('PHPUNIT_COVERAGE')) { - $customInis['auto_prepend_file'] = __DIR__ . '/../save_code_coverage.php'; - $xdebugExtension = glob(PHP_EXTENSION_DIR . '/xdebug*.so'); $xdebugExtension = end($xdebugExtension); $customInis['zend_extension'] = $xdebugExtension; @@ -274,6 +281,7 @@ public function executeCli($scriptPath, $customEnvs = [], $customInis = [], $arg public function resetRequestDumper() { $curl = curl_init(self::$agentRequestDumperUrl . '/clear-dumped-data'); + curl_setopt($curl, CURLOPT_HTTPHEADER, ['x-datadog-test-session-token: ' . ini_get("datadog.trace.agent_test_session_token")]); curl_exec($curl); } @@ -444,6 +452,7 @@ public function retrieveDumpedData(callable $until = null, $throw = false) for ($attemptNumber = 1; $attemptNumber <= 50; $attemptNumber++) { $curl = curl_init(self::$agentRequestDumperUrl . '/replay'); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HTTPHEADER, ['x-datadog-test-session-token: ' . ini_get("datadog.trace.agent_test_session_token")]); // Retrieving data $response = curl_exec($curl); if (!$response) { diff --git a/tests/Common/WebFrameworkTestCase.php b/tests/Common/WebFrameworkTestCase.php index 97e2634592..c1ae0bdadb 100644 --- a/tests/Common/WebFrameworkTestCase.php +++ b/tests/Common/WebFrameworkTestCase.php @@ -18,7 +18,7 @@ abstract class WebFrameworkTestCase extends IntegrationTestCase // host and port for the testing framework const HOST = 'http://localhost'; const HOST_WITH_CREDENTIALS = 'http://my_user:my_password@localhost'; - const PORT = 9999; + const PORT = 9999 - GLOBAL_PORT_OFFSET; const ERROR_LOG_NAME = 'phpunit_error.log'; const COOKIE_JAR = 'cookies.txt'; @@ -215,7 +215,7 @@ protected function call(RequestSpec $spec, $options = []) { $response = $this->sendRequest( $spec->getMethod(), - self::HOST . ':' . self::PORT . $spec->getPath(), + self::HOST . $spec->getPath(), $spec->getHeaders(), $spec->getBody(), $options @@ -245,6 +245,7 @@ protected function sendRequest($method, $url, $headers = [], $body = [], $change for ($i = 0; $i < 10; ++$i) { $ch = curl_init($url); + curl_setopt($ch, CURLOPT_CONNECT_TO, ["localhost:80:localhost:" . self::PORT]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, $options[CURLOPT_RETURNTRANSFER]); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $options[CURLOPT_FOLLOWLOCATION]); diff --git a/tests/Composer/ComposerInteroperabilityTest.php b/tests/Composer/ComposerInteroperabilityTest.php index 0e0752161c..4a73d1b577 100644 --- a/tests/Composer/ComposerInteroperabilityTest.php +++ b/tests/Composer/ComposerInteroperabilityTest.php @@ -94,7 +94,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /no-manual-tracing') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/no-manual-tracing', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/no-manual-tracing', 'http.status_code' => '200', ]), ]); @@ -128,7 +128,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /manual-tracing') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/manual-tracing', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/manual-tracing', 'http.status_code' => '200', ]) ->withChildren([ @@ -168,7 +168,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /manual-tracing') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/manual-tracing', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/manual-tracing', 'http.status_code' => '200', ]) ->withChildren([ @@ -208,7 +208,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /no-manual-tracing') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/no-manual-tracing', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/no-manual-tracing', 'http.status_code' => '200', ]), ]); @@ -242,7 +242,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /manual-tracing') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/manual-tracing', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/manual-tracing', 'http.status_code' => '200', ]) ->withChildren([ @@ -276,7 +276,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /no-manual-tracing') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/no-manual-tracing', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/no-manual-tracing', 'http.status_code' => '200', ]), ]); @@ -304,7 +304,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /no-composer') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/no-composer', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/no-composer', 'http.status_code' => '200', ]), ]); @@ -337,7 +337,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /no-composer') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/no-composer', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/no-composer', 'http.status_code' => '200', ]), ]); @@ -372,7 +372,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /no-composer-autoload-fails') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/no-composer-autoload-fails', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/no-composer-autoload-fails', 'http.status_code' => '200', ]), ]); @@ -407,7 +407,7 @@ function ($execute) { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /composer-autoload-fails') ->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://127.0.0.1:6666/composer-autoload-fails', + 'http.url' => 'http://127.0.0.1:' . self::$webserverPort . '/composer-autoload-fails', 'http.status_code' => '200', ]), ]); diff --git a/tests/DistributedTracing/SyntheticsTest.php b/tests/DistributedTracing/SyntheticsTest.php index 760e0fea92..4180ca8455 100644 --- a/tests/DistributedTracing/SyntheticsTest.php +++ b/tests/DistributedTracing/SyntheticsTest.php @@ -51,7 +51,7 @@ public function testSyntheticsRequest() 'GET /index.php' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/index.php', + 'http.url' => 'http://localhost/index.php', 'http.status_code' => '200', '_dd.origin' => 'synthetics-browser', ])->withExactMetrics([ diff --git a/tests/Frameworks/CakePHP/Version_2_8/app/Config/database.php b/tests/Frameworks/CakePHP/Version_2_8/app/Config/database.php index 6d4011bf25..4ca4baeb67 100644 --- a/tests/Frameworks/CakePHP/Version_2_8/app/Config/database.php +++ b/tests/Frameworks/CakePHP/Version_2_8/app/Config/database.php @@ -49,7 +49,7 @@ * database. Uses database default not specified. * * sslmode => - * For Postgres specifies whether to 'disable', 'allow', 'prefer', or 'require' SSL for the + * For Postgres specifies whether to 'disable', 'allow', 'prefer', or 'require' SSL for the * connection. The default value is 'allow'. * * unix_socket => @@ -72,7 +72,7 @@ class DATABASE_CONFIG { 'host' => 'mysql_integration', 'login' => 'test', 'password' => 'test', - 'database' => 'test', + 'database' => 'cake28', 'prefix' => '', //'encoding' => 'utf8', ); diff --git a/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Command/InstallCommand.php b/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Command/InstallCommand.php index 8b1d139eda..4929fe811b 100644 --- a/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Command/InstallCommand.php +++ b/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Command/InstallCommand.php @@ -143,7 +143,7 @@ protected function install($class_loader, SymfonyStyle $io, $profile, $langcode, 'install_settings_form' => [ 'driver' => 'mysql', 'mysql' => [ - 'database' => 'test', + 'database' => 'drupal101', 'username' => 'test', 'password' => 'test', 'host' => 'mysql_integration', diff --git a/tests/Frameworks/Drupal/Version_10_1/scripts/erase_drupal_db.php b/tests/Frameworks/Drupal/Version_10_1/scripts/erase_drupal_db.php index df21a6ce8d..5d371abf5b 100644 --- a/tests/Frameworks/Drupal/Version_10_1/scripts/erase_drupal_db.php +++ b/tests/Frameworks/Drupal/Version_10_1/scripts/erase_drupal_db.php @@ -1,36 +1,38 @@ query('DROP TABLE IF EXISTS cache_bootstrap'); -$pdo->query('DROP TABLE IF EXISTS cache_config'); -$pdo->query('DROP TABLE IF EXISTS cache_container'); // Drupal doesn't like us dropping this table (PDOException) -$pdo->query('DROP TABLE IF EXISTS cache_data'); -$pdo->query('DROP TABLE IF EXISTS cache_default'); -$pdo->query('DROP TABLE IF EXISTS cache_discovery'); -$pdo->query('DROP TABLE IF EXISTS cache_entity'); -$pdo->query('DROP TABLE IF EXISTS config'); -$pdo->query('DROP TABLE IF EXISTS file_managed'); -$pdo->query('DROP TABLE IF EXISTS file_usage'); -$pdo->query('DROP TABLE IF EXISTS key_value'); -$pdo->query('DROP TABLE IF EXISTS key_value_expire'); -$pdo->query('DROP TABLE IF EXISTS menu_tree'); -$pdo->query('DROP TABLE IF EXISTS node'); -$pdo->query('DROP TABLE IF EXISTS node__body'); -$pdo->query('DROP TABLE IF EXISTS node_access'); -$pdo->query('DROP TABLE IF EXISTS node_field_data'); -$pdo->query('DROP TABLE IF EXISTS node_field_revision'); -$pdo->query('DROP TABLE IF EXISTS node_revision'); -$pdo->query('DROP TABLE IF EXISTS node_revision__body'); -$pdo->query('DROP TABLE IF EXISTS path_alias'); -$pdo->query('DROP TABLE IF EXISTS path_alias_revision'); -$pdo->query('DROP TABLE IF EXISTS queue'); -$pdo->query('DROP TABLE IF EXISTS router'); -$pdo->query('DROP TABLE IF EXISTS semaphore'); -$pdo->query('DROP TABLE IF EXISTS sequences'); -$pdo->query('DROP TABLE IF EXISTS sessions'); -$pdo->query('DROP TABLE IF EXISTS user__roles'); -$pdo->query('DROP TABLE IF EXISTS users'); -$pdo->query('DROP TABLE IF EXISTS users_data'); -$pdo->query('DROP TABLE IF EXISTS users_field_data'); -$pdo->query('DROP TABLE IF EXISTS watchdog'); +$pdo->query("CREATE DATABASE IF NOT EXISTS drupal101"); + +$pdo->query('DROP TABLE IF EXISTS drupal101.cache_bootstrap'); +$pdo->query('DROP TABLE IF EXISTS drupal101.cache_config'); +$pdo->query('DROP TABLE IF EXISTS drupal101.cache_container'); // Drupal doesn't like us dropping this table (PDOException) +$pdo->query('DROP TABLE IF EXISTS drupal101.cache_data'); +$pdo->query('DROP TABLE IF EXISTS drupal101.cache_default'); +$pdo->query('DROP TABLE IF EXISTS drupal101.cache_discovery'); +$pdo->query('DROP TABLE IF EXISTS drupal101.cache_entity'); +$pdo->query('DROP TABLE IF EXISTS drupal101.config'); +$pdo->query('DROP TABLE IF EXISTS drupal101.file_managed'); +$pdo->query('DROP TABLE IF EXISTS drupal101.file_usage'); +$pdo->query('DROP TABLE IF EXISTS drupal101.key_value'); +$pdo->query('DROP TABLE IF EXISTS drupal101.key_value_expire'); +$pdo->query('DROP TABLE IF EXISTS drupal101.menu_tree'); +$pdo->query('DROP TABLE IF EXISTS drupal101.node'); +$pdo->query('DROP TABLE IF EXISTS drupal101.node__body'); +$pdo->query('DROP TABLE IF EXISTS drupal101.node_access'); +$pdo->query('DROP TABLE IF EXISTS drupal101.node_field_data'); +$pdo->query('DROP TABLE IF EXISTS drupal101.node_field_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal101.node_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal101.node_revision__body'); +$pdo->query('DROP TABLE IF EXISTS drupal101.path_alias'); +$pdo->query('DROP TABLE IF EXISTS drupal101.path_alias_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal101.queue'); +$pdo->query('DROP TABLE IF EXISTS drupal101.router'); +$pdo->query('DROP TABLE IF EXISTS drupal101.semaphore'); +$pdo->query('DROP TABLE IF EXISTS drupal101.sequences'); +$pdo->query('DROP TABLE IF EXISTS drupal101.sessions'); +$pdo->query('DROP TABLE IF EXISTS drupal101.user__roles'); +$pdo->query('DROP TABLE IF EXISTS drupal101.users'); +$pdo->query('DROP TABLE IF EXISTS drupal101.users_data'); +$pdo->query('DROP TABLE IF EXISTS drupal101.users_field_data'); +$pdo->query('DROP TABLE IF EXISTS drupal101.watchdog'); diff --git a/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Command/InstallCommand.php b/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Command/InstallCommand.php index d0f4f82640..f38a635e52 100644 --- a/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Command/InstallCommand.php +++ b/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Command/InstallCommand.php @@ -143,7 +143,7 @@ protected function install($class_loader, SymfonyStyle $io, $profile, $langcode, 'install_settings_form' => [ 'driver' => 'mysql', 'mysql' => [ - 'database' => 'test', + 'database' => 'drupal89', 'username' => 'test', 'password' => 'test', 'host' => 'mysql_integration', diff --git a/tests/Frameworks/Drupal/Version_8_9/scripts/erase_drupal_db.php b/tests/Frameworks/Drupal/Version_8_9/scripts/erase_drupal_db.php index eb4916833d..0e8b12c66e 100644 --- a/tests/Frameworks/Drupal/Version_8_9/scripts/erase_drupal_db.php +++ b/tests/Frameworks/Drupal/Version_8_9/scripts/erase_drupal_db.php @@ -1,36 +1,38 @@ query('DROP TABLE IF EXISTS cache_bootstrap'); -$pdo->query('DROP TABLE IF EXISTS cache_config'); -$pdo->query('DROP TABLE IF EXISTS cache_container'); -$pdo->query('DROP TABLE IF EXISTS cache_data'); -$pdo->query('DROP TABLE IF EXISTS cache_default'); -$pdo->query('DROP TABLE IF EXISTS cache_discovery'); -$pdo->query('DROP TABLE IF EXISTS cache_entity'); -$pdo->query('DROP TABLE IF EXISTS config'); -$pdo->query('DROP TABLE IF EXISTS file_managed'); -$pdo->query('DROP TABLE IF EXISTS file_usage'); -$pdo->query('DROP TABLE IF EXISTS key_value'); -$pdo->query('DROP TABLE IF EXISTS key_value_expire'); -$pdo->query('DROP TABLE IF EXISTS menu_tree'); -$pdo->query('DROP TABLE IF EXISTS node'); -$pdo->query('DROP TABLE IF EXISTS node__body'); -$pdo->query('DROP TABLE IF EXISTS node_access'); -$pdo->query('DROP TABLE IF EXISTS node_field_data'); -$pdo->query('DROP TABLE IF EXISTS node_field_revision'); -$pdo->query('DROP TABLE IF EXISTS node_revision'); -$pdo->query('DROP TABLE IF EXISTS node_revision__body'); -$pdo->query('DROP TABLE IF EXISTS path_alias'); -$pdo->query('DROP TABLE IF EXISTS path_alias_revision'); -$pdo->query('DROP TABLE IF EXISTS queue'); -$pdo->query('DROP TABLE IF EXISTS router'); -$pdo->query('DROP TABLE IF EXISTS semaphore'); -$pdo->query('DROP TABLE IF EXISTS sequences'); -$pdo->query('DROP TABLE IF EXISTS sessions'); -$pdo->query('DROP TABLE IF EXISTS user__roles'); -$pdo->query('DROP TABLE IF EXISTS users'); -$pdo->query('DROP TABLE IF EXISTS users_data'); -$pdo->query('DROP TABLE IF EXISTS users_field_data'); -$pdo->query('DROP TABLE IF EXISTS watchdog'); +$pdo->query("CREATE DATABASE IF NOT EXISTS drupal89"); + +$pdo->query('DROP TABLE IF EXISTS drupal89.cache_bootstrap'); +$pdo->query('DROP TABLE IF EXISTS drupal89.cache_config'); +$pdo->query('DROP TABLE IF EXISTS drupal89.cache_container'); +$pdo->query('DROP TABLE IF EXISTS drupal89.cache_data'); +$pdo->query('DROP TABLE IF EXISTS drupal89.cache_default'); +$pdo->query('DROP TABLE IF EXISTS drupal89.cache_discovery'); +$pdo->query('DROP TABLE IF EXISTS drupal89.cache_entity'); +$pdo->query('DROP TABLE IF EXISTS drupal89.config'); +$pdo->query('DROP TABLE IF EXISTS drupal89.file_managed'); +$pdo->query('DROP TABLE IF EXISTS drupal89.file_usage'); +$pdo->query('DROP TABLE IF EXISTS drupal89.key_value'); +$pdo->query('DROP TABLE IF EXISTS drupal89.key_value_expire'); +$pdo->query('DROP TABLE IF EXISTS drupal89.menu_tree'); +$pdo->query('DROP TABLE IF EXISTS drupal89.node'); +$pdo->query('DROP TABLE IF EXISTS drupal89.node__body'); +$pdo->query('DROP TABLE IF EXISTS drupal89.node_access'); +$pdo->query('DROP TABLE IF EXISTS drupal89.node_field_data'); +$pdo->query('DROP TABLE IF EXISTS drupal89.node_field_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal89.node_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal89.node_revision__body'); +$pdo->query('DROP TABLE IF EXISTS drupal89.path_alias'); +$pdo->query('DROP TABLE IF EXISTS drupal89.path_alias_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal89.queue'); +$pdo->query('DROP TABLE IF EXISTS drupal89.router'); +$pdo->query('DROP TABLE IF EXISTS drupal89.semaphore'); +$pdo->query('DROP TABLE IF EXISTS drupal89.sequences'); +$pdo->query('DROP TABLE IF EXISTS drupal89.sessions'); +$pdo->query('DROP TABLE IF EXISTS drupal89.user__roles'); +$pdo->query('DROP TABLE IF EXISTS drupal89.users'); +$pdo->query('DROP TABLE IF EXISTS drupal89.users_data'); +$pdo->query('DROP TABLE IF EXISTS drupal89.users_field_data'); +$pdo->query('DROP TABLE IF EXISTS drupal89.watchdog'); diff --git a/tests/Frameworks/Drupal/Version_8_9/sites/default/default.settings.php b/tests/Frameworks/Drupal/Version_8_9/sites/default/default.settings.php index ff2c41f1ce..2fdeb8455a 100755 --- a/tests/Frameworks/Drupal/Version_8_9/sites/default/default.settings.php +++ b/tests/Frameworks/Drupal/Version_8_9/sites/default/default.settings.php @@ -90,7 +90,7 @@ */ $databases['default']['default'] = [ 'driver' => 'mysql', - 'database' => 'test', + 'database' => 'drupal89', 'username' => 'test', 'password' => 'test', 'host' => 'mysql_integration', @@ -258,7 +258,7 @@ */ $databases['default']['default'] = [ 'driver' => 'mysql', - 'database' => 'test', + 'database' => 'drupal89', 'username' => 'test', 'password' => 'test', 'host' => 'mysql_integration', diff --git a/tests/Frameworks/Drupal/Version_9_5/composer.lock b/tests/Frameworks/Drupal/Version_9_5/composer.lock deleted file mode 100644 index 1bc25f38ea..0000000000 --- a/tests/Frameworks/Drupal/Version_9_5/composer.lock +++ /dev/null @@ -1,8625 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "75276288e7e5acb61a63d2db0c1e5eb0", - "packages": [ - { - "name": "asm89/stack-cors", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/asm89/stack-cors.git", - "reference": "b9c31def6a83f84b4d4a40d35996d375755f0e08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/asm89/stack-cors/zipball/b9c31def6a83f84b4d4a40d35996d375755f0e08", - "reference": "b9c31def6a83f84b4d4a40d35996d375755f0e08", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/http-foundation": "~2.7|~3.0|~4.0|~5.0", - "symfony/http-kernel": "~2.7|~3.0|~4.0|~5.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.0 || ^4.8.10", - "squizlabs/php_codesniffer": "^2.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "psr-4": { - "Asm89\\Stack\\": "src/Asm89/Stack/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alexander", - "email": "iam.asm89@gmail.com" - } - ], - "description": "Cross-origin resource sharing library and stack middleware", - "homepage": "https://github.com/asm89/stack-cors", - "keywords": [ - "cors", - "stack" - ], - "support": { - "issues": "https://github.com/asm89/stack-cors/issues", - "source": "https://github.com/asm89/stack-cors/tree/1.3.0" - }, - "time": "2019-12-24T22:41:47+00:00" - }, - { - "name": "composer/installers", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/composer/installers.git", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/d20a64ed3c94748397ff5973488761b22f6d3f19", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0" - }, - "replace": { - "roundcube/plugin-installer": "*", - "shama/baton": "*" - }, - "require-dev": { - "composer/composer": "1.6.* || ^2.0", - "composer/semver": "^1 || ^3", - "phpstan/phpstan": "^0.12.55", - "phpstan/phpstan-phpunit": "^0.12.16", - "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.3" - }, - "type": "composer-plugin", - "extra": { - "class": "Composer\\Installers\\Plugin", - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Installers\\": "src/Composer/Installers" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kyle Robinson Young", - "email": "kyle@dontkry.com", - "homepage": "https://github.com/shama" - } - ], - "description": "A multi-framework Composer library installer", - "homepage": "https://composer.github.io/installers/", - "keywords": [ - "Craft", - "Dolibarr", - "Eliasis", - "Hurad", - "ImageCMS", - "Kanboard", - "Lan Management System", - "MODX Evo", - "MantisBT", - "Mautic", - "Maya", - "OXID", - "Plentymarkets", - "Porto", - "RadPHP", - "SMF", - "Starbug", - "Thelia", - "Whmcs", - "WolfCMS", - "agl", - "aimeos", - "annotatecms", - "attogram", - "bitrix", - "cakephp", - "chef", - "cockpit", - "codeigniter", - "concrete5", - "croogo", - "dokuwiki", - "drupal", - "eZ Platform", - "elgg", - "expressionengine", - "fuelphp", - "grav", - "installer", - "itop", - "joomla", - "known", - "kohana", - "laravel", - "lavalite", - "lithium", - "magento", - "majima", - "mako", - "mediawiki", - "miaoxing", - "modulework", - "modx", - "moodle", - "osclass", - "pantheon", - "phpbb", - "piwik", - "ppi", - "processwire", - "puppet", - "pxcms", - "reindex", - "roundcube", - "shopware", - "silverstripe", - "sydes", - "sylius", - "symfony", - "tastyigniter", - "typo3", - "wordpress", - "yawik", - "zend", - "zikula" - ], - "support": { - "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.12.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2021-09-13T08:19:44+00:00" - }, - { - "name": "composer/semver", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-04-01T19:23:25+00:00" - }, - { - "name": "doctrine/annotations", - "version": "1.14.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", - "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^1 || ^2", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "vimeo/psalm": "^4.10" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.14.3" - }, - "time": "2023-02-01T09:20:38+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" - }, - "time": "2023-06-03T09:27:29+00:00" - }, - { - "name": "doctrine/lexer", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2022-12-14T08:49:07+00:00" - }, - { - "name": "doctrine/reflection", - "version": "1.2.4", - "source": { - "type": "git", - "url": "https://github.com/doctrine/reflection.git", - "reference": "6bcea3e81ab8b3d0abe5fde5300bbc8a968960c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/reflection/zipball/6bcea3e81ab8b3d0abe5fde5300bbc8a968960c7", - "reference": "6bcea3e81ab8b3d0abe5fde5300bbc8a968960c7", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.0 || ^2.0", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": "<2.9" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "doctrine/common": "^3.3", - "phpstan/phpstan": "^1.4.10", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Reflection project is a simple library used by the various Doctrine projects which adds some additional functionality on top of the reflection functionality that comes with PHP. It allows you to get the reflection information about classes, methods and properties statically.", - "homepage": "https://www.doctrine-project.org/projects/reflection.html", - "keywords": [ - "reflection", - "static" - ], - "support": { - "issues": "https://github.com/doctrine/reflection/issues", - "source": "https://github.com/doctrine/reflection/tree/1.2.4" - }, - "abandoned": "roave/better-reflection", - "time": "2023-07-27T18:11:59+00:00" - }, - { - "name": "drupal/core", - "version": "dev-master", - "dist": { - "type": "path", - "url": "core", - "reference": "737286c20cf976748c4a3e46add09b526d577681" - }, - "require": { - "asm89/stack-cors": "^1.3", - "composer/semver": "^3.3", - "doctrine/annotations": "^1.13", - "doctrine/reflection": "^1.2", - "egulias/email-validator": "^2.1.22|^3.2", - "ext-date": "*", - "ext-dom": "*", - "ext-filter": "*", - "ext-gd": "*", - "ext-hash": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-pdo": "*", - "ext-session": "*", - "ext-simplexml": "*", - "ext-spl": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "laminas/laminas-feed": "^2.17", - "longwave/laminas-diactoros": "^2.14", - "masterminds/html5": "^2.7", - "pear/archive_tar": "^1.4.14", - "php": ">=7.3.0", - "psr/log": "^1.1", - "stack/builder": "^1.0", - "symfony-cmf/routing": "^2.3", - "symfony/console": "^4.4", - "symfony/dependency-injection": "^4.4", - "symfony/event-dispatcher": "^4.4", - "symfony/http-foundation": "^4.4.7", - "symfony/http-kernel": "^4.4", - "symfony/mime": "^5.4", - "symfony/polyfill-iconv": "^1.26", - "symfony/polyfill-php80": "^1.26", - "symfony/process": "^4.4", - "symfony/psr-http-message-bridge": "^2.1", - "symfony/routing": "^4.4", - "symfony/serializer": "^4.4", - "symfony/translation": "^4.4", - "symfony/validator": "^4.4", - "symfony/yaml": "^4.4.19", - "twig/twig": "^2.15.3", - "typo3/phar-stream-wrapper": "^3.1.3" - }, - "conflict": { - "drush/drush": "<8.1.10", - "symfony/http-foundation": "4.4.42" - }, - "replace": { - "drupal/core-annotation": "self.version", - "drupal/core-assertion": "self.version", - "drupal/core-bridge": "self.version", - "drupal/core-class-finder": "self.version", - "drupal/core-datetime": "self.version", - "drupal/core-dependency-injection": "self.version", - "drupal/core-diff": "self.version", - "drupal/core-discovery": "self.version", - "drupal/core-event-dispatcher": "self.version", - "drupal/core-file-cache": "self.version", - "drupal/core-file-security": "self.version", - "drupal/core-filesystem": "self.version", - "drupal/core-front-matter": "self.version", - "drupal/core-gettext": "self.version", - "drupal/core-graph": "self.version", - "drupal/core-http-foundation": "self.version", - "drupal/core-php-storage": "self.version", - "drupal/core-plugin": "self.version", - "drupal/core-proxy-builder": "self.version", - "drupal/core-render": "self.version", - "drupal/core-serialization": "self.version", - "drupal/core-transliteration": "self.version", - "drupal/core-utility": "self.version", - "drupal/core-uuid": "self.version", - "drupal/core-version": "self.version" - }, - "type": "drupal-core", - "extra": { - "drupal-scaffold": { - "file-mapping": { - "[project-root]/.editorconfig": "assets/scaffold/files/editorconfig", - "[project-root]/.gitattributes": "assets/scaffold/files/gitattributes", - "[web-root]/.csslintrc": "assets/scaffold/files/csslintrc", - "[web-root]/.eslintignore": "assets/scaffold/files/eslintignore", - "[web-root]/.eslintrc.json": "assets/scaffold/files/eslintrc.json", - "[web-root]/.ht.router.php": "assets/scaffold/files/ht.router.php", - "[web-root]/.htaccess": "assets/scaffold/files/htaccess", - "[web-root]/example.gitignore": "assets/scaffold/files/example.gitignore", - "[web-root]/index.php": "assets/scaffold/files/index.php", - "[web-root]/INSTALL.txt": "assets/scaffold/files/drupal.INSTALL.txt", - "[web-root]/README.md": "assets/scaffold/files/drupal.README.md", - "[web-root]/robots.txt": "assets/scaffold/files/robots.txt", - "[web-root]/update.php": "assets/scaffold/files/update.php", - "[web-root]/web.config": "assets/scaffold/files/web.config", - "[web-root]/sites/README.txt": "assets/scaffold/files/sites.README.txt", - "[web-root]/sites/development.services.yml": "assets/scaffold/files/development.services.yml", - "[web-root]/sites/example.settings.local.php": "assets/scaffold/files/example.settings.local.php", - "[web-root]/sites/example.sites.php": "assets/scaffold/files/example.sites.php", - "[web-root]/sites/default/default.services.yml": "assets/scaffold/files/default.services.yml", - "[web-root]/sites/default/default.settings.php": "assets/scaffold/files/default.settings.php", - "[web-root]/modules/README.txt": "assets/scaffold/files/modules.README.txt", - "[web-root]/profiles/README.txt": "assets/scaffold/files/profiles.README.txt", - "[web-root]/themes/README.txt": "assets/scaffold/files/themes.README.txt" - } - } - }, - "autoload": { - "psr-4": { - "Drupal\\Core\\": "lib/Drupal/Core", - "Drupal\\Component\\": "lib/Drupal/Component", - "Drupal\\Driver\\": "../drivers/lib/Drupal/Driver" - }, - "classmap": [ - "lib/Drupal.php", - "lib/Drupal/Component/DependencyInjection/Container.php", - "lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php", - "lib/Drupal/Component/FileCache/FileCacheFactory.php", - "lib/Drupal/Component/Utility/Timer.php", - "lib/Drupal/Component/Utility/Unicode.php", - "lib/Drupal/Core/Cache/Cache.php", - "lib/Drupal/Core/Cache/CacheBackendInterface.php", - "lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php", - "lib/Drupal/Core/Cache/CacheTagsChecksumTrait.php", - "lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php", - "lib/Drupal/Core/Cache/DatabaseBackend.php", - "lib/Drupal/Core/Cache/DatabaseCacheTagsChecksum.php", - "lib/Drupal/Core/Database/Connection.php", - "lib/Drupal/Core/Database/Database.php", - "lib/Drupal/Core/Database/Statement.php", - "lib/Drupal/Core/Database/StatementInterface.php", - "lib/Drupal/Core/DependencyInjection/Container.php", - "lib/Drupal/Core/DrupalKernel.php", - "lib/Drupal/Core/DrupalKernelInterface.php", - "lib/Drupal/Core/Http/InputBag.php", - "lib/Drupal/Core/Installer/InstallerRedirectTrait.php", - "lib/Drupal/Core/Site/Settings.php" - ], - "files": [ - "includes/bootstrap.inc", - "includes/guzzle_php81_shim.php" - ] - }, - "scripts": { - "pre-autoload-dump": [ - "Drupal\\Core\\Composer\\Composer::preAutoloadDump" - ] - }, - "license": [ - "GPL-2.0-or-later" - ], - "description": "Drupal is an open source content management platform powering millions of websites and applications.", - "transport-options": { - "relative": true - } - }, - { - "name": "drupal/core-project-message", - "version": "dev-master", - "dist": { - "type": "path", - "url": "composer/Plugin/ProjectMessage", - "reference": "b4efdbe26634b41a1b89e4f3770a8074769088a6" - }, - "require": { - "composer-plugin-api": "^1.1 || ^2", - "php": ">=7.3.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Drupal\\Composer\\Plugin\\ProjectMessage\\MessagePlugin" - }, - "autoload": { - "psr-4": { - "Drupal\\Composer\\Plugin\\ProjectMessage\\": "." - } - }, - "license": [ - "GPL-2.0-or-later" - ], - "description": "Adds a message after Composer installation.", - "homepage": "https://www.drupal.org/project/drupal", - "keywords": [ - "drupal" - ], - "transport-options": { - "relative": true - } - }, - { - "name": "drupal/core-vendor-hardening", - "version": "dev-master", - "dist": { - "type": "path", - "url": "composer/Plugin/VendorHardening", - "reference": "d54f0b3cc8b4237f3a41a0860a808db242f9da9e" - }, - "require": { - "composer-plugin-api": "^1.1 || ^2", - "php": ">=7.3.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Drupal\\Composer\\Plugin\\VendorHardening\\VendorHardeningPlugin" - }, - "autoload": { - "psr-4": { - "Drupal\\Composer\\Plugin\\VendorHardening\\": "." - } - }, - "license": [ - "GPL-2.0-or-later" - ], - "description": "Hardens the vendor directory for when it's in the docroot.", - "homepage": "https://www.drupal.org/project/drupal", - "keywords": [ - "drupal" - ], - "transport-options": { - "relative": true - } - }, - { - "name": "egulias/email-validator", - "version": "3.2.6", - "source": { - "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", - "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^1.2|^2", - "php": ">=7.2", - "symfony/polyfill-intl-idn": "^1.15" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.8|^9.3.3", - "vimeo/psalm": "^4" - }, - "suggest": { - "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Egulias\\EmailValidator\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eduardo Gulias Davis" - } - ], - "description": "A library for validating emails against several RFCs", - "homepage": "https://github.com/egulias/EmailValidator", - "keywords": [ - "email", - "emailvalidation", - "emailvalidator", - "validation", - "validator" - ], - "support": { - "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/3.2.6" - }, - "funding": [ - { - "url": "https://github.com/egulias", - "type": "github" - } - ], - "time": "2023-06-01T07:04:22+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.7.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.7.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2023-05-21T14:04:53+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2023-08-03T15:11:55+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.6.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/8bd7c33a0734ae1c5d074360512beb716bef3f77", - "reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2023-08-03T15:06:02+00:00" - }, - { - "name": "laminas/laminas-escaper", - "version": "2.12.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-escaper.git", - "reference": "ee7a4c37bf3d0e8c03635d5bddb5bb3184ead490" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/ee7a4c37bf3d0e8c03635d5bddb5bb3184ead490", - "reference": "ee7a4c37bf3d0e8c03635d5bddb5bb3184ead490", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-mbstring": "*", - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0" - }, - "conflict": { - "zendframework/zend-escaper": "*" - }, - "require-dev": { - "infection/infection": "^0.26.6", - "laminas/laminas-coding-standard": "~2.4.0", - "maglnet/composer-require-checker": "^3.8.0", - "phpunit/phpunit": "^9.5.18", - "psalm/plugin-phpunit": "^0.17.0", - "vimeo/psalm": "^4.22.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Escaper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", - "homepage": "https://laminas.dev", - "keywords": [ - "escaper", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-escaper/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-escaper/issues", - "rss": "https://github.com/laminas/laminas-escaper/releases.atom", - "source": "https://github.com/laminas/laminas-escaper" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2022-10-10T10:11:09+00:00" - }, - { - "name": "laminas/laminas-feed", - "version": "2.21.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-feed.git", - "reference": "52918789a417bc292ccd6fbb4b91bd78a65d50ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/52918789a417bc292ccd6fbb4b91bd78a65d50ab", - "reference": "52918789a417bc292ccd6fbb4b91bd78a65d50ab", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "laminas/laminas-escaper": "^2.9", - "laminas/laminas-stdlib": "^3.6", - "php": "~8.1.0 || ~8.2.0" - }, - "conflict": { - "laminas/laminas-servicemanager": "<3.3", - "zendframework/zend-feed": "*" - }, - "require-dev": { - "laminas/laminas-cache": "^2.13.2 || ^3.10.1", - "laminas/laminas-cache-storage-adapter-memory": "^1.1.0 || ^2.2", - "laminas/laminas-coding-standard": "~2.5.0", - "laminas/laminas-db": "^2.18", - "laminas/laminas-http": "^2.18", - "laminas/laminas-servicemanager": "^3.21.0", - "laminas/laminas-validator": "^2.30.1", - "phpunit/phpunit": "^10.2.6", - "psalm/plugin-phpunit": "^0.18.4", - "psr/http-message": "^2.0", - "vimeo/psalm": "^5.13.1" - }, - "suggest": { - "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", - "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", - "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", - "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", - "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent", - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Feed\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides functionality for creating and consuming RSS and Atom feeds", - "homepage": "https://laminas.dev", - "keywords": [ - "atom", - "feed", - "laminas", - "rss" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-feed/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-feed/issues", - "rss": "https://github.com/laminas/laminas-feed/releases.atom", - "source": "https://github.com/laminas/laminas-feed" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-07-24T09:21:16+00:00" - }, - { - "name": "laminas/laminas-stdlib", - "version": "3.17.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-stdlib.git", - "reference": "dd35c868075bad80b6718959740913e178eb4274" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/dd35c868075bad80b6718959740913e178eb4274", - "reference": "dd35c868075bad80b6718959740913e178eb4274", - "shasum": "" - }, - "require": { - "php": "~8.1.0 || ~8.2.0" - }, - "conflict": { - "zendframework/zend-stdlib": "*" - }, - "require-dev": { - "laminas/laminas-coding-standard": "^2.5", - "phpbench/phpbench": "^1.2.9", - "phpunit/phpunit": "^10.0.16", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Stdlib\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "SPL extensions, array utilities, error handlers, and more", - "homepage": "https://laminas.dev", - "keywords": [ - "laminas", - "stdlib" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-stdlib/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-stdlib/issues", - "rss": "https://github.com/laminas/laminas-stdlib/releases.atom", - "source": "https://github.com/laminas/laminas-stdlib" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2023-03-20T13:51:37+00:00" - }, - { - "name": "longwave/laminas-diactoros", - "version": "2.14.2", - "source": { - "type": "git", - "url": "https://github.com/longwave/laminas-diactoros.git", - "reference": "ae4f0becf249ae8eea8f2f8f9fb927104e55a885" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/longwave/laminas-diactoros/zipball/ae4f0becf249ae8eea8f2f8f9fb927104e55a885", - "reference": "ae4f0becf249ae8eea8f2f8f9fb927104e55a885", - "shasum": "" - }, - "require": { - "php": "^7.3 || ~8.0.0 || ~8.1.0 || ~8.2.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0" - }, - "conflict": { - "phpspec/prophecy": "<1.9.0", - "zendframework/zend-diactoros": "*" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "replace": { - "laminas/laminas-diactoros": "2.18.1" - }, - "require-dev": { - "ext-curl": "*", - "ext-dom": "*", - "ext-gd": "*", - "ext-libxml": "*", - "http-interop/http-factory-tests": "^0.9.0", - "laminas/laminas-coding-standard": "~2.3.0", - "php-http/psr7-integration-tests": "^1.1.1", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "psalm/plugin-phpunit": "^0.17.0", - "vimeo/psalm": "^4.24.0" - }, - "type": "library", - "extra": { - "laminas": { - "config-provider": "Laminas\\Diactoros\\ConfigProvider", - "module": "Laminas\\Diactoros" - } - }, - "autoload": { - "files": [ - "src/functions/create_uploaded_file.php", - "src/functions/marshal_headers_from_sapi.php", - "src/functions/marshal_method_from_sapi.php", - "src/functions/marshal_protocol_version_from_sapi.php", - "src/functions/marshal_uri_from_sapi.php", - "src/functions/normalize_server.php", - "src/functions/normalize_uploaded_files.php", - "src/functions/parse_cookie_header.php", - "src/functions/create_uploaded_file.legacy.php", - "src/functions/marshal_headers_from_sapi.legacy.php", - "src/functions/marshal_method_from_sapi.legacy.php", - "src/functions/marshal_protocol_version_from_sapi.legacy.php", - "src/functions/marshal_uri_from_sapi.legacy.php", - "src/functions/normalize_server.legacy.php", - "src/functions/normalize_uploaded_files.legacy.php", - "src/functions/parse_cookie_header.legacy.php" - ], - "psr-4": { - "Laminas\\Diactoros\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "PSR HTTP Message implementations", - "homepage": "https://laminas.dev", - "keywords": [ - "http", - "laminas", - "psr", - "psr-17", - "psr-7" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-diactoros/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-diactoros/issues", - "rss": "https://github.com/laminas/laminas-diactoros/releases.atom", - "source": "https://github.com/laminas/laminas-diactoros" - }, - "time": "2023-04-26T21:27:14+00:00" - }, - { - "name": "masterminds/html5", - "version": "2.8.1", - "source": { - "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Masterminds\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", - "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" - ], - "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" - }, - "time": "2023-05-10T11:58:31+00:00" - }, - { - "name": "pear/archive_tar", - "version": "1.4.14", - "source": { - "type": "git", - "url": "https://github.com/pear/Archive_Tar.git", - "reference": "4d761c5334c790e45ef3245f0864b8955c562caa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/4d761c5334c790e45ef3245f0864b8955c562caa", - "reference": "4d761c5334c790e45ef3245f0864b8955c562caa", - "shasum": "" - }, - "require": { - "pear/pear-core-minimal": "^1.10.0alpha2", - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "suggest": { - "ext-bz2": "Bz2 compression support.", - "ext-xz": "Lzma2 compression support.", - "ext-zlib": "Gzip compression support." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Archive_Tar": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "./" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Vincent Blavet", - "email": "vincent@phpconcept.net" - }, - { - "name": "Greg Beaver", - "email": "greg@chiaraquartet.net" - }, - { - "name": "Michiel Rook", - "email": "mrook@php.net" - } - ], - "description": "Tar file management class with compression support (gzip, bzip2, lzma2)", - "homepage": "https://github.com/pear/Archive_Tar", - "keywords": [ - "archive", - "tar" - ], - "support": { - "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar", - "source": "https://github.com/pear/Archive_Tar" - }, - "funding": [ - { - "url": "https://github.com/mrook", - "type": "github" - }, - { - "url": "https://www.patreon.com/michielrook", - "type": "patreon" - } - ], - "time": "2021-07-20T13:53:39+00:00" - }, - { - "name": "pear/console_getopt", - "version": "v1.4.3", - "source": { - "type": "git", - "url": "https://github.com/pear/Console_Getopt.git", - "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/a41f8d3e668987609178c7c4a9fe48fecac53fa0", - "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-0": { - "Console": "./" - } - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "./" - ], - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Andrei Zmievski", - "email": "andrei@php.net", - "role": "Lead" - }, - { - "name": "Stig Bakken", - "email": "stig@php.net", - "role": "Developer" - }, - { - "name": "Greg Beaver", - "email": "cellog@php.net", - "role": "Helper" - } - ], - "description": "More info available on: http://pear.php.net/package/Console_Getopt", - "support": { - "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Console_Getopt", - "source": "https://github.com/pear/Console_Getopt" - }, - "time": "2019-11-20T18:27:48+00:00" - }, - { - "name": "pear/pear-core-minimal", - "version": "v1.10.13", - "source": { - "type": "git", - "url": "https://github.com/pear/pear-core-minimal.git", - "reference": "aed862e95fd286c53cc546734868dc38ff4b5b1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/aed862e95fd286c53cc546734868dc38ff4b5b1d", - "reference": "aed862e95fd286c53cc546734868dc38ff4b5b1d", - "shasum": "" - }, - "require": { - "pear/console_getopt": "~1.4", - "pear/pear_exception": "~1.0" - }, - "replace": { - "rsky/pear-core-min": "self.version" - }, - "type": "library", - "autoload": { - "psr-0": { - "": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "src/" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "cweiske@php.net", - "role": "Lead" - } - ], - "description": "Minimal set of PEAR core files to be used as composer dependency", - "support": { - "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR", - "source": "https://github.com/pear/pear-core-minimal" - }, - "time": "2023-04-19T19:15:47+00:00" - }, - { - "name": "pear/pear_exception", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/pear/PEAR_Exception.git", - "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", - "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "<9" - }, - "type": "class", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "PEAR/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "." - ], - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Helgi Thormar", - "email": "dufuz@php.net" - }, - { - "name": "Greg Beaver", - "email": "cellog@php.net" - } - ], - "description": "The PEAR Exception base class.", - "homepage": "https://github.com/pear/PEAR_Exception", - "keywords": [ - "exception" - ], - "support": { - "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR_Exception", - "source": "https://github.com/pear/PEAR_Exception" - }, - "time": "2021-03-21T15:43:46+00:00" - }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" - }, - { - "name": "psr/container", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" - }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client/tree/1.0.2" - }, - "time": "2023-04-10T20:12:12+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" - }, - "time": "2023-04-10T20:10:41+00:00" - }, - { - "name": "psr/http-message", - "version": "1.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", - "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/1.1" - }, - "time": "2023-04-04T09:50:52+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "stack/builder", - "version": "v1.0.6", - "source": { - "type": "git", - "url": "https://github.com/stackphp/builder.git", - "reference": "a4faaa6f532c6086bc66c29e1bc6c29593e1ca7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/stackphp/builder/zipball/a4faaa6f532c6086bc66c29e1bc6c29593e1ca7c", - "reference": "a4faaa6f532c6086bc66c29e1bc6c29593e1ca7c", - "shasum": "" - }, - "require": { - "php": ">=7.2.0", - "symfony/http-foundation": "~2.1|~3.0|~4.0|~5.0", - "symfony/http-kernel": "~2.1|~3.0|~4.0|~5.0" - }, - "require-dev": { - "phpunit/phpunit": "~8.0", - "symfony/routing": "^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Stack": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - } - ], - "description": "Builder for stack middleware based on HttpKernelInterface.", - "keywords": [ - "stack" - ], - "support": { - "issues": "https://github.com/stackphp/builder/issues", - "source": "https://github.com/stackphp/builder/tree/v1.0.6" - }, - "time": "2020-01-30T12:17:27+00:00" - }, - { - "name": "symfony-cmf/routing", - "version": "2.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony-cmf/Routing.git", - "reference": "bbcdf2f6301d740454ba9ebb8adaefd436c36a6b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony-cmf/Routing/zipball/bbcdf2f6301d740454ba9ebb8adaefd436c36a6b", - "reference": "bbcdf2f6301d740454ba9ebb8adaefd436c36a6b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/http-kernel": "^4.4 || ^5.0", - "symfony/routing": "^4.4 || ^5.0" - }, - "require-dev": { - "symfony-cmf/testing": "^3@dev", - "symfony/config": "^4.4 || ^5.0", - "symfony/dependency-injection": "^4.4 || ^5.0", - "symfony/event-dispatcher": "^4.4 || ^5.0", - "symfony/phpunit-bridge": "^5.0" - }, - "suggest": { - "symfony/event-dispatcher": "DynamicRouter can optionally trigger an event at the start of matching. Minimal version (^4.4 || ^5.0)" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Cmf\\Component\\Routing\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony CMF Community", - "homepage": "https://github.com/symfony-cmf/Routing/contributors" - } - ], - "description": "Extends the Symfony routing component for dynamic routes and chaining several routers", - "homepage": "http://cmf.symfony.com", - "keywords": [ - "database", - "routing" - ], - "support": { - "issues": "https://github.com/symfony-cmf/Routing/issues", - "source": "https://github.com/symfony-cmf/Routing/tree/2.3.4" - }, - "time": "2021-11-08T16:33:10+00:00" - }, - { - "name": "symfony/console", - "version": "v4.4.49", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", - "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3|>=5", - "symfony/lock": "<4.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/event-dispatcher": "^4.3", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^3.4|^4.0|^5.0", - "symfony/var-dumper": "^4.3|^5.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/console/tree/v4.4.49" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-05T17:10:16+00:00" - }, - { - "name": "symfony/debug", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "1a692492190773c5310bc7877cb590c04c2f05be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/1a692492190773c5310bc7877cb590c04c2f05be", - "reference": "1a692492190773c5310bc7877cb590c04c2f05be", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "psr/log": "^1|^2|^3" - }, - "conflict": { - "symfony/http-kernel": "<3.4" - }, - "require-dev": { - "symfony/http-kernel": "^3.4|^4.0|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to ease debugging PHP code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/debug/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "abandoned": "symfony/error-handler", - "time": "2022-07-28T16:29:46+00:00" - }, - { - "name": "symfony/dependency-injection", - "version": "v4.4.49", - "source": { - "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "9065fe97dbd38a897e95ea254eb5ddfe1310f734" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/9065fe97dbd38a897e95ea254eb5ddfe1310f734", - "reference": "9065fe97dbd38a897e95ea254eb5ddfe1310f734", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "psr/container": "^1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1.6|^2" - }, - "conflict": { - "symfony/config": "<4.3|>=5.0", - "symfony/finder": "<3.4", - "symfony/proxy-manager-bridge": "<3.4", - "symfony/yaml": "<4.4.26" - }, - "provide": { - "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0|2.0" - }, - "require-dev": { - "symfony/config": "^4.3", - "symfony/expression-language": "^3.4|^4.0|^5.0", - "symfony/yaml": "^4.4.26|^5.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows you to standardize and centralize the way objects are constructed in your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v4.4.49" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-16T16:18:09+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-05-23T14:45:45+00:00" - }, - { - "name": "symfony/error-handler", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/error-handler.git", - "reference": "be731658121ef2d8be88f3a1ec938148a9237291" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/be731658121ef2d8be88f3a1ec938148a9237291", - "reference": "be731658121ef2d8be88f3a1ec938148a9237291", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "psr/log": "^1|^2|^3", - "symfony/debug": "^4.4.5", - "symfony/var-dumper": "^4.4|^5.0" - }, - "require-dev": { - "symfony/http-kernel": "^4.4|^5.0", - "symfony/serializer": "^4.4|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ErrorHandler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to manage errors and ease debugging PHP code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/error-handler/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-28T16:29:46+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "1e866e9e5c1b22168e0ce5f0b467f19bba61266a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1e866e9e5c1b22168e0ce5f0b467f19bba61266a", - "reference": "1e866e9e5c1b22168e0ce5f0b467f19bba61266a", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/event-dispatcher-contracts": "^1.1", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/dependency-injection": "<3.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "1.1" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/error-handler": "~3.4|~4.4", - "symfony/expression-language": "^3.4|^4.0|^5.0", - "symfony/http-foundation": "^3.4|^4.0|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/stopwatch": "^3.4|^4.0|^5.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-20T09:59:04+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/1d5cd762abaa6b2a4169d3e77610193a7157129e", - "reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e", - "shasum": "" - }, - "require": { - "php": ">=7.1.3" - }, - "suggest": { - "psr/event-dispatcher": "", - "symfony/event-dispatcher-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.1-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:41:36+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", - "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/http-client-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-04-12T15:48:08+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v4.4.49", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "191413c7b832c015bb38eae963f2e57498c3c173" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/191413c7b832c015bb38eae963f2e57498c3c173", - "reference": "191413c7b832c015bb38eae963f2e57498c3c173", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/mime": "^4.3|^5.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/expression-language": "^3.4|^4.0|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Defines an object-oriented layer for the HTTP specification", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-foundation/tree/v4.4.49" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-04T16:17:57+00:00" - }, - { - "name": "symfony/http-kernel", - "version": "v4.4.50", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "aa6df6c045f034aa13ac752fc234bb300b9488ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/aa6df6c045f034aa13ac752fc234bb300b9488ef", - "reference": "aa6df6c045f034aa13ac752fc234bb300b9488ef", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "psr/log": "^1|^2", - "symfony/error-handler": "^4.4", - "symfony/event-dispatcher": "^4.4", - "symfony/http-client-contracts": "^1.1|^2", - "symfony/http-foundation": "^4.4.30|^5.3.7", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/browser-kit": "<4.3", - "symfony/config": "<3.4", - "symfony/console": ">=5", - "symfony/dependency-injection": "<4.3", - "symfony/translation": "<4.2", - "twig/twig": "<1.43|<2.13,>=2" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^4.3|^5.0", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/console": "^3.4|^4.0", - "symfony/css-selector": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^4.3|^5.0", - "symfony/dom-crawler": "^3.4|^4.0|^5.0", - "symfony/expression-language": "^3.4|^4.0|^5.0", - "symfony/finder": "^3.4|^4.0|^5.0", - "symfony/process": "^3.4|^4.0|^5.0", - "symfony/routing": "^3.4|^4.0|^5.0", - "symfony/stopwatch": "^3.4|^4.0|^5.0", - "symfony/templating": "^3.4|^4.0|^5.0", - "symfony/translation": "^4.2|^5.0", - "symfony/translation-contracts": "^1.1|^2", - "twig/twig": "^1.43|^2.13|^3.0.4" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a structured process for converting a Request into a Response", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-kernel/tree/v4.4.50" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-02-01T08:01:31+00:00" - }, - { - "name": "symfony/mime", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "bb2ccf759e2b967dcd11bdee5bdf30dddd2290bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/bb2ccf759e2b967dcd11bdee5bdf30dddd2290bd", - "reference": "bb2ccf759e2b967dcd11bdee5bdf30dddd2290bd", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<4.4" - }, - "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.1|^6.0", - "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/serializer": "^5.2|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Mime\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows manipulating MIME messages", - "homepage": "https://symfony.com", - "keywords": [ - "mime", - "mime-type" - ], - "support": { - "source": "https://github.com/symfony/mime/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-01T18:18:29+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-iconv", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "927013f3aac555983a5059aada98e1907d842695" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/927013f3aac555983a5059aada98e1907d842695", - "reference": "927013f3aac555983a5059aada98e1907d842695", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-iconv": "*" - }, - "suggest": { - "ext-iconv": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Iconv extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "iconv", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/process", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "5cee9cdc4f7805e2699d9fd66991a0e6df8252a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5cee9cdc4f7805e2699d9fd66991a0e6df8252a2", - "reference": "5cee9cdc4f7805e2699d9fd66991a0e6df8252a2", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-06-27T13:16:42+00:00" - }, - { - "name": "symfony/psr-http-message-bridge", - "version": "v2.1.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "a125b93ef378c492e274f217874906fb9babdebb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/a125b93ef378c492e274f217874906fb9babdebb", - "reference": "a125b93ef378c492e274f217874906fb9babdebb", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "psr/http-message": "^1.0", - "symfony/http-foundation": "^4.4 || ^5.0 || ^6.0" - }, - "require-dev": { - "nyholm/psr7": "^1.1", - "psr/log": "^1.1 || ^2 || ^3", - "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0", - "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", - "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0", - "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0", - "symfony/phpunit-bridge": "^5.4@dev || ^6.0" - }, - "suggest": { - "nyholm/psr7": "For a super lightweight PSR-7/17 implementation" - }, - "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-main": "2.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bridge\\PsrHttpMessage\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "PSR HTTP message bridge", - "homepage": "http://symfony.com", - "keywords": [ - "http", - "http-message", - "psr-17", - "psr-7" - ], - "support": { - "issues": "https://github.com/symfony/psr-http-message-bridge/issues", - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-28T22:46:34+00:00" - }, - { - "name": "symfony/routing", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "f7751fd8b60a07f3f349947a309b5bdfce22d6ae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/f7751fd8b60a07f3f349947a309b5bdfce22d6ae", - "reference": "f7751fd8b60a07f3f349947a309b5bdfce22d6ae", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/config": "<4.2", - "symfony/dependency-injection": "<3.4", - "symfony/yaml": "<3.4" - }, - "require-dev": { - "doctrine/annotations": "^1.10.4", - "psr/log": "^1|^2|^3", - "symfony/config": "^4.2|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/expression-language": "^3.4|^4.0|^5.0", - "symfony/http-foundation": "^3.4|^4.0|^5.0", - "symfony/yaml": "^3.4|^4.0|^5.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation loader", - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Maps an HTTP request to a set of configuration variables", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "support": { - "source": "https://github.com/symfony/routing/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-20T09:59:04+00:00" - }, - { - "name": "symfony/serializer", - "version": "v4.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/serializer.git", - "reference": "6e01d63c55657930a6de03d6e36aae50af98888d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/6e01d63c55657930a6de03d6e36aae50af98888d", - "reference": "6e01d63c55657930a6de03d6e36aae50af98888d", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.0|>=3.2.0,<3.2.2", - "phpdocumentor/type-resolver": "<0.3.0|1.3.*", - "symfony/dependency-injection": "<3.4", - "symfony/property-access": "<3.4", - "symfony/property-info": "<3.4", - "symfony/yaml": "<3.4" - }, - "require-dev": { - "doctrine/annotations": "^1.10.4", - "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", - "symfony/cache": "^3.4|^4.0|^5.0", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-foundation": "^3.4|^4.0|^5.0", - "symfony/mime": "^4.4|^5.0", - "symfony/property-access": "^4.4.36|^5.3.13", - "symfony/property-info": "^3.4.13|~4.0|^5.0", - "symfony/validator": "^3.4|^4.0|^5.0", - "symfony/yaml": "^3.4|^4.0|^5.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation mapping.", - "psr/cache-implementation": "For using the metadata cache.", - "symfony/config": "For using the XML mapping loader.", - "symfony/http-foundation": "For using a MIME type guesser within the DataUriNormalizer.", - "symfony/property-access": "For using the ObjectNormalizer.", - "symfony/property-info": "To deserialize relations.", - "symfony/yaml": "For using the default YAML mapping loader." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Serializer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/serializer/tree/v4.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-19T08:38:33+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-30T19:17:29+00:00" - }, - { - "name": "symfony/translation", - "version": "v4.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "45036b1d53accc48fe9bab71ccd86d57eba0dd94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/45036b1d53accc48fe9bab71ccd86d57eba0dd94", - "reference": "45036b1d53accc48fe9bab71ccd86d57eba0dd94", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^1.1.6|^2" - }, - "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<3.4", - "symfony/http-kernel": "<4.4", - "symfony/yaml": "<3.4" - }, - "provide": { - "symfony/translation-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/console": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/finder": "~2.8|~3.0|~4.0|^5.0", - "symfony/http-kernel": "^4.4", - "symfony/intl": "^3.4|^4.0|^5.0", - "symfony/service-contracts": "^1.1.2|^2", - "symfony/yaml": "^3.4|^4.0|^5.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to internationalize your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/translation/tree/v4.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-10-03T15:15:11+00:00" - }, - { - "name": "symfony/translation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/translation-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Translation\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to translation", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-06-27T16:58:25+00:00" - }, - { - "name": "symfony/validator", - "version": "v4.4.48", - "source": { - "type": "git", - "url": "https://github.com/symfony/validator.git", - "reference": "54781a4c41efbd283b779110bf8ae7f263737775" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/54781a4c41efbd283b779110bf8ae7f263737775", - "reference": "54781a4c41efbd283b779110bf8ae7f263737775", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^1.1|^2" - }, - "conflict": { - "doctrine/lexer": "<1.1", - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/dependency-injection": "<3.4", - "symfony/http-kernel": "<4.4", - "symfony/intl": "<4.3", - "symfony/translation": ">=5.0", - "symfony/yaml": "<3.4" - }, - "require-dev": { - "doctrine/annotations": "^1.10.4", - "doctrine/cache": "^1.0|^2.0", - "egulias/email-validator": "^2.1.10|^3", - "symfony/cache": "^3.4|^4.0|^5.0", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/expression-language": "^3.4|^4.0|^5.0", - "symfony/http-client": "^4.3|^5.0", - "symfony/http-foundation": "^4.1|^5.0", - "symfony/http-kernel": "^4.4", - "symfony/intl": "^4.3|^5.0", - "symfony/mime": "^4.4|^5.0", - "symfony/property-access": "^3.4|^4.0|^5.0", - "symfony/property-info": "^3.4|^4.0|^5.0", - "symfony/translation": "^4.2", - "symfony/yaml": "^3.4|^4.0|^5.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", - "doctrine/cache": "For using the default cached annotation reader.", - "egulias/email-validator": "Strict (RFC compliant) email validation", - "psr/cache-implementation": "For using the mapping cache.", - "symfony/config": "", - "symfony/expression-language": "For using the Expression validator", - "symfony/http-foundation": "", - "symfony/intl": "", - "symfony/property-access": "For accessing properties within comparison constraints", - "symfony/property-info": "To automatically add NotNull and Type constraints", - "symfony/translation": "For translating validation errors.", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Validator\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to validate values", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/validator/tree/v4.4.48" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-10-25T13:54:11+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v5.4.26", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "e706c99b4a6f4d9383b52b80dd8c74880501e314" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e706c99b4a6f4d9383b52b80dd8c74880501e314", - "reference": "e706c99b4a6f4d9383b52b80dd8c74880501e314", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/console": "<4.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.26" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-13T07:32:46+00:00" - }, - { - "name": "symfony/yaml", - "version": "v4.4.45", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d", - "reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "^3.4|^4.0|^5.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v4.4.45" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-08-02T15:47:23+00:00" - }, - { - "name": "twig/twig", - "version": "v2.15.5", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "fc02a6af3eeb97c4bf5650debc76c2eda85ac22e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/fc02a6af3eeb97c4bf5650debc76c2eda85ac22e", - "reference": "fc02a6af3eeb97c4bf5650debc76c2eda85ac22e", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php72": "^1.8" - }, - "require-dev": { - "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.15-dev" - } - }, - "autoload": { - "psr-0": { - "Twig_": "lib/" - }, - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v2.15.5" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-05-03T17:49:41+00:00" - }, - { - "name": "typo3/phar-stream-wrapper", - "version": "v3.1.7", - "source": { - "type": "git", - "url": "https://github.com/TYPO3/phar-stream-wrapper.git", - "reference": "5cc2f04a4e2f5c7e9cc02a3bdf80fae0f3e11a8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/5cc2f04a4e2f5c7e9cc02a3bdf80fae0f3e11a8c", - "reference": "5cc2f04a4e2f5c7e9cc02a3bdf80fae0f3e11a8c", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "ext-xdebug": "*", - "phpspec/prophecy": "^1.10", - "symfony/phpunit-bridge": "^5.1" - }, - "suggest": { - "ext-fileinfo": "For PHP builtin file type guessing, otherwise uses internal processing" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "v3.x-dev" - } - }, - "autoload": { - "psr-4": { - "TYPO3\\PharStreamWrapper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Interceptors for PHP's native phar:// stream handling", - "homepage": "https://typo3.org/", - "keywords": [ - "phar", - "php", - "security", - "stream-wrapper" - ], - "support": { - "issues": "https://github.com/TYPO3/phar-stream-wrapper/issues", - "source": "https://github.com/TYPO3/phar-stream-wrapper/tree/v3.1.7" - }, - "time": "2021-09-20T19:19:13+00:00" - } - ], - "packages-dev": [ - { - "name": "behat/mink", - "version": "v1.10.0", - "source": { - "type": "git", - "url": "https://github.com/minkphp/Mink.git", - "reference": "19e58905632e7cfdc5b2bafb9b950a3521af32c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/minkphp/Mink/zipball/19e58905632e7cfdc5b2bafb9b950a3521af32c5", - "reference": "19e58905632e7cfdc5b2bafb9b950a3521af32c5", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "symfony/css-selector": "^4.4 || ^5.0 || ^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.22 || ^9.5.11", - "symfony/error-handler": "^4.4 || ^5.0 || ^6.0", - "symfony/phpunit-bridge": "^5.4 || ^6.0" - }, - "suggest": { - "behat/mink-browserkit-driver": "fast headless driver for any app without JS emulation", - "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)", - "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)", - "dmore/chrome-mink-driver": "fast and JS-enabled driver for any app (requires chromium or google chrome)" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Behat\\Mink\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Browser controller/emulator abstraction for PHP", - "homepage": "https://mink.behat.org/", - "keywords": [ - "browser", - "testing", - "web" - ], - "support": { - "issues": "https://github.com/minkphp/Mink/issues", - "source": "https://github.com/minkphp/Mink/tree/v1.10.0" - }, - "time": "2022-03-28T14:22:43+00:00" - }, - { - "name": "behat/mink-selenium2-driver", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/minkphp/MinkSelenium2Driver.git", - "reference": "e5f8421654930da725499fb92983e6948c6f973e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/minkphp/MinkSelenium2Driver/zipball/e5f8421654930da725499fb92983e6948c6f973e", - "reference": "e5f8421654930da725499fb92983e6948c6f973e", - "shasum": "" - }, - "require": { - "behat/mink": "^1.9@dev", - "ext-json": "*", - "instaclick/php-webdriver": "^1.4", - "php": ">=7.2" - }, - "require-dev": { - "mink/driver-testsuite": "dev-master", - "phpunit/phpunit": "^8.5.22 || ^9.5.11", - "symfony/error-handler": "^4.4 || ^5.0" - }, - "type": "mink-driver", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Behat\\Mink\\Driver\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Pete Otaqui", - "email": "pete@otaqui.com", - "homepage": "https://github.com/pete-otaqui" - }, - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Selenium2 (WebDriver) driver for Mink framework", - "homepage": "https://mink.behat.org/", - "keywords": [ - "ajax", - "browser", - "javascript", - "selenium", - "testing", - "webdriver" - ], - "support": { - "issues": "https://github.com/minkphp/MinkSelenium2Driver/issues", - "source": "https://github.com/minkphp/MinkSelenium2Driver/tree/v1.6.0" - }, - "time": "2022-03-28T14:55:17+00:00" - }, - { - "name": "composer/ca-bundle", - "version": "1.3.6", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "90d087e988ff194065333d16bc5cf649872d9cdb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/90d087e988ff194065333d16bc5cf649872d9cdb", - "reference": "90d087e988ff194065333d16bc5cf649872d9cdb", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.55", - "psr/log": "^1.0", - "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.3.6" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2023-06-06T12:02:59+00:00" - }, - { - "name": "composer/composer", - "version": "2.2.21", - "source": { - "type": "git", - "url": "https://github.com/composer/composer.git", - "reference": "978198befc71de0b18fc1fc5a472c03b184b504a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/978198befc71de0b18fc1fc5a472c03b184b504a", - "reference": "978198befc71de0b18fc1fc5a472c03b184b504a", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "composer/metadata-minifier": "^1.0", - "composer/pcre": "^1.0", - "composer/semver": "^3.0", - "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^2.0 || ^3.0", - "justinrainbow/json-schema": "^5.2.11", - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0 || ^2.0", - "react/promise": "^1.2 || ^2.7", - "seld/jsonlint": "^1.4", - "seld/phar-utils": "^1.0", - "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", - "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", - "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", - "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" - }, - "require-dev": { - "phpspec/prophecy": "^1.10", - "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" - }, - "suggest": { - "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", - "ext-zip": "Enabling the zip extension allows you to unzip archives", - "ext-zlib": "Allow gzip compression of HTTP requests" - }, - "bin": [ - "bin/composer" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.2-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\": "src/Composer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "https://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" - } - ], - "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", - "homepage": "https://getcomposer.org/", - "keywords": [ - "autoload", - "dependency", - "package" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/2.2.21" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2023-02-15T12:07:40+00:00" - }, - { - "name": "composer/metadata-minifier", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/composer/metadata-minifier.git", - "reference": "c549d23829536f0d0e984aaabbf02af91f443207" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", - "reference": "c549d23829536f0d0e984aaabbf02af91f443207", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "composer/composer": "^2", - "phpstan/phpstan": "^0.12.55", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\MetadataMinifier\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Small utility library that handles metadata minification and expansion.", - "keywords": [ - "composer", - "compression" - ], - "support": { - "issues": "https://github.com/composer/metadata-minifier/issues", - "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2021-04-07T13:37:33+00:00" - }, - { - "name": "composer/pcre", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/1.0.1" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-21T20:24:37+00:00" - }, - { - "name": "composer/spdx-licenses", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/composer/spdx-licenses.git", - "reference": "c848241796da2abf65837d51dce1fae55a960149" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/c848241796da2abf65837d51dce1fae55a960149", - "reference": "c848241796da2abf65837d51dce1fae55a960149", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.55", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Spdx\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "SPDX licenses list and validation library.", - "keywords": [ - "license", - "spdx", - "validator" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/spdx-licenses/issues", - "source": "https://github.com/composer/spdx-licenses/tree/1.5.7" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-05-23T07:37:50+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", - "shasum": "" - }, - "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-02-25T21:32:43+00:00" - }, - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "4be43904336affa5c2f70744a348312336afd0da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", - "reference": "4be43904336affa5c2f70744a348312336afd0da", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.4", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" - }, - "require-dev": { - "composer/composer": "*", - "ext-json": "*", - "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0", - "yoast/phpunit-polyfills": "^1.0" - }, - "type": "composer-plugin", - "extra": { - "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcbf", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/composer-installer/issues", - "source": "https://github.com/PHPCSStandards/composer-installer" - }, - "time": "2023-01-05T11:28:13+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:23:10+00:00" - }, - { - "name": "drupal/coder", - "version": "8.3.21", - "source": { - "type": "git", - "url": "https://github.com/pfrenssen/coder.git", - "reference": "a0b76c6c8ea277b07d58fa75dcacf102a203ad51" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pfrenssen/coder/zipball/a0b76c6c8ea277b07d58fa75dcacf102a203ad51", - "reference": "a0b76c6c8ea277b07d58fa75dcacf102a203ad51", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1 || ^1.0.0", - "ext-mbstring": "*", - "php": ">=7.2", - "sirbrillig/phpcs-variable-analysis": "^2.11.7", - "slevomat/coding-standard": "^8.11", - "squizlabs/php_codesniffer": "^3.7.1", - "symfony/yaml": ">=3.4.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.7.12", - "phpunit/phpunit": "^8.0" - }, - "type": "phpcodesniffer-standard", - "autoload": { - "psr-4": { - "Drupal\\": "coder_sniffer/Drupal/", - "DrupalPractice\\": "coder_sniffer/DrupalPractice/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0-or-later" - ], - "description": "Coder is a library to review Drupal code.", - "homepage": "https://www.drupal.org/project/coder", - "keywords": [ - "code review", - "phpcs", - "standards" - ], - "support": { - "issues": "https://www.drupal.org/project/issues/coder", - "source": "https://www.drupal.org/project/coder" - }, - "time": "2023-07-17T15:36:49+00:00" - }, - { - "name": "easyrdf/easyrdf", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/easyrdf/easyrdf.git", - "reference": "c7b0a9dbcb211eb7de03ee99ff5b52d17f2a8e64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/easyrdf/easyrdf/zipball/c7b0a9dbcb211eb7de03ee99ff5b52d17f2a8e64", - "reference": "c7b0a9dbcb211eb7de03ee99ff5b52d17f2a8e64", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "ext-pcre": "*", - "ext-xmlreader": "*", - "lib-libxml": "*", - "php": ">=7.1.0" - }, - "require-dev": { - "code-lts/doctum": "^5", - "ml/json-ld": "~1.0", - "phpunit/phpunit": "^7", - "semsol/arc2": "^2.4", - "squizlabs/php_codesniffer": "3.*", - "zendframework/zend-http": "~2.3" - }, - "suggest": { - "ml/json-ld": "~1.0", - "semsol/arc2": "~2.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "EasyRdf\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nicholas Humfrey", - "email": "njh@aelius.com", - "homepage": "http://www.aelius.com/njh/", - "role": "Developer" - }, - { - "name": "Alexey Zakhlestin", - "email": "indeyets@gmail.com", - "homepage": "http://indeyets.ru/", - "role": "Developer" - } - ], - "description": "EasyRdf is a PHP library designed to make it easy to consume and produce RDF.", - "homepage": "http://www.easyrdf.org/", - "keywords": [ - "Linked Data", - "RDF", - "Semantic Web", - "Turtle", - "rdfa", - "sparql" - ], - "support": { - "forum": "http://groups.google.com/group/easyrdf/", - "issues": "http://github.com/easyrdf/easyrdf/issues", - "source": "https://github.com/easyrdf/easyrdf/tree/1.1.1" - }, - "time": "2020-12-02T08:47:31+00:00" - }, - { - "name": "friends-of-behat/mink-browserkit-driver", - "version": "v1.6.1", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfBehat/MinkBrowserKitDriver.git", - "reference": "b3c29f18fe20487846e4c2733b066ec5e47f4f76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfBehat/MinkBrowserKitDriver/zipball/b3c29f18fe20487846e4c2733b066ec5e47f4f76", - "reference": "b3c29f18fe20487846e4c2733b066ec5e47f4f76", - "shasum": "" - }, - "require": { - "behat/mink": "^1.7", - "php": "^7.4|^8.0", - "symfony/browser-kit": "^4.4|^5.0|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0" - }, - "replace": { - "behat/mink-browserkit-driver": "self.version" - }, - "require-dev": { - "friends-of-behat/mink-driver-testsuite": "dev-master", - "symfony/http-kernel": "^4.4|^5.0|^6.0" - }, - "type": "mink-driver", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Behat\\Mink\\Driver\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Symfony2 BrowserKit driver for Mink framework", - "homepage": "http://mink.behat.org/", - "keywords": [ - "Mink", - "Symfony2", - "browser", - "testing" - ], - "support": { - "source": "https://github.com/FriendsOfBehat/MinkBrowserKitDriver/tree/v1.6.1" - }, - "time": "2021-12-13T10:41:57+00:00" - }, - { - "name": "instaclick/php-webdriver", - "version": "1.4.16", - "source": { - "type": "git", - "url": "https://github.com/instaclick/php-webdriver.git", - "reference": "a39a1f6dc0f4ddd8b2438fa5eb1f67755730d606" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/instaclick/php-webdriver/zipball/a39a1f6dc0f4ddd8b2438fa5eb1f67755730d606", - "reference": "a39a1f6dc0f4ddd8b2438fa5eb1f67755730d606", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.5", - "satooshi/php-coveralls": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "WebDriver": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Justin Bishop", - "email": "jubishop@gmail.com", - "role": "Developer" - }, - { - "name": "Anthon Pang", - "email": "apang@softwaredevelopment.ca", - "role": "Fork Maintainer" - } - ], - "description": "PHP WebDriver for Selenium 2", - "homepage": "http://instaclick.com/", - "keywords": [ - "browser", - "selenium", - "webdriver", - "webtest" - ], - "support": { - "issues": "https://github.com/instaclick/php-webdriver/issues", - "source": "https://github.com/instaclick/php-webdriver/tree/1.4.16" - }, - "time": "2022-10-28T13:30:35+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "5.2.12", - "source": { - "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "support": { - "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" - }, - "time": "2022-04-13T08:02:27+00:00" - }, - { - "name": "mikey179/vfsstream", - "version": "v1.6.11", - "source": { - "type": "git", - "url": "https://github.com/bovigo/vfsStream.git", - "reference": "17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f", - "reference": "17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.5|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-0": { - "org\\bovigo\\vfs\\": "src/main/php" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Frank Kleine", - "homepage": "http://frankkleine.de/", - "role": "Developer" - } - ], - "description": "Virtual file system to mock the real file system in unit tests.", - "homepage": "http://vfs.bovigo.org/", - "support": { - "issues": "https://github.com/bovigo/vfsStream/issues", - "source": "https://github.com/bovigo/vfsStream/tree/master", - "wiki": "https://github.com/bovigo/vfsStream/wiki" - }, - "time": "2022-02-23T02:02:42+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" - }, - "time": "2023-08-12T11:01:26+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.23.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "846ae76eef31c6d7790fac9bc399ecee45160b26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26", - "reference": "846ae76eef31c6d7790fac9bc399ecee45160b26", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1" - }, - "time": "2023-08-03T16:32:59+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.27", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-07-26T13:44:30+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.28", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1 || ^2", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2023-01-14T12:32:24+00:00" - }, - { - "name": "react/promise", - "version": "v2.10.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "f913fb8cceba1e6644b7b90c4bfb678ed8a3ef38" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/f913fb8cceba1e6644b7b90c4bfb678ed8a3ef38", - "reference": "f913fb8cceba1e6644b7b90c4bfb678ed8a3ef38", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "React\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" - ], - "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v2.10.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2023-05-02T15:15:43+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-02T09:26:13+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "seld/jsonlint", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", - "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", - "shasum": "" - }, - "require": { - "php": "^5.3 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.5", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" - }, - "bin": [ - "bin/jsonlint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "JSON Linter", - "keywords": [ - "json", - "linter", - "parser", - "validator" - ], - "support": { - "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" - }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", - "type": "tidelift" - } - ], - "time": "2023-05-11T13:16:46+00:00" - }, - { - "name": "seld/phar-utils", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", - "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Seld\\PharUtils\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "PHAR file format utilities, for when PHP phars you up", - "keywords": [ - "phar" - ], - "support": { - "issues": "https://github.com/Seldaek/phar-utils/issues", - "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" - }, - "time": "2022-08-31T10:31:18+00:00" - }, - { - "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.11.17", - "source": { - "type": "git", - "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "3b71162a6bf0cde2bff1752e40a1788d8273d049" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/3b71162a6bf0cde2bff1752e40a1788d8273d049", - "reference": "3b71162a6bf0cde2bff1752e40a1788d8273d049", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "squizlabs/php_codesniffer": "^3.5.6" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", - "phpcsstandards/phpcsdevcs": "^1.1", - "phpstan/phpstan": "^1.7", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0", - "sirbrillig/phpcs-import-detection": "^1.1", - "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" - }, - "type": "phpcodesniffer-standard", - "autoload": { - "psr-4": { - "VariableAnalysis\\": "VariableAnalysis/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Sam Graham", - "email": "php-codesniffer-variableanalysis@illusori.co.uk" - }, - { - "name": "Payton Swick", - "email": "payton@foolord.com" - } - ], - "description": "A PHPCS sniff to detect problems with variables.", - "keywords": [ - "phpcs", - "static analysis" - ], - "support": { - "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", - "source": "https://github.com/sirbrillig/phpcs-variable-analysis", - "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" - }, - "time": "2023-08-05T23:46:11+00:00" - }, - { - "name": "slevomat/coding-standard", - "version": "8.13.4", - "source": { - "type": "git", - "url": "https://github.com/slevomat/coding-standard.git", - "reference": "4b2af2fb17773656d02fbfb5d18024ebd19fe322" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/4b2af2fb17773656d02fbfb5d18024ebd19fe322", - "reference": "4b2af2fb17773656d02fbfb5d18024ebd19fe322", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", - "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": "^1.23.0", - "squizlabs/php_codesniffer": "^3.7.1" - }, - "require-dev": { - "phing/phing": "2.17.4", - "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.10.26", - "phpstan/phpstan-deprecation-rules": "1.1.3", - "phpstan/phpstan-phpunit": "1.3.13", - "phpstan/phpstan-strict-rules": "1.5.1", - "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.2.6" - }, - "type": "phpcodesniffer-standard", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", - "keywords": [ - "dev", - "phpcs" - ], - "support": { - "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.13.4" - }, - "funding": [ - { - "url": "https://github.com/kukulich", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", - "type": "tidelift" - } - ], - "time": "2023-07-25T10:28:55+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "symfony/browser-kit", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "2a1ff40723ef6b29c8229a860a9c8f815ad7dbbb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/2a1ff40723ef6b29c8229a860a9c8f815ad7dbbb", - "reference": "2a1ff40723ef6b29c8229a860a9c8f815ad7dbbb", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/dom-crawler": "^3.4|^4.0|^5.0", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/css-selector": "^3.4|^4.0|^5.0", - "symfony/http-client": "^4.3|^5.0", - "symfony/mime": "^4.3|^5.0", - "symfony/process": "^3.4|^4.0|^5.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/browser-kit/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-25T12:56:14+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "bd0a6737e48de45b4b0b7b6fc98c78404ddceaed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/bd0a6737e48de45b4b0b7b6fc98c78404ddceaed", - "reference": "bd0a6737e48de45b4b0b7b6fc98c78404ddceaed", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/css-selector/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-06-27T13:16:42+00:00" - }, - { - "name": "symfony/dom-crawler", - "version": "v4.4.45", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "4b8daf6c56801e6d664224261cb100b73edc78a5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b8daf6c56801e6d664224261cb100b73edc78a5", - "reference": "4b8daf6c56801e6d664224261cb100b73edc78a5", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "masterminds/html5": "<2.6" - }, - "require-dev": { - "masterminds/html5": "^2.6", - "symfony/css-selector": "^3.4|^4.0|^5.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v4.4.45" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-08-03T12:57:57+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v4.4.42", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "815412ee8971209bd4c1eecd5f4f481eacd44bf5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/815412ee8971209bd4c1eecd5f4f481eacd44bf5", - "reference": "815412ee8971209bd4c1eecd5f4f481eacd44bf5", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v4.4.42" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-20T08:49:14+00:00" - }, - { - "name": "symfony/finder", - "version": "v4.4.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "66bd787edb5e42ff59d3523f623895af05043e4f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/66bd787edb5e42ff59d3523f623895af05043e4f", - "reference": "66bd787edb5e42ff59d3523f623895af05043e4f", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v4.4.44" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-29T07:35:46+00:00" - }, - { - "name": "symfony/lock", - "version": "v4.4.46", - "source": { - "type": "git", - "url": "https://github.com/symfony/lock.git", - "reference": "8b060dd4e10f05219698ceb3a4ad735b19c1be4f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/lock/zipball/8b060dd4e10f05219698ceb3a4ad735b19c1be4f", - "reference": "8b060dd4e10f05219698ceb3a4ad735b19c1be4f", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "psr/log": "^1|^2|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/dbal": "<2.7" - }, - "require-dev": { - "doctrine/dbal": "^2.7|^3.0", - "predis/predis": "~1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Lock\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jérémy Derussé", - "email": "jeremy@derusse.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Creates and manages locks, a mechanism to provide exclusive access to a shared resource", - "homepage": "https://symfony.com", - "keywords": [ - "cas", - "flock", - "locking", - "mutex", - "redlock", - "semaphore" - ], - "support": { - "source": "https://github.com/symfony/lock/tree/v4.4.46" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-19T08:41:12+00:00" - }, - { - "name": "symfony/phpunit-bridge", - "version": "v5.4.26", - "source": { - "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "d04639b395e25efa4260fc5b12a9fa1eafb38a64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/d04639b395e25efa4260fc5b12a9fa1eafb38a64", - "reference": "d04639b395e25efa4260fc5b12a9fa1eafb38a64", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" - }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", - "extra": { - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v5.4.26" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-12T15:44:31+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - } - ], - "aliases": [], - "minimum-stability": "dev", - "stability-flags": { - "drupal/core": 20, - "drupal/core-project-message": 20, - "drupal/core-vendor-hardening": 20 - }, - "prefer-stable": true, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "platform-overrides": { - "php": "7.3.0" - }, - "plugin-api-version": "2.2.0" -} diff --git a/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/CoreRecommended/composer.json b/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/CoreRecommended/composer.json index 83bbbcc7b0..92073a2784 100644 --- a/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/CoreRecommended/composer.json +++ b/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/CoreRecommended/composer.json @@ -9,25 +9,25 @@ "require": { "drupal/core": "9.5.x-dev", "asm89/stack-cors": "~1.3.0", - "composer/semver": "~3.3.2", + "composer/semver": "~3.4.2", "doctrine/annotations": "~1.14.3", - "doctrine/deprecations": "~v1.1.1", - "doctrine/lexer": "~2.1.0", + "doctrine/deprecations": "~1.1.3", + "doctrine/lexer": "~2.1.1", "doctrine/reflection": "~1.2.4", "egulias/email-validator": "~3.2.6", - "guzzlehttp/guzzle": "~7.7.0", - "guzzlehttp/promises": "~2.0.1", - "guzzlehttp/psr7": "~2.6.0", + "guzzlehttp/guzzle": "~7.9.2", + "guzzlehttp/promises": "~2.0.3", + "guzzlehttp/psr7": "~2.7.0", "longwave/laminas-diactoros": "~2.14.2", - "masterminds/html5": "~2.8.1", - "pear/archive_tar": "~1.4.14", + "masterminds/html5": "~2.9.0", + "pear/archive_tar": "~1.5.0", "pear/console_getopt": "~v1.4.3", - "pear/pear-core-minimal": "~v1.10.13", + "pear/pear-core-minimal": "~v1.10.15", "pear/pear_exception": "~v1.0.2", "psr/cache": "~3.0.0", "psr/container": "~1.1.2", - "psr/http-client": "~1.0.2", - "psr/http-factory": "~1.0.2", + "psr/http-client": "~1.0.3", + "psr/http-factory": "~1.1.0", "psr/http-message": "~1.1", "psr/log": "~1.1.4", "ralouphie/getallheaders": "~3.0.3", @@ -36,31 +36,31 @@ "symfony/console": "~v4.4.49", "symfony/debug": "~v4.4.44", "symfony/dependency-injection": "~v4.4.49", - "symfony/deprecation-contracts": "~v3.3.0", + "symfony/deprecation-contracts": "~v3.5.0", "symfony/error-handler": "~v4.4.44", "symfony/event-dispatcher": "~v4.4.44", - "symfony/event-dispatcher-contracts": "~v1.1.13", - "symfony/http-client-contracts": "~v2.5.2", + "symfony/event-dispatcher-contracts": "~v1.10.0", + "symfony/http-client-contracts": "~v2.5.3", "symfony/http-foundation": "~v4.4.49", - "symfony/http-kernel": "~v4.4.50", + "symfony/http-kernel": "~v4.4.51", "symfony/mime": "~v5.4.13", - "symfony/polyfill-ctype": "~v1.27.0", - "symfony/polyfill-iconv": "~v1.27.0", - "symfony/polyfill-intl-idn": "~v1.27.0", - "symfony/polyfill-intl-normalizer": "~v1.27.0", - "symfony/polyfill-mbstring": "~v1.27.0", - "symfony/polyfill-php80": "~v1.27.0", + "symfony/polyfill-ctype": "~v1.30.0", + "symfony/polyfill-iconv": "~v1.30.0", + "symfony/polyfill-intl-idn": "~v1.30.0", + "symfony/polyfill-intl-normalizer": "~v1.30.0", + "symfony/polyfill-mbstring": "~v1.30.0", + "symfony/polyfill-php80": "~v1.30.0", "symfony/process": "~v4.4.44", "symfony/psr-http-message-bridge": "~v2.1.4", "symfony/routing": "~v4.4.44", "symfony/serializer": "~v4.4.47", - "symfony/service-contracts": "~v2.5.2", + "symfony/service-contracts": "~v2.5.3", "symfony/translation": "~v4.4.47", - "symfony/translation-contracts": "~v2.5.2", + "symfony/translation-contracts": "~v2.5.3", "symfony/validator": "~v4.4.48", - "symfony/var-dumper": "~v5.4.26", + "symfony/var-dumper": "~v5.4.42", "symfony/yaml": "~v4.4.45", - "twig/twig": "~v2.15.5", + "twig/twig": "~v2.16.0", "typo3/phar-stream-wrapper": "~v3.1.7" } } diff --git a/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/PinnedDevDependencies/composer.json b/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/PinnedDevDependencies/composer.json index 01acdd1f7c..945c52aac1 100644 --- a/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/PinnedDevDependencies/composer.json +++ b/tests/Frameworks/Drupal/Version_9_5/composer/Metapackage/PinnedDevDependencies/composer.json @@ -8,67 +8,67 @@ }, "require": { "drupal/core": "9.5.x-dev", - "behat/mink": "v1.10.0", - "behat/mink-selenium2-driver": "v1.6.0", - "composer/ca-bundle": "1.3.6", - "composer/composer": "2.2.21", + "behat/mink": "v1.11.0", + "behat/mink-selenium2-driver": "v1.7.0", + "composer/ca-bundle": "1.5.1", + "composer/composer": "2.2.24", "composer/metadata-minifier": "1.0.0", "composer/pcre": "1.0.1", - "composer/spdx-licenses": "1.5.7", - "composer/xdebug-handler": "3.0.3", + "composer/spdx-licenses": "1.5.8", + "composer/xdebug-handler": "3.0.5", "dealerdirect/phpcodesniffer-composer-installer": "v1.0.0", "doctrine/instantiator": "2.0.0", - "drupal/coder": "8.3.21", + "drupal/coder": "8.3.24", "easyrdf/easyrdf": "1.1.1", - "friends-of-behat/mink-browserkit-driver": "v1.6.1", - "instaclick/php-webdriver": "1.4.16", - "justinrainbow/json-schema": "5.2.12", + "friends-of-behat/mink-browserkit-driver": "v1.6.2", + "instaclick/php-webdriver": "1.4.19", + "justinrainbow/json-schema": "5.3.0", "mikey179/vfsstream": "v1.6.11", - "myclabs/deep-copy": "1.11.1", - "nikic/php-parser": "v4.17.1", - "phar-io/manifest": "2.0.3", + "myclabs/deep-copy": "1.12.0", + "nikic/php-parser": "v5.1.0", + "phar-io/manifest": "2.0.4", "phar-io/version": "3.2.1", "phpdocumentor/reflection-common": "2.2.0", - "phpdocumentor/reflection-docblock": "5.3.0", - "phpdocumentor/type-resolver": "1.7.3", - "phpspec/prophecy": "v1.17.0", - "phpstan/phpdoc-parser": "1.23.1", - "phpunit/php-code-coverage": "9.2.27", + "phpdocumentor/reflection-docblock": "5.4.1", + "phpdocumentor/type-resolver": "1.8.2", + "phpspec/prophecy": "v1.19.0", + "phpstan/phpdoc-parser": "1.29.1", + "phpunit/php-code-coverage": "9.2.31", "phpunit/php-file-iterator": "3.0.6", "phpunit/php-invoker": "3.1.1", "phpunit/php-text-template": "2.0.4", "phpunit/php-timer": "5.0.3", "phpunit/phpunit": "9.5.28", - "react/promise": "v2.10.0", - "sebastian/cli-parser": "1.0.1", + "react/promise": "v2.11.0", + "sebastian/cli-parser": "1.0.2", "sebastian/code-unit": "1.0.8", "sebastian/code-unit-reverse-lookup": "2.0.3", "sebastian/comparator": "4.0.8", - "sebastian/complexity": "2.0.2", - "sebastian/diff": "4.0.5", + "sebastian/complexity": "2.0.3", + "sebastian/diff": "4.0.6", "sebastian/environment": "5.1.5", - "sebastian/exporter": "4.0.5", - "sebastian/global-state": "5.0.6", - "sebastian/lines-of-code": "1.0.3", + "sebastian/exporter": "4.0.6", + "sebastian/global-state": "5.0.7", + "sebastian/lines-of-code": "1.0.4", "sebastian/object-enumerator": "4.0.4", "sebastian/object-reflector": "2.0.4", "sebastian/recursion-context": "4.0.5", - "sebastian/resource-operations": "3.0.3", + "sebastian/resource-operations": "3.0.4", "sebastian/type": "3.2.1", "sebastian/version": "3.0.2", - "seld/jsonlint": "1.10.0", + "seld/jsonlint": "1.11.0", "seld/phar-utils": "1.2.1", - "sirbrillig/phpcs-variable-analysis": "v2.11.17", - "slevomat/coding-standard": "8.13.4", - "squizlabs/php_codesniffer": "3.7.2", + "sirbrillig/phpcs-variable-analysis": "v2.11.19", + "slevomat/coding-standard": "8.15.0", + "squizlabs/php_codesniffer": "3.10.2", "symfony/browser-kit": "v4.4.44", "symfony/css-selector": "v4.4.44", "symfony/dom-crawler": "v4.4.45", "symfony/filesystem": "v4.4.42", "symfony/finder": "v4.4.44", "symfony/lock": "v4.4.46", - "symfony/phpunit-bridge": "v5.4.26", - "theseer/tokenizer": "1.2.1", + "symfony/phpunit-bridge": "v5.4.42", + "theseer/tokenizer": "1.2.3", "webmozart/assert": "1.11.0" } } diff --git a/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/DependencyInjection/composer.json b/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/DependencyInjection/composer.json index fc523503db..be6eea2034 100644 --- a/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/DependencyInjection/composer.json +++ b/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/DependencyInjection/composer.json @@ -15,7 +15,7 @@ "require": { "php": ">=7.3.0", "symfony/dependency-injection": "^4.4", - "symfony/service-contracts": "v2.5.2" + "symfony/service-contracts": "v2.5.3" }, "suggest": { "symfony/expression-language": "For using expressions in service container configuration" diff --git a/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/composer.json b/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/composer.json index 3b934854fa..26e5b2ca3b 100644 --- a/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/composer.json +++ b/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/composer.json @@ -10,7 +10,7 @@ "php": ">=7.3.0", "symfony/dependency-injection": "^4.4", "symfony/event-dispatcher": "^4.4", - "symfony/event-dispatcher-contracts": "v1.1.13" + "symfony/event-dispatcher-contracts": "v1.10.0" }, "autoload": { "psr-4": { diff --git a/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Command/InstallCommand.php b/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Command/InstallCommand.php index 40f091c060..859f9769c4 100644 --- a/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Command/InstallCommand.php +++ b/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Command/InstallCommand.php @@ -143,7 +143,7 @@ protected function install($class_loader, SymfonyStyle $io, $profile, $langcode, 'install_settings_form' => [ 'driver' => 'mysql', 'mysql' => [ - 'database' => 'test', + 'database' => 'drupal95', 'username' => 'test', 'password' => 'test', 'host' => 'mysql_integration', diff --git a/tests/Frameworks/Drupal/Version_9_5/scripts/erase_drupal_db.php b/tests/Frameworks/Drupal/Version_9_5/scripts/erase_drupal_db.php index df21a6ce8d..1fadcf78fd 100644 --- a/tests/Frameworks/Drupal/Version_9_5/scripts/erase_drupal_db.php +++ b/tests/Frameworks/Drupal/Version_9_5/scripts/erase_drupal_db.php @@ -1,36 +1,38 @@ query('DROP TABLE IF EXISTS cache_bootstrap'); -$pdo->query('DROP TABLE IF EXISTS cache_config'); -$pdo->query('DROP TABLE IF EXISTS cache_container'); // Drupal doesn't like us dropping this table (PDOException) -$pdo->query('DROP TABLE IF EXISTS cache_data'); -$pdo->query('DROP TABLE IF EXISTS cache_default'); -$pdo->query('DROP TABLE IF EXISTS cache_discovery'); -$pdo->query('DROP TABLE IF EXISTS cache_entity'); -$pdo->query('DROP TABLE IF EXISTS config'); -$pdo->query('DROP TABLE IF EXISTS file_managed'); -$pdo->query('DROP TABLE IF EXISTS file_usage'); -$pdo->query('DROP TABLE IF EXISTS key_value'); -$pdo->query('DROP TABLE IF EXISTS key_value_expire'); -$pdo->query('DROP TABLE IF EXISTS menu_tree'); -$pdo->query('DROP TABLE IF EXISTS node'); -$pdo->query('DROP TABLE IF EXISTS node__body'); -$pdo->query('DROP TABLE IF EXISTS node_access'); -$pdo->query('DROP TABLE IF EXISTS node_field_data'); -$pdo->query('DROP TABLE IF EXISTS node_field_revision'); -$pdo->query('DROP TABLE IF EXISTS node_revision'); -$pdo->query('DROP TABLE IF EXISTS node_revision__body'); -$pdo->query('DROP TABLE IF EXISTS path_alias'); -$pdo->query('DROP TABLE IF EXISTS path_alias_revision'); -$pdo->query('DROP TABLE IF EXISTS queue'); -$pdo->query('DROP TABLE IF EXISTS router'); -$pdo->query('DROP TABLE IF EXISTS semaphore'); -$pdo->query('DROP TABLE IF EXISTS sequences'); -$pdo->query('DROP TABLE IF EXISTS sessions'); -$pdo->query('DROP TABLE IF EXISTS user__roles'); -$pdo->query('DROP TABLE IF EXISTS users'); -$pdo->query('DROP TABLE IF EXISTS users_data'); -$pdo->query('DROP TABLE IF EXISTS users_field_data'); -$pdo->query('DROP TABLE IF EXISTS watchdog'); +$pdo->query("CREATE DATABASE IF NOT EXISTS drupal95"); + +$pdo->query('DROP TABLE IF EXISTS drupal95.cache_bootstrap'); +$pdo->query('DROP TABLE IF EXISTS drupal95.cache_config'); +$pdo->query('DROP TABLE IF EXISTS drupal95.cache_container'); // Drupal doesn't like us dropping this table (PDOException) +$pdo->query('DROP TABLE IF EXISTS drupal95.cache_data'); +$pdo->query('DROP TABLE IF EXISTS drupal95.cache_default'); +$pdo->query('DROP TABLE IF EXISTS drupal95.cache_discovery'); +$pdo->query('DROP TABLE IF EXISTS drupal95.cache_entity'); +$pdo->query('DROP TABLE IF EXISTS drupal95.config'); +$pdo->query('DROP TABLE IF EXISTS drupal95.file_managed'); +$pdo->query('DROP TABLE IF EXISTS drupal95.file_usage'); +$pdo->query('DROP TABLE IF EXISTS drupal95.key_value'); +$pdo->query('DROP TABLE IF EXISTS drupal95.key_value_expire'); +$pdo->query('DROP TABLE IF EXISTS drupal95.menu_tree'); +$pdo->query('DROP TABLE IF EXISTS drupal95.node'); +$pdo->query('DROP TABLE IF EXISTS drupal95.node__body'); +$pdo->query('DROP TABLE IF EXISTS drupal95.node_access'); +$pdo->query('DROP TABLE IF EXISTS drupal95.node_field_data'); +$pdo->query('DROP TABLE IF EXISTS drupal95.node_field_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal95.node_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal95.node_revision__body'); +$pdo->query('DROP TABLE IF EXISTS drupal95.path_alias'); +$pdo->query('DROP TABLE IF EXISTS drupal95.path_alias_revision'); +$pdo->query('DROP TABLE IF EXISTS drupal95.queue'); +$pdo->query('DROP TABLE IF EXISTS drupal95.router'); +$pdo->query('DROP TABLE IF EXISTS drupal95.semaphore'); +$pdo->query('DROP TABLE IF EXISTS drupal95.sequences'); +$pdo->query('DROP TABLE IF EXISTS drupal95.sessions'); +$pdo->query('DROP TABLE IF EXISTS drupal95.user__roles'); +$pdo->query('DROP TABLE IF EXISTS drupal95.users'); +$pdo->query('DROP TABLE IF EXISTS drupal95.users_data'); +$pdo->query('DROP TABLE IF EXISTS drupal95.users_field_data'); +$pdo->query('DROP TABLE IF EXISTS drupal95.watchdog'); diff --git a/tests/Frameworks/Laravel/Octane/.gitignore b/tests/Frameworks/Laravel/Octane/.gitignore index 7fe978f851..50aea355c7 100644 --- a/tests/Frameworks/Laravel/Octane/.gitignore +++ b/tests/Frameworks/Laravel/Octane/.gitignore @@ -17,3 +17,4 @@ yarn-error.log /.fleet /.idea /.vscode +/swoole.ini diff --git a/tests/Frameworks/Laravel/Version_10_x/composer.json b/tests/Frameworks/Laravel/Version_10_x/composer.json index 9756f363d4..e0524363d1 100644 --- a/tests/Frameworks/Laravel/Version_10_x/composer.json +++ b/tests/Frameworks/Laravel/Version_10_x/composer.json @@ -36,6 +36,7 @@ "scripts": { "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php -r \"(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS laravel10');\"", "@php artisan package:discover --ansi", "@php artisan migrate:fresh --force" ], diff --git a/tests/Frameworks/Laravel/Version_10_x/config/database.php b/tests/Frameworks/Laravel/Version_10_x/config/database.php index cb7cc4d59e..53a0c43b15 100644 --- a/tests/Frameworks/Laravel/Version_10_x/config/database.php +++ b/tests/Frameworks/Laravel/Version_10_x/config/database.php @@ -48,7 +48,7 @@ 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', 'mysql_integration'), 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'test'), + 'database' => env('DB_DATABASE', 'laravel10'), 'username' => env('DB_USERNAME', 'test'), 'password' => env('DB_PASSWORD', 'test'), 'unix_socket' => env('DB_SOCKET', ''), diff --git a/tests/Frameworks/Laravel/Version_4_2/app/config/database.php b/tests/Frameworks/Laravel/Version_4_2/app/config/database.php index a883e58d8e..5d27cf0566 100644 --- a/tests/Frameworks/Laravel/Version_4_2/app/config/database.php +++ b/tests/Frameworks/Laravel/Version_4_2/app/config/database.php @@ -55,7 +55,7 @@ 'mysql' => array( 'driver' => 'mysql', 'host' => 'mysql_integration', - 'database' => 'test', + 'database' => 'laravel42', 'username' => 'test', 'password' => 'test', 'charset' => 'utf8', diff --git a/tests/Frameworks/Laravel/Version_4_2/composer.json b/tests/Frameworks/Laravel/Version_4_2/composer.json index 445d6415c9..ecf45b04f6 100644 --- a/tests/Frameworks/Laravel/Version_4_2/composer.json +++ b/tests/Frameworks/Laravel/Version_4_2/composer.json @@ -24,11 +24,13 @@ }, "scripts": { "post-install-cmd": [ + "php -r \"(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS laravel42');\"", "php artisan clear-compiled", "php artisan optimize", "php artisan migrate --force" ], "post-update-cmd": [ + "php -r \"(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS laravel42');\"", "php artisan clear-compiled", "php artisan optimize", "php artisan migrate --force" diff --git a/tests/Frameworks/Laravel/Version_5_7/composer.json b/tests/Frameworks/Laravel/Version_5_7/composer.json index 5b6da71e5c..19d687012c 100644 --- a/tests/Frameworks/Laravel/Version_5_7/composer.json +++ b/tests/Frameworks/Laravel/Version_5_7/composer.json @@ -45,6 +45,7 @@ ], "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php -r \"(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS laravel57');\"", "@php artisan package:discover", "@php artisan migrate:fresh --force" ], diff --git a/tests/Frameworks/Laravel/Version_5_7/config/database.php b/tests/Frameworks/Laravel/Version_5_7/config/database.php index 8b11981f9c..3594b0d549 100644 --- a/tests/Frameworks/Laravel/Version_5_7/config/database.php +++ b/tests/Frameworks/Laravel/Version_5_7/config/database.php @@ -43,7 +43,7 @@ 'driver' => 'mysql', 'host' => env('DB_HOST', 'mysql_integration'), 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'test'), + 'database' => env('DB_DATABASE', 'laravel57'), 'username' => env('DB_USERNAME', 'test'), 'password' => env('DB_PASSWORD', 'test'), 'unix_socket' => env('DB_SOCKET', ''), diff --git a/tests/Frameworks/Laravel/Version_5_8/composer.json b/tests/Frameworks/Laravel/Version_5_8/composer.json index 683f99ddce..cd13f2a6c9 100644 --- a/tests/Frameworks/Laravel/Version_5_8/composer.json +++ b/tests/Frameworks/Laravel/Version_5_8/composer.json @@ -54,6 +54,7 @@ "scripts": { "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php -r \"(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS laravel58');\"", "@php artisan package:discover --ansi", "@php artisan migrate:fresh --force" ], diff --git a/tests/Frameworks/Laravel/Version_5_8/config/database.php b/tests/Frameworks/Laravel/Version_5_8/config/database.php index b3c9ea298d..23fdd4eb69 100644 --- a/tests/Frameworks/Laravel/Version_5_8/config/database.php +++ b/tests/Frameworks/Laravel/Version_5_8/config/database.php @@ -44,7 +44,7 @@ 'driver' => 'mysql', 'host' => env('DB_HOST', 'mysql_integration'), 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'test'), + 'database' => env('DB_DATABASE', 'laravel58'), 'username' => env('DB_USERNAME', 'test'), 'password' => env('DB_PASSWORD', 'test'), 'unix_socket' => env('DB_SOCKET', ''), diff --git a/tests/Frameworks/Laravel/Version_8_x/.env b/tests/Frameworks/Laravel/Version_8_x/.env index 7573c140eb..528c285056 100644 --- a/tests/Frameworks/Laravel/Version_8_x/.env +++ b/tests/Frameworks/Laravel/Version_8_x/.env @@ -10,7 +10,7 @@ LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=mysql_integration DB_PORT=3306 -DB_DATABASE=test +DB_DATABASE=laravel8 DB_USERNAME=test DB_PASSWORD=test diff --git a/tests/Frameworks/Laravel/Version_8_x/.env.example b/tests/Frameworks/Laravel/Version_8_x/.env.example index 7573c140eb..528c285056 100644 --- a/tests/Frameworks/Laravel/Version_8_x/.env.example +++ b/tests/Frameworks/Laravel/Version_8_x/.env.example @@ -10,7 +10,7 @@ LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=mysql_integration DB_PORT=3306 -DB_DATABASE=test +DB_DATABASE=laravel8 DB_USERNAME=test DB_PASSWORD=test diff --git a/tests/Frameworks/Laravel/Version_8_x/composer.json b/tests/Frameworks/Laravel/Version_8_x/composer.json index 2a0e00ad25..59e68a4857 100644 --- a/tests/Frameworks/Laravel/Version_8_x/composer.json +++ b/tests/Frameworks/Laravel/Version_8_x/composer.json @@ -50,6 +50,7 @@ "scripts": { "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php -r \"(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS laravel8');\"", "@php artisan package:discover --ansi", "@php artisan migrate:fresh --force" ], diff --git a/tests/Frameworks/Laravel/Version_8_x/config/database.php b/tests/Frameworks/Laravel/Version_8_x/config/database.php index ba3dd46dda..4268fb0288 100644 --- a/tests/Frameworks/Laravel/Version_8_x/config/database.php +++ b/tests/Frameworks/Laravel/Version_8_x/config/database.php @@ -48,7 +48,7 @@ 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', 'mysql_integration'), 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'test'), + 'database' => env('DB_DATABASE', 'laravel8'), 'username' => env('DB_USERNAME', 'test'), 'password' => env('DB_PASSWORD', 'test'), 'unix_socket' => env('DB_SOCKET', ''), diff --git a/tests/Frameworks/Laravel/Version_9_x/composer.json b/tests/Frameworks/Laravel/Version_9_x/composer.json index 87fc0353f9..66ce2a9a7b 100644 --- a/tests/Frameworks/Laravel/Version_9_x/composer.json +++ b/tests/Frameworks/Laravel/Version_9_x/composer.json @@ -36,6 +36,7 @@ "scripts": { "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php -r \"(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS laravel9');\"", "@php artisan package:discover --ansi", "@php artisan migrate:fresh --force" ], diff --git a/tests/Frameworks/Laravel/Version_9_x/config/database.php b/tests/Frameworks/Laravel/Version_9_x/config/database.php index 9a2d3e5648..a5778340eb 100644 --- a/tests/Frameworks/Laravel/Version_9_x/config/database.php +++ b/tests/Frameworks/Laravel/Version_9_x/config/database.php @@ -48,7 +48,7 @@ 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', 'mysql_integration'), 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'test'), + 'database' => env('DB_DATABASE', 'laravel9'), 'username' => env('DB_USERNAME', 'test'), 'password' => env('DB_PASSWORD', 'test'), 'unix_socket' => env('DB_SOCKET', ''), diff --git a/tests/Frameworks/Magento/Version_2_3/install-magento b/tests/Frameworks/Magento/Version_2_3/install-magento index c34cd5660f..90738182a5 100755 --- a/tests/Frameworks/Magento/Version_2_3/install-magento +++ b/tests/Frameworks/Magento/Version_2_3/install-magento @@ -1,5 +1,7 @@ #!/usr/bin/env bash +php -r "(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS magento23');" + php -d memory_limit=1G ./bin/magento setup:install \ --base-url=http://localhost/ \ --backend-frontname=admin \ @@ -7,7 +9,7 @@ php -d memory_limit=1G ./bin/magento setup:install \ --timezone=America/Los_Angeles \ --currency=USD \ --db-host=mysql_integration \ - --db-name=test \ + --db-name=magento23 \ --db-user=test \ --db-password=test \ --use-secure=0 \ diff --git a/tests/Frameworks/Magento/Version_2_4/install-magento b/tests/Frameworks/Magento/Version_2_4/install-magento index 8704e879bb..e2725abcd9 100755 --- a/tests/Frameworks/Magento/Version_2_4/install-magento +++ b/tests/Frameworks/Magento/Version_2_4/install-magento @@ -1,5 +1,7 @@ #!/usr/bin/env bash +php -r "(new PDO('mysql:host=mysql_integration', 'test', 'test'))->exec('CREATE DATABASE IF NOT EXISTS magento24');" + php -d memory_limit=1G ./bin/magento setup:install \ --base-url=http://localhost/ \ --backend-frontname=admin \ @@ -7,7 +9,7 @@ php -d memory_limit=1G ./bin/magento setup:install \ --timezone=America/Los_Angeles \ --currency=USD \ --db-host=mysql_integration \ - --db-name=test \ + --db-name=magento24 \ --db-user=test \ --db-password=test \ --use-secure=0 \ diff --git a/tests/Frameworks/Swoole/index.php b/tests/Frameworks/Swoole/index.php index 3eb0a4e914..0d90baade6 100644 --- a/tests/Frameworks/Swoole/index.php +++ b/tests/Frameworks/Swoole/index.php @@ -2,7 +2,7 @@ require __DIR__ . '/../../vendor/autoload.php'; -$http = new Swoole\Http\Server("0.0.0.0", 9999); +$http = new Swoole\Http\Server("0.0.0.0", $argv[1]); $http->set([ 'worker_num' => 2 ]); diff --git a/tests/Frameworks/Symfony/Version_3_3/app/config/parameters.yml.dist b/tests/Frameworks/Symfony/Version_3_3/app/config/parameters.yml.dist index e03069b4d3..cc3dcbfed5 100644 --- a/tests/Frameworks/Symfony/Version_3_3/app/config/parameters.yml.dist +++ b/tests/Frameworks/Symfony/Version_3_3/app/config/parameters.yml.dist @@ -4,7 +4,7 @@ parameters: database_host: mysql_integration database_port: null - database_name: test + database_name: symfony33 database_user: test database_password: test # You should uncomment this if you want to use pdo_sqlite diff --git a/tests/Frameworks/Symfony/Version_3_3/composer.json b/tests/Frameworks/Symfony/Version_3_3/composer.json index 0d7ae5555a..b6875580d0 100644 --- a/tests/Frameworks/Symfony/Version_3_3/composer.json +++ b/tests/Frameworks/Symfony/Version_3_3/composer.json @@ -50,7 +50,7 @@ "@symfony-scripts", "rm -rf var/cache/dev/*", "rm -rf var/cache/prod/*", - "@php bin/console doctrine:database:drop --force", + "@php bin/console doctrine:database:drop --force || true", "@php bin/console doctrine:database:create", "@php bin/console doctrine:schema:update --force" ] diff --git a/tests/Frameworks/Symfony/Version_4_4/composer.json b/tests/Frameworks/Symfony/Version_4_4/composer.json index 70a73793e7..64d0977864 100644 --- a/tests/Frameworks/Symfony/Version_4_4/composer.json +++ b/tests/Frameworks/Symfony/Version_4_4/composer.json @@ -90,7 +90,7 @@ "post-autoload-dump": [ "rm -rf var/cache/dev/*", "rm -rf var/cache/prod/*", - "@php bin/console doctrine:database:drop --force", + "@php bin/console doctrine:database:drop --force || true", "@php bin/console doctrine:database:create", "@php bin/console doctrine:migrations:migrate -n" ] diff --git a/tests/Frameworks/Symfony/Version_4_4/config/packages/doctrine.yaml b/tests/Frameworks/Symfony/Version_4_4/config/packages/doctrine.yaml index c8ab818b9d..e858f69857 100644 --- a/tests/Frameworks/Symfony/Version_4_4/config/packages/doctrine.yaml +++ b/tests/Frameworks/Symfony/Version_4_4/config/packages/doctrine.yaml @@ -1,6 +1,6 @@ doctrine: dbal: - url: 'mysql://test:test@mysql_integration:3306/test?serverVersion=5&charset=utf8mb4' + url: 'mysql://test:test@mysql_integration:3306/symfony44?serverVersion=5&charset=utf8mb4' # IMPORTANT: You MUST configure your server version, # either here or in the DATABASE_URL env var (see .env file) diff --git a/tests/Frameworks/Symfony/Version_5_2/composer.json b/tests/Frameworks/Symfony/Version_5_2/composer.json index 38343a82db..21be2382a9 100644 --- a/tests/Frameworks/Symfony/Version_5_2/composer.json +++ b/tests/Frameworks/Symfony/Version_5_2/composer.json @@ -60,7 +60,7 @@ "@auto-scripts" ], "post-autoload-dump": [ - "@php bin/console doctrine:database:drop --force", + "@php bin/console doctrine:database:drop --force || true", "@php bin/console doctrine:database:create", "@php bin/console doctrine:migrations:migrate -n" ] diff --git a/tests/Frameworks/Symfony/Version_5_2/config/packages/doctrine.yaml b/tests/Frameworks/Symfony/Version_5_2/config/packages/doctrine.yaml index 91b3d8253b..963c4347b9 100644 --- a/tests/Frameworks/Symfony/Version_5_2/config/packages/doctrine.yaml +++ b/tests/Frameworks/Symfony/Version_5_2/config/packages/doctrine.yaml @@ -1,6 +1,6 @@ doctrine: dbal: - url: 'mysql://test:test@mysql_integration:3306/test?serverVersion=5&charset=utf8mb4' + url: 'mysql://test:test@mysql_integration:3306/symfony52?serverVersion=5&charset=utf8mb4' # IMPORTANT: You MUST configure your server version, # either here or in the DATABASE_URL env var (see .env file) diff --git a/tests/Frameworks/Symfony/Version_6_2/composer.json b/tests/Frameworks/Symfony/Version_6_2/composer.json index df2e44bbe0..aeaa94cd72 100644 --- a/tests/Frameworks/Symfony/Version_6_2/composer.json +++ b/tests/Frameworks/Symfony/Version_6_2/composer.json @@ -67,7 +67,7 @@ "post-autoload-dump": [ "rm -rf var/cache/dev/*", "rm -rf var/cache/prod/*", - "@php bin/console doctrine:database:drop --force", + "@php bin/console doctrine:database:drop --force || true", "@php bin/console doctrine:database:create", "@php bin/console doctrine:migrations:migrate -n" ] diff --git a/tests/Frameworks/Symfony/Version_6_2/config/packages/doctrine.yaml b/tests/Frameworks/Symfony/Version_6_2/config/packages/doctrine.yaml index 85f2faddb4..ab5991e154 100644 --- a/tests/Frameworks/Symfony/Version_6_2/config/packages/doctrine.yaml +++ b/tests/Frameworks/Symfony/Version_6_2/config/packages/doctrine.yaml @@ -1,6 +1,6 @@ doctrine: dbal: - url: 'mysql://test:test@mysql_integration:3306/test?serverVersion=5&charset=utf8mb4' + url: 'mysql://test:test@mysql_integration:3306/symfony62?serverVersion=5&charset=utf8mb4' # IMPORTANT: You MUST configure your server version, # either here or in the DATABASE_URL env var (see .env file) diff --git a/tests/Frameworks/Symfony/Version_7_0/composer.json b/tests/Frameworks/Symfony/Version_7_0/composer.json index 7e89f29fc8..0f8033def5 100644 --- a/tests/Frameworks/Symfony/Version_7_0/composer.json +++ b/tests/Frameworks/Symfony/Version_7_0/composer.json @@ -67,7 +67,7 @@ "post-autoload-dump": [ "rm -rf var/cache/dev/*", "rm -rf var/cache/prod/*", - "@php bin/console doctrine:database:drop --force", + "@php bin/console doctrine:database:drop --force || true", "@php bin/console doctrine:database:create", "@php bin/console doctrine:migrations:migrate -n" ] diff --git a/tests/Frameworks/Symfony/Version_7_0/config/packages/doctrine.yaml b/tests/Frameworks/Symfony/Version_7_0/config/packages/doctrine.yaml index 11d765735f..6e9e0bea02 100644 --- a/tests/Frameworks/Symfony/Version_7_0/config/packages/doctrine.yaml +++ b/tests/Frameworks/Symfony/Version_7_0/config/packages/doctrine.yaml @@ -1,6 +1,6 @@ doctrine: dbal: - url: 'mysql://test:test@mysql_integration:3306/test?serverVersion=5&charset=utf8mb4' + url: 'mysql://test:test@mysql_integration:3306/symfony70?serverVersion=5&charset=utf8mb4' # IMPORTANT: You MUST configure your server version, # either here or in the DATABASE_URL env var (see .env file) diff --git a/tests/Frameworks/WordPress/Version_4_8/wp-config.php b/tests/Frameworks/WordPress/Version_4_8/wp-config.php index b8f0eed437..35d490484c 100644 --- a/tests/Frameworks/WordPress/Version_4_8/wp-config.php +++ b/tests/Frameworks/WordPress/Version_4_8/wp-config.php @@ -20,7 +20,7 @@ // ** MySQL settings - You can get this info from your web host ** // /** The name of the database for WordPress */ -define('DB_NAME', 'test'); +define('DB_NAME', 'wp48'); /** MySQL database username */ define('DB_USER', 'test'); diff --git a/tests/Frameworks/WordPress/Version_4_8/wp_2019-10-01.sql b/tests/Frameworks/WordPress/Version_4_8/wp_2019-10-01.sql index 344ae0e119..bf38aae351 100644 --- a/tests/Frameworks/WordPress/Version_4_8/wp_2019-10-01.sql +++ b/tests/Frameworks/WordPress/Version_4_8/wp_2019-10-01.sql @@ -121,8 +121,8 @@ LOCK TABLES `wp_options` WRITE; INSERT INTO `wp_options` (`option_id`, `option_name`, `option_value`, `autoload`) VALUES - (1,'siteurl','http://localhost:9999','yes'), - (2,'home','http://localhost:9999','yes'), + (1,'siteurl','http://localhost','yes'), + (2,'home','http://localhost','yes'), (3,'blogname','Datadog','yes'), (4,'blogdescription','Just another WordPress site','yes'), (5,'users_can_register','1','yes'), @@ -336,10 +336,10 @@ LOCK TABLES `wp_posts` WRITE; INSERT INTO `wp_posts` (`ID`, `post_author`, `post_date`, `post_date_gmt`, `post_content`, `post_title`, `post_excerpt`, `post_status`, `comment_status`, `ping_status`, `post_password`, `post_name`, `to_ping`, `pinged`, `post_modified`, `post_modified_gmt`, `post_content_filtered`, `post_parent`, `guid`, `menu_order`, `post_type`, `post_mime_type`, `comment_count`) VALUES - (1,1,'2019-09-16 21:10:55','2019-09-16 21:10:55','Welcome to WordPress. This is your first post. Edit or delete it, then start writing!','Hello world!','','publish','open','open','','hello-world','','','2019-09-16 21:10:55','2019-09-16 21:10:55','',0,'http://localhost:9999/?p=1',0,'post','',1), - (2,1,'2019-09-16 21:10:55','2019-09-16 21:10:55','This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:\r\n
Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)
\r\n...or something like this:\r\n
The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.
\r\nAs a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!','Sample Page','','publish','closed','open','','simple_view','','','2019-10-01 17:15:24','2019-10-01 17:15:24','',0,'http://localhost:9999/?page_id=2',0,'page','',0), - (3,1,'2019-09-16 21:11:07','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2019-09-16 21:11:07','0000-00-00 00:00:00','',0,'http://localhost:9999/?p=3',0,'post','',0), - (4,1,'2019-10-01 17:15:24','2019-10-01 17:15:24','This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:\r\n
Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)
\r\n...or something like this:\r\n
The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.
\r\nAs a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!','Sample Page','','inherit','closed','closed','','2-revision-v1','','','2019-10-01 17:15:24','2019-10-01 17:15:24','',2,'http://localhost:9999/2-revision-v1/',0,'revision','',0); + (1,1,'2019-09-16 21:10:55','2019-09-16 21:10:55','Welcome to WordPress. This is your first post. Edit or delete it, then start writing!','Hello world!','','publish','open','open','','hello-world','','','2019-09-16 21:10:55','2019-09-16 21:10:55','',0,'http://localhost/?p=1',0,'post','',1), + (2,1,'2019-09-16 21:10:55','2019-09-16 21:10:55','This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:\r\n
Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)
\r\n...or something like this:\r\n
The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.
\r\nAs a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!','Sample Page','','publish','closed','open','','simple_view','','','2019-10-01 17:15:24','2019-10-01 17:15:24','',0,'http://localhost/?page_id=2',0,'page','',0), + (3,1,'2019-09-16 21:11:07','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2019-09-16 21:11:07','0000-00-00 00:00:00','',0,'http://localhost/?p=3',0,'post','',0), + (4,1,'2019-10-01 17:15:24','2019-10-01 17:15:24','This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:\r\n
Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)
\r\n...or something like this:\r\n
The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.
\r\nAs a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!','Sample Page','','inherit','closed','closed','','2-revision-v1','','','2019-10-01 17:15:24','2019-10-01 17:15:24','',2,'http://localhost/2-revision-v1/',0,'revision','',0); /*!40000 ALTER TABLE `wp_posts` ENABLE KEYS */; UNLOCK TABLES; diff --git a/tests/Frameworks/WordPress/Version_5_5/wp-config.php b/tests/Frameworks/WordPress/Version_5_5/wp-config.php index dd44e66a6d..71300d72b7 100644 --- a/tests/Frameworks/WordPress/Version_5_5/wp-config.php +++ b/tests/Frameworks/WordPress/Version_5_5/wp-config.php @@ -20,7 +20,7 @@ // ** MySQL settings - You can get this info from your web host ** // /** The name of the database for WordPress */ -define( 'DB_NAME', 'test' ); +define( 'DB_NAME', 'wp55' ); /** MySQL database username */ define( 'DB_USER', 'test' ); diff --git a/tests/Frameworks/WordPress/Version_5_5/wp_2020-10-21.sql b/tests/Frameworks/WordPress/Version_5_5/wp_2020-10-21.sql index 19298c95f8..4504d239f1 100644 --- a/tests/Frameworks/WordPress/Version_5_5/wp_2020-10-21.sql +++ b/tests/Frameworks/WordPress/Version_5_5/wp_2020-10-21.sql @@ -172,7 +172,7 @@ CREATE TABLE `wp55_options` ( LOCK TABLES `wp55_options` WRITE; /*!40000 ALTER TABLE `wp55_options` DISABLE KEYS */; -INSERT INTO `wp55_options` VALUES (1,'siteurl','http://localhost:9999','yes'),(2,'home','http://localhost:9999','yes'),(3,'blogname','Datadog Test Application','yes'),(4,'blogdescription','Just another WordPress site','yes'),(5,'users_can_register','1','yes'),(6,'admin_email','test@gmail.com','yes'),(7,'start_of_week','1','yes'),(8,'use_balanceTags','0','yes'),(9,'use_smilies','1','yes'),(10,'require_name_email','1','yes'),(11,'comments_notify','1','yes'),(12,'posts_per_rss','10','yes'),(13,'rss_use_excerpt','0','yes'),(14,'mailserver_url','mail.example.com','yes'),(15,'mailserver_login','login@example.com','yes'),(16,'mailserver_pass','password','yes'),(17,'mailserver_port','110','yes'),(18,'default_category','1','yes'),(19,'default_comment_status','open','yes'),(20,'default_ping_status','open','yes'),(21,'default_pingback_flag','0','yes'),(22,'posts_per_page','10','yes'),(23,'date_format','F j, Y','yes'),(24,'time_format','g:i a','yes'),(25,'links_updated_date_format','F j, Y g:i a','yes'),(26,'comment_moderation','0','yes'),(27,'moderation_notify','1','yes'),(28,'permalink_structure','/%postname%','yes'),(29,'rewrite_rules','a:93:{s:11:\"^wp-json/?$\";s:22:\"index.php?rest_route=/\";s:14:\"^wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:21:\"^index.php/wp-json/?$\";s:22:\"index.php?rest_route=/\";s:24:\"^index.php/wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:17:\"^wp-sitemap\\.xml$\";s:23:\"index.php?sitemap=index\";s:17:\"^wp-sitemap\\.xsl$\";s:36:\"index.php?sitemap-stylesheet=sitemap\";s:23:\"^wp-sitemap-index\\.xsl$\";s:34:\"index.php?sitemap-stylesheet=index\";s:48:\"^wp-sitemap-([a-z]+?)-([a-z\\d_-]+?)-(\\d+?)\\.xml$\";s:75:\"index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]\";s:34:\"^wp-sitemap-([a-z]+?)-(\\d+?)\\.xml$\";s:47:\"index.php?sitemap=$matches[1]&paged=$matches[2]\";s:47:\"category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:42:\"category/(.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:23:\"category/(.+?)/embed/?$\";s:46:\"index.php?category_name=$matches[1]&embed=true\";s:35:\"category/(.+?)/page/?([0-9]{1,})/?$\";s:53:\"index.php?category_name=$matches[1]&paged=$matches[2]\";s:17:\"category/(.+?)/?$\";s:35:\"index.php?category_name=$matches[1]\";s:44:\"tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:39:\"tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:20:\"tag/([^/]+)/embed/?$\";s:36:\"index.php?tag=$matches[1]&embed=true\";s:32:\"tag/([^/]+)/page/?([0-9]{1,})/?$\";s:43:\"index.php?tag=$matches[1]&paged=$matches[2]\";s:14:\"tag/([^/]+)/?$\";s:25:\"index.php?tag=$matches[1]\";s:45:\"type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:40:\"type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:21:\"type/([^/]+)/embed/?$\";s:44:\"index.php?post_format=$matches[1]&embed=true\";s:33:\"type/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?post_format=$matches[1]&paged=$matches[2]\";s:15:\"type/([^/]+)/?$\";s:33:\"index.php?post_format=$matches[1]\";s:12:\"robots\\.txt$\";s:18:\"index.php?robots=1\";s:13:\"favicon\\.ico$\";s:19:\"index.php?favicon=1\";s:48:\".*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\\.php$\";s:18:\"index.php?feed=old\";s:20:\".*wp-app\\.php(/.*)?$\";s:19:\"index.php?error=403\";s:18:\".*wp-register.php$\";s:23:\"index.php?register=true\";s:32:\"feed/(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:27:\"(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:8:\"embed/?$\";s:21:\"index.php?&embed=true\";s:20:\"page/?([0-9]{1,})/?$\";s:28:\"index.php?&paged=$matches[1]\";s:41:\"comments/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:36:\"comments/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:17:\"comments/embed/?$\";s:21:\"index.php?&embed=true\";s:44:\"search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:39:\"search/(.+)/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:20:\"search/(.+)/embed/?$\";s:34:\"index.php?s=$matches[1]&embed=true\";s:32:\"search/(.+)/page/?([0-9]{1,})/?$\";s:41:\"index.php?s=$matches[1]&paged=$matches[2]\";s:14:\"search/(.+)/?$\";s:23:\"index.php?s=$matches[1]\";s:47:\"author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:42:\"author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:23:\"author/([^/]+)/embed/?$\";s:44:\"index.php?author_name=$matches[1]&embed=true\";s:35:\"author/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?author_name=$matches[1]&paged=$matches[2]\";s:17:\"author/([^/]+)/?$\";s:33:\"index.php?author_name=$matches[1]\";s:69:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:64:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:45:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$\";s:74:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true\";s:57:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:81:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]\";s:39:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$\";s:63:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]\";s:56:\"([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:51:\"([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:32:\"([0-9]{4})/([0-9]{1,2})/embed/?$\";s:58:\"index.php?year=$matches[1]&monthnum=$matches[2]&embed=true\";s:44:\"([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:65:\"index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]\";s:26:\"([0-9]{4})/([0-9]{1,2})/?$\";s:47:\"index.php?year=$matches[1]&monthnum=$matches[2]\";s:43:\"([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:38:\"([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:19:\"([0-9]{4})/embed/?$\";s:37:\"index.php?year=$matches[1]&embed=true\";s:31:\"([0-9]{4})/page/?([0-9]{1,})/?$\";s:44:\"index.php?year=$matches[1]&paged=$matches[2]\";s:13:\"([0-9]{4})/?$\";s:26:\"index.php?year=$matches[1]\";s:27:\".?.+?/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\".?.+?/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\".?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\".?.+?/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"(.?.+?)/embed/?$\";s:41:\"index.php?pagename=$matches[1]&embed=true\";s:20:\"(.?.+?)/trackback/?$\";s:35:\"index.php?pagename=$matches[1]&tb=1\";s:40:\"(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:35:\"(.?.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:28:\"(.?.+?)/page/?([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&paged=$matches[2]\";s:35:\"(.?.+?)/comment-page-([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&cpage=$matches[2]\";s:24:\"(.?.+?)(?:/([0-9]+))?/?$\";s:47:\"index.php?pagename=$matches[1]&page=$matches[2]\";s:27:\"[^/]+/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\"[^/]+/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\"[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\"[^/]+/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"([^/]+)/embed/?$\";s:37:\"index.php?name=$matches[1]&embed=true\";s:20:\"([^/]+)/trackback/?$\";s:31:\"index.php?name=$matches[1]&tb=1\";s:40:\"([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:35:\"([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:28:\"([^/]+)/page/?([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&paged=$matches[2]\";s:35:\"([^/]+)/comment-page-([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&cpage=$matches[2]\";s:24:\"([^/]+)(?:/([0-9]+))?/?$\";s:43:\"index.php?name=$matches[1]&page=$matches[2]\";s:16:\"[^/]+/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:26:\"[^/]+/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:46:\"[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:22:\"[^/]+/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";}','yes'),(30,'hack_file','0','yes'),(31,'blog_charset','UTF-8','yes'),(32,'moderation_keys','','no'),(33,'active_plugins','a:1:{i:0;s:19:\"datadog/datadog.php\";}','yes'),(34,'category_base','','yes'),(35,'ping_sites','http://rpc.pingomatic.com/','yes'),(36,'comment_max_links','2','yes'),(37,'gmt_offset','0','yes'),(38,'default_email_category','1','yes'),(39,'recently_edited','','no'),(40,'template','twentytwenty','yes'),(41,'stylesheet','twentytwenty','yes'),(42,'comment_registration','0','yes'),(43,'html_type','text/html','yes'),(44,'use_trackback','0','yes'),(45,'default_role','subscriber','yes'),(46,'db_version','48748','yes'),(47,'uploads_use_yearmonth_folders','1','yes'),(48,'upload_path','','yes'),(49,'blog_public','0','yes'),(50,'default_link_category','2','yes'),(51,'show_on_front','posts','yes'),(52,'tag_base','','yes'),(53,'show_avatars','1','yes'),(54,'avatar_rating','G','yes'),(55,'upload_url_path','','yes'),(56,'thumbnail_size_w','150','yes'),(57,'thumbnail_size_h','150','yes'),(58,'thumbnail_crop','1','yes'),(59,'medium_size_w','300','yes'),(60,'medium_size_h','300','yes'),(61,'avatar_default','mystery','yes'),(62,'large_size_w','1024','yes'),(63,'large_size_h','1024','yes'),(64,'image_default_link_type','none','yes'),(65,'image_default_size','','yes'),(66,'image_default_align','','yes'),(67,'close_comments_for_old_posts','0','yes'),(68,'close_comments_days_old','14','yes'),(69,'thread_comments','1','yes'),(70,'thread_comments_depth','5','yes'),(71,'page_comments','0','yes'),(72,'comments_per_page','50','yes'),(73,'default_comments_page','newest','yes'),(74,'comment_order','asc','yes'),(75,'sticky_posts','a:0:{}','yes'),(76,'widget_categories','a:2:{i:2;a:4:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:12:\"hierarchical\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(77,'widget_text','a:0:{}','yes'),(78,'widget_rss','a:0:{}','yes'),(79,'uninstall_plugins','a:0:{}','no'),(80,'timezone_string','','yes'),(81,'page_for_posts','0','yes'),(82,'page_on_front','0','yes'),(83,'default_post_format','0','yes'),(84,'link_manager_enabled','0','yes'),(85,'finished_splitting_shared_terms','1','yes'),(86,'site_icon','0','yes'),(87,'medium_large_size_w','768','yes'),(88,'medium_large_size_h','0','yes'),(89,'wp_page_for_privacy_policy','3','yes'),(90,'show_comments_cookies_opt_in','1','yes'),(91,'admin_email_lifespan','1618936275','yes'),(92,'disallowed_keys','','no'),(93,'comment_previously_approved','1','yes'),(94,'auto_plugin_theme_update_emails','a:0:{}','no'),(95,'initial_db_version','48748','yes'),(96,'wp55_user_roles','a:5:{s:13:\"administrator\";a:2:{s:4:\"name\";s:13:\"Administrator\";s:12:\"capabilities\";a:61:{s:13:\"switch_themes\";b:1;s:11:\"edit_themes\";b:1;s:16:\"activate_plugins\";b:1;s:12:\"edit_plugins\";b:1;s:10:\"edit_users\";b:1;s:10:\"edit_files\";b:1;s:14:\"manage_options\";b:1;s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:6:\"import\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:8:\"level_10\";b:1;s:7:\"level_9\";b:1;s:7:\"level_8\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;s:12:\"delete_users\";b:1;s:12:\"create_users\";b:1;s:17:\"unfiltered_upload\";b:1;s:14:\"edit_dashboard\";b:1;s:14:\"update_plugins\";b:1;s:14:\"delete_plugins\";b:1;s:15:\"install_plugins\";b:1;s:13:\"update_themes\";b:1;s:14:\"install_themes\";b:1;s:11:\"update_core\";b:1;s:10:\"list_users\";b:1;s:12:\"remove_users\";b:1;s:13:\"promote_users\";b:1;s:18:\"edit_theme_options\";b:1;s:13:\"delete_themes\";b:1;s:6:\"export\";b:1;}}s:6:\"editor\";a:2:{s:4:\"name\";s:6:\"Editor\";s:12:\"capabilities\";a:34:{s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;}}s:6:\"author\";a:2:{s:4:\"name\";s:6:\"Author\";s:12:\"capabilities\";a:10:{s:12:\"upload_files\";b:1;s:10:\"edit_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;s:22:\"delete_published_posts\";b:1;}}s:11:\"contributor\";a:2:{s:4:\"name\";s:11:\"Contributor\";s:12:\"capabilities\";a:5:{s:10:\"edit_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;}}s:10:\"subscriber\";a:2:{s:4:\"name\";s:10:\"Subscriber\";s:12:\"capabilities\";a:2:{s:4:\"read\";b:1;s:7:\"level_0\";b:1;}}}','yes'),(97,'fresh_site','0','yes'),(98,'widget_search','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(99,'widget_recent-posts','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(100,'widget_recent-comments','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(101,'widget_archives','a:2:{i:2;a:3:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(102,'widget_meta','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(103,'sidebars_widgets','a:4:{s:19:\"wp_inactive_widgets\";a:0:{}s:9:\"sidebar-1\";a:3:{i:0;s:8:\"search-2\";i:1;s:14:\"recent-posts-2\";i:2;s:17:\"recent-comments-2\";}s:9:\"sidebar-2\";a:3:{i:0;s:10:\"archives-2\";i:1;s:12:\"categories-2\";i:2;s:6:\"meta-2\";}s:13:\"array_version\";i:3;}','yes'),(104,'cron','a:7:{i:1674663182;a:1:{s:34:\"wp_privacy_delete_old_export_files\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"hourly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:3600;}}}i:1674695582;a:4:{s:18:\"wp_https_detection\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_version_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:17:\"wp_update_plugins\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_update_themes\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674695658;a:1:{s:21:\"wp_update_user_counts\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674738782;a:2:{s:30:\"wp_site_health_scheduled_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"weekly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:604800;}}s:32:\"recovery_mode_clean_expired_keys\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738858;a:2:{s:19:\"wp_scheduled_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:25:\"delete_expired_transients\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738859;a:1:{s:30:\"wp_scheduled_auto_draft_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}s:7:\"version\";i:2;}','yes'),(105,'widget_pages','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(106,'widget_calendar','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(107,'widget_media_audio','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(108,'widget_media_image','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(109,'widget_media_gallery','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(110,'widget_media_video','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(111,'widget_tag_cloud','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(112,'widget_nav_menu','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(113,'widget_custom_html','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(114,'_transient_doing_cron','1603384688.0394918918609619140625','yes'),(115,'_site_transient_update_core','O:8:\"stdClass\":4:{s:7:\"updates\";a:1:{i:0;O:8:\"stdClass\":10:{s:8:\"response\";s:6:\"latest\";s:8:\"download\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:6:\"locale\";s:5:\"en_US\";s:8:\"packages\";O:8:\"stdClass\":5:{s:4:\"full\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:10:\"no_content\";s:70:\"https://downloads.wordpress.org/release/wordpress-5.5.1-no-content.zip\";s:11:\"new_bundled\";s:71:\"https://downloads.wordpress.org/release/wordpress-5.5.1-new-bundled.zip\";s:7:\"partial\";s:0:\"\";s:8:\"rollback\";s:0:\"\";}s:7:\"current\";s:5:\"5.5.1\";s:7:\"version\";s:5:\"5.5.1\";s:11:\"php_version\";s:6:\"5.6.20\";s:13:\"mysql_version\";s:3:\"5.0\";s:11:\"new_bundled\";s:3:\"5.3\";s:15:\"partial_version\";s:0:\"\";}}s:12:\"last_checked\";i:1603384286;s:15:\"version_checked\";s:5:\"5.5.1\";s:12:\"translations\";a:0:{}}','no'),(116,'_site_transient_update_plugins','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384412;s:7:\"checked\";a:3:{s:19:\"akismet/akismet.php\";s:5:\"4.1.6\";s:19:\"datadog/datadog.php\";s:5:\"0.0.0\";s:9:\"hello.php\";s:5:\"1.7.2\";}s:8:\"response\";a:0:{}s:12:\"translations\";a:0:{}s:9:\"no_update\";a:2:{s:19:\"akismet/akismet.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:21:\"w.org/plugins/akismet\";s:4:\"slug\";s:7:\"akismet\";s:6:\"plugin\";s:19:\"akismet/akismet.php\";s:11:\"new_version\";s:5:\"4.1.6\";s:3:\"url\";s:38:\"https://wordpress.org/plugins/akismet/\";s:7:\"package\";s:56:\"https://downloads.wordpress.org/plugin/akismet.4.1.6.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:59:\"https://ps.w.org/akismet/assets/icon-256x256.png?rev=969272\";s:2:\"1x\";s:59:\"https://ps.w.org/akismet/assets/icon-128x128.png?rev=969272\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:61:\"https://ps.w.org/akismet/assets/banner-772x250.jpg?rev=479904\";}s:11:\"banners_rtl\";a:0:{}}s:9:\"hello.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:25:\"w.org/plugins/hello-dolly\";s:4:\"slug\";s:11:\"hello-dolly\";s:6:\"plugin\";s:9:\"hello.php\";s:11:\"new_version\";s:5:\"1.7.2\";s:3:\"url\";s:42:\"https://wordpress.org/plugins/hello-dolly/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/plugin/hello-dolly.1.7.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-256x256.jpg?rev=2052855\";s:2:\"1x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-128x128.jpg?rev=2052855\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:66:\"https://ps.w.org/hello-dolly/assets/banner-772x250.jpg?rev=2052855\";}s:11:\"banners_rtl\";a:0:{}}}}','no'),(117,'_site_transient_timeout_theme_roots','1603386087','no'),(118,'_site_transient_theme_roots','a:3:{s:14:\"twentynineteen\";s:7:\"/themes\";s:15:\"twentyseventeen\";s:7:\"/themes\";s:12:\"twentytwenty\";s:7:\"/themes\";}','no'),(119,'_site_transient_update_themes','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384287;s:7:\"checked\";a:3:{s:14:\"twentynineteen\";s:3:\"1.7\";s:15:\"twentyseventeen\";s:3:\"2.4\";s:12:\"twentytwenty\";s:3:\"1.5\";}s:8:\"response\";a:0:{}s:9:\"no_update\";a:3:{s:14:\"twentynineteen\";a:6:{s:5:\"theme\";s:14:\"twentynineteen\";s:11:\"new_version\";s:3:\"1.7\";s:3:\"url\";s:44:\"https://wordpress.org/themes/twentynineteen/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/theme/twentynineteen.1.7.zip\";s:8:\"requires\";s:5:\"4.9.6\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:15:\"twentyseventeen\";a:6:{s:5:\"theme\";s:15:\"twentyseventeen\";s:11:\"new_version\";s:3:\"2.4\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentyseventeen/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentyseventeen.2.4.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:12:\"twentytwenty\";a:6:{s:5:\"theme\";s:12:\"twentytwenty\";s:11:\"new_version\";s:3:\"1.5\";s:3:\"url\";s:42:\"https://wordpress.org/themes/twentytwenty/\";s:7:\"package\";s:58:\"https://downloads.wordpress.org/theme/twentytwenty.1.5.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}}s:12:\"translations\";a:0:{}}','no'),(120,'_site_transient_timeout_browser_6daa110c3e56e442b403473c9591e946','1603989088','no'),(121,'_site_transient_browser_6daa110c3e56e442b403473c9591e946','a:10:{s:4:\"name\";s:6:\"Chrome\";s:7:\"version\";s:12:\"86.0.4240.80\";s:8:\"platform\";s:9:\"Macintosh\";s:10:\"update_url\";s:29:\"https://www.google.com/chrome\";s:7:\"img_src\";s:43:\"http://s.w.org/images/browsers/chrome.png?1\";s:11:\"img_src_ssl\";s:44:\"https://s.w.org/images/browsers/chrome.png?1\";s:15:\"current_version\";s:2:\"18\";s:7:\"upgrade\";b:0;s:8:\"insecure\";b:0;s:6:\"mobile\";b:0;}','no'),(122,'_site_transient_timeout_php_check_56babb1797dd31750a342dc4c8a11025','1603989088','no'),(123,'_site_transient_php_check_56babb1797dd31750a342dc4c8a11025','a:5:{s:19:\"recommended_version\";s:3:\"7.4\";s:15:\"minimum_version\";s:6:\"5.6.20\";s:12:\"is_supported\";b:1;s:9:\"is_secure\";b:1;s:13:\"is_acceptable\";b:1;}','no'),(124,'_site_transient_timeout_community-events-e0e4f94be3c2d577e126ec3b012627f2','1603427490','no'),(125,'_site_transient_community-events-e0e4f94be3c2d577e126ec3b012627f2','a:4:{s:9:\"sandboxed\";b:0;s:5:\"error\";N;s:8:\"location\";a:1:{s:2:\"ip\";s:12:\"192.168.16.0\";}s:6:\"events\";a:2:{i:0;a:10:{s:4:\"type\";s:6:\"meetup\";s:5:\"title\";s:58:\"Discussion Group: WordPress Troubleshooting Basics: Part 1\";s:3:\"url\";s:68:\"https://www.meetup.com/learn-wordpress-discussions/events/273993927/\";s:6:\"meetup\";s:27:\"Learn WordPress Discussions\";s:10:\"meetup_url\";s:51:\"https://www.meetup.com/learn-wordpress-discussions/\";s:4:\"date\";s:19:\"2020-10-23 06:00:00\";s:8:\"end_date\";s:19:\"2020-10-23 07:00:00\";s:20:\"start_unix_timestamp\";i:1603458000;s:18:\"end_unix_timestamp\";i:1603461600;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"US\";s:8:\"latitude\";d:37.779998779297;s:9:\"longitude\";d:-122.41999816895;}}i:1;a:10:{s:4:\"type\";s:8:\"wordcamp\";s:5:\"title\";s:17:\"WordCamp Bulgaria\";s:3:\"url\";s:35:\"https://bulgaria.wordcamp.org/2020/\";s:6:\"meetup\";N;s:10:\"meetup_url\";N;s:4:\"date\";s:19:\"2020-10-24 10:00:00\";s:8:\"end_date\";s:19:\"2020-10-24 10:00:00\";s:20:\"start_unix_timestamp\";i:1603522800;s:18:\"end_unix_timestamp\";i:1603522800;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"BG\";s:8:\"latitude\";d:42.733883;s:9:\"longitude\";d:25.48583;}}}}','no'),(126,'can_compress_scripts','0','no'),(127,'_transient_timeout_feed_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(128,'_transient_feed_9bbd59226dc36b9b26cd43f15694c5c3','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:49:\"\n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:27:\"News – – WordPress.org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:13:\"lastBuildDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 20:10:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"en-US\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"generator\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"https://wordpress.org/?v=5.6-beta1-49274\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:10:{i:0;a:6:{s:4:\"data\";s:60:\"\n \n\n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WordPress 5.6 Beta 1 is now available for testing!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8236:\"\n

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:38:\"The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:363:\"This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September.  WordPress 5.5.1 Launch On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8713:\"\n

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"WordPress 5.5.1 Maintenance Release\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"https://wordpress.org/news/2020/09/wordpress-5-5-1-maintenance-release/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 19:13:53 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8979\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:460:\"WordPress 5.5.1 is now available! This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade. You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process. […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9020:\"\n

WordPress 5.5.1 is now available!

\n\n\n\n

This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade.

\n\n\n\n

You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process.

\n\n\n\n

WordPress 5.5.1 is a short-cycle maintenance release. The next major release will be version 5.6.

\n\n\n\n

To see a full list of changes, you can browse the list on Trac, read the 5.5.1 RC1 and 5.5.1 RC2 posts, or visit the 5.5.1 documentation page.

\n\n\n\n

Thanks and props!

\n\n\n\n

The 5.5.1 release was led by @audrasjb, @azhiyadev, @davidbaumwald, @desrosj, @johnbillion, @planningwrite, @sergeybiryukov and @whyisjake.

\n\n\n\n

Thank you to everyone who helped make WordPress 5.5.1 happen:

\n\n\n\nAmit Dudhat, Andrea Fercia, Andrey “Rarst” Savchenko, Andy Fragen, Angel Hess, avixansa, bobbingwide, Brian Hogg, chunkysteveo, Clayton Collie, David Baumwald, David Herrera, dd32, demetris, Dominik Schilling, dushakov, Earle Davies, Enrique Sánchez, Frankie Jarrett, fullofcaffeine, Garrett Hyder, Gary Jones, gchtr, Hauwa, Herre Groen, Howdy_McGee, Ipstenu (Mika Epstein), Jb Audras, Jeremy Felt, Jeroen Rotty, Joen A., Johanna de Vos, John Blackbourn, John James Jacoby, Jonathan Bossenger, Jonathan Desrosiers, Jonathan Stegall, Joost de Valk, Jorge Costa, Justin Ahinon, Kalpesh Akabari, Kevin Hagerty, Knut Sparhell, Kyle B. Johnson, landau, Laxman Prajapati, Lester Chan, mailnew2ster, Marius L. J., Mark Jaquith, Mark Uraine, Matt Gibson, Michael Beckwith, Mikey Arce, Mohammad Jangda, Mukesh Panchal, Nabil Moqbel, net, oakesjosh, O André, Omar Reiss, Ov3rfly, Paddy, Pascal Casier, Paul Biron, Peter Wilson, rajeshsingh520, Rami Yushuvaev, rebasaurus, riaanlom, Riad Benguella, Rodrigo Arias, rtagliento, salvoaranzulla, Sanjeev Aryal, sarahricker, Sergey Biryukov, Stephen Bernhardt, Steven Stern (sterndata), Thomas M, Timothy Jacobs, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), TwentyZeroTwo, Winstina, wittich, and Yoav Farhi.\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8979\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"The Month in WordPress: August 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wordpress.org/news/2020/09/the-month-in-wordpress-august-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 09:32:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8983\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:362:\"August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world. WordPress 5.5 Launch […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9605:\"\n

August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Launch

\n\n\n\n

The team launched WordPress 5.5 on August 11. The major release comes with a host of features like automatic updates for plugins and themes, enabling updates over uploaded ZIP files, a block directory, XML sitemaps, block patterns, inline image editing, and lazy-loading images, to name a few. WordPress 5.5 is now available in 50 languages too! You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. Subsequent to the 5.5 release, the 5.5.1 release candidate came out on August 28, which will be followed by its official launch of the minor release on September 1.

\n\n\n\n

A record 805 people contributed to WordPress 5.5, hailing from 58 different countries. @audrasjb has compiled many more stats like that and they’re well worth a read!

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.7 and 8.8

\n\n\n\n

The core team launched Gutenberg 8.7 and 8.8. Version 8.7 saw many improvements to the Post Block suite, along with other changes like adding a block example to the Buttons block, consistently autosaving edits, and updating the group block description. Version 8.8 offers updates to Global Styles, the Post Block suite, and Template management. The release significantly improves the back-compatibility of the new Widget Screen, and also includes other important accessibility and mobile improvements to user interfaces like the Toolbar, navigation menus, and Popovers. For full details on the latest versions of these Gutenberg releases, visit these posts about 8.7 and 8.8.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Check out the brand new Learn WordPress platform!

\n\n\n\n

Learn WordPress is a brand new cross-team initiative led by the WordPress Community team, with support from the training team, the TV team, and the meta team. This platform is a learning repository on learn.wordpress.org, where WordPress learning content will be made available. Video workshops published on the site will be followed up by supplementary discussion groups based on workshop content. The first of these discussion groups have been scheduled, and you can join an upcoming discussion on the dedicated meetup group. The community team invites members to contribute to the project. You can apply to present a workshop, assist with reviewing submitted workshops, and add ideas for workshops that you would like to see on the site. You can also apply to be a discussion group leader to organize discussions directly through the learn.wordpress.org platform. We are also creating a dedicated Learn WordPress working group and have posted a call for volunteers. Meetup organizers can use Learn WordPress content for their meetup events (without applying as a discussion group leader). Simply ask your meetup group to watch one of the workshops in the weeks leading up to your scheduled event, and then host a discussion group for that content as your event.

\n\n\n\n

Want to get involved with the Community team? Follow the Community blog, or join them in the #community-events channel in the Making WordPress Slack group. To organize a local WordPress community event, visit the handbook page

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8983\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n\n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"WordPress 5.5 “Eckstine”\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"https://wordpress.org/news/2020/08/eckstine/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 11 Aug 2020 19:03:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8799\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:354:\"Version 5.5 \"Eckstine\" of WordPress is available for download or update in your WordPress dashboard. With this release, your site gets new power in three major areas: \nspeed (lazy-loading images), search (sitemaps included by default), and security (auto-updates for plugins and themes), along with many new features and improvements to the block editor.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:3:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:48:\"https://s.w.org/images/core/5.5/auto-updates.mp4\";s:6:\"length\";s:6:\"238264\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:50:\"https://s.w.org/images/core/5.5/block-patterns.mp4\";s:6:\"length\";s:7:\"3518792\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:56:\"https://s.w.org/images/core/5.5/inline-image-editing.mp4\";s:6:\"length\";s:7:\"3145140\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Matt Mullenweg\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:71062:\"\n

Here it is! Named “Eckstine” in honor of Billy Eckstine, this latest and greatest version of WordPress is available for download or update in your dashboard.

\n\n\n\n
\"\"
\n\n\n\n
\n

Welcome to WordPress 5.5.

\n\n\n\n

In WordPress 5.5, your site gets new power in three major areas:
speed, search, and security.

\n
\n\n\n\n
\n
\n\n\n\n
\n

Speed

\n\n\n\n

Posts and pages feel faster, thanks to lazy-loaded images.

\n\n\n\n

Images give your story a lot of impact, but they can sometimes make your site seem slow.

\n\n\n\n

In WordPress 5.5, images wait to load until they’re just about to scroll into view. The technical term is ‘lazy loading.’

\n\n\n\n

On mobile, lazy loading can also keep browsers from loading files meant for other devices. That can save your readers money on data — and help preserve battery life.

\n\n\n\n

Search

\n\n\n\n

Say hello to your new sitemap.

\n\n\n\n

WordPress sites work well with search engines.

\n\n\n\n

Now, by default, WordPress 5.5 includes an XML sitemap that helps search engines discover your most important pages from the very minute you go live.

\n\n\n\n

So more people will find your site sooner, giving you more time to engage, retain and convert them to subscribers, customers or whatever fits your definition of success.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Security

\n\n\n\n
Now you can choose to update plugins and themes automatically–or pick just a few–from the screens you’ve always used.
\n\n\n\n

Auto-updates for Plugins and Themes

\n\n\n\n

Now you can set plugins and themes to update automatically — or not! — in the WordPress admin. So you always know your site is running the latest code available.

\n\n\n\n

You can also turn auto-updates on or off for each plugin or theme you have installed — all on the same screens you’ve always used.

\n\n\n\n

Update by uploading ZIP files

\n\n\n\n

If updating plugins and themes manually is your thing, now that’s easier too — just upload a ZIP file.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Highlights from the block editor

\n\n\n\n

Once again, the latest WordPress release packs a long list of exciting new features for the block editor. For example:

\n\n\n\n
\n\n\n\n
\n
\n

Block patterns

\n\n\n\n

New block patterns make it simple and fun to create complex, beautiful layouts, using combinations of text and media that you can mix and match to fit your story.

\n\n\n\n

You will also find block patterns in a wide variety of plugins and themes, with more added all the time. Pick any of them from a single place — just click and go!

\n
\n\n\n\n
\n

The new block directory

\n\n\n\n

Now it’s easier than ever to find the block you need. The new block directory is built right into the block editor, so you can install new block types to your site without ever leaving the editor.

\n\n\n\n

Inline image editing

\n\n\n\n

Crop, rotate, and zoom your photos right from the image block. If you spend a lot of time on images, this could save you hours!

\n
\n
\n\n\n\n
\n\n\n\n

And so much more.

\n\n\n\n

The highlights above are a tiny fraction of the new block editor features you’ve just installed. Open the block editor and enjoy!

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Accessibility

\n\n\n\n

Every release adds improvements to the accessible publishing experience, and that remains true for WordPress 5.5.

\n\n\n\n

Now you can copy links in media screens and modal dialogs with a button, instead of trying to highlight a line of text.

\n\n\n\n

You can also move meta boxes with the keyboard, and edit images in WordPress with your assistive device, as it can read you the instructions in the image editor.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

For developers

\n\n\n\n

5.5 also brings a big box of changes just for developers.

\n\n\n\n
\n
\n

Server-side registered blocks in the REST API

\n\n\n\n

The addition of block types endpoints means that JavaScript apps (like the block editor) can retrieve definitions for any blocks registered on the server.

\n\n\n\n

Defining environments

\n\n\n\n

WordPress now has a standardized way to define a site’s environment type (staging, production, etc). Retrieve that type with wp_get_environment_type() and execute only the appropriate code.

\n\n\n\n

Dashicons

\n\n\n\n

The Dashicons library has received its final update in 5.5. It adds 39 block editor icons along with 26 others.

\n\n\n\n

Passing data to template files

\n\n\n\n

The template loading functions (get_header()get_template_part(), etc.) have a new $args argument. So now you can pass an entire array’s worth of data to those templates.

\n
\n\n\n\n
\n

More changes for developers

\n\n\n\n
  • The PHPMailer library just got a major update, going from version 5.2.27 to 6.1.6.
  • Now get more fine-grained control of redirect_guess_404_permalink().
  • Sites that use PHP’s OPcache will see more reliable cache invalidation, thanks to the new wp_opcache_invalidate() function during updates (including to plugins and themes).
  • Custom post types associated with the category taxonomy can now opt-in to supporting the default term.
  • Default terms can now be specified for custom taxonomies in register_taxonomy().
  • The REST API now officially supports specifying default metadata values through register_meta().
  • You will find updated versions of these bundled libraries: SimplePie, Twemoji, Masonry, imagesLoaded, getID3, Moment.js, and clipboard.js.
\n
\n
\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

The Squad

\n\n\n\n

Leading this release were Matt MullenwegJake Spurlock, and David Baumwald. Supporting them was this highly enthusiastic release squad:

\n\n\n\n\n\n\n\n

Joining the squad throughout the release cycle were 805 generous volunteer contributors who collectively worked on over 523 tickets on Trac and over 1660 pull requests on GitHub.

\n\n\n\n

Put on a Billy Eckstine playlist, click that update button (or download it directly), and check the profiles of the fine folks that helped:

\n\n\nA2 Hosting, a4jp . com, a6software, Aaron D. Campbell, Aaron Jorbin, abderrahman, Abha Thakor, Achal Jain, achbed, Achyuth Ajoy, acosmin, acsnaterse, Adam Silverstein, Addie, addyosmani, adnan.limdi, adrian, airamerica, Ajay Ghaghretiya, Ajit Bohra, akbarhusen, akbarhusen429, Akhilesh Sabharwal, Akira Tachibana, Alain Schlesser, Albert Juhé Lluveras, Alex Concha, Alex Kirk, Alex Lende, Alex Shiels, Ali Shan, ali11007, Allen Snook, amaschas, Amit Dudhat, anbumz, andfinally, Andrea Fercia, Andrea Middleton, Andrea Tarantini, Andrei Draganescu, Andrew Duthie, Andrew Nacin, Andrew Nevins, Andrew Ozz, Andrey \"Rarst\" Savchenko, Andrés Maneiro, Andy Fragen, Andy Meerwaldt, Andy Peatling, Angel Hess, Angela Jin, Angelika Reisiger, Anh Tran, Ankit Gade, Ankit K Gupta, Ankit Panchal, Anne McCarthy, Anthony Burchell, Anthony Hortin, Anton Timmermans, Antonis Lilis, apedog, archon810, argentite, Arpit G Shah, Arslan Ahmed, asalce, ashiagr, ashour, Atharva Dhekne, Aurélien Joahny, aussi, automaton, avixansa, Ayesh Karunaratne, BackuPs, Barry, Barry Ceelen, Bart Czyz, bartekcholewa, bartkalisz, Bastien Ho, Bastien Martinent, bcworkz, bdbch, bdcstr, Ben Dunkle, Bence Szalai, bencroskery, Benjamin Gosset, Benoit Chantre, Bernhard Reiter, BettyJJ, bgermann, bigcloudmedia, bigdawggi, Bill Erickson, Birgir Erlendsson (birgire), Birgit Pauli-Haack, BjornW, bobbingwide, bonger, Boone Gorges, Boris Brdarić, Boy Witthaya, Brandon Kraft, Brandon Payton, Brent Swisher, Brian Hogg, Brian Krogsgard, bruandet, Bunty, Burhan Nasir, caiocrcosta, Cameron Voell, cameronamcintyre, Carike, Carl Wuensche, Carlos Galarza, Carolina Nymark, Caroline Moore, Carrigan, ceyhun, Chad, Chad Butler, Charles Fulton, Chetan Prajapati, Chintan hingrajiya, Chip Snyder, Chloé Bringmann, Chouby, Chris Van Patten, chriscct7, Christian Chung, Christian Jongeneel, Christian Sabo, Christian Wach, Christoph Herr, Christopher Churchill, chunkysteveo, cklee, clayray, Clayton Collie, Clifford Paulick, codeforest, Commeuneimage, Copons, Corey McKrill, cpasqualini, Cristovao Verstraeten, Csaba (LittleBigThings), Curtis Belt, Cyrus Collier, D.PERONNE, d6, Daniel Bachhuber, Daniel Hüsken, Daniel James, Daniel Llewellyn, Daniel Richards, Daniel Roch, Daniele Scasciafratte, Danny, Darko G., Darren Ethier (nerrad), Dave McHale, Dave Whitley, David A. Kennedy, David Aguilera, David Anderson, David Artiss, David Baumwald, David Brumbaugh, David E. Smith, David Herrera, David Ryan, David Shanske, David Smith, david.binda, davidvee, dchymko, Debabrata Karfa, Deepak Lalwani, dekervit, Delowar Hossain, demetris, Denis Yanchevskiy, derekakelly, Derrick Hammer, Derrick Tennant, Diane Co, Dilip Bheda, Dimitris Mitsis, dingo-d, Dion Hulse, Dixita Dusara, djennez, dmenard, dmethvin, doc987, Dominik Schilling, donmhico, Dono12, Doobeedoo, Dossy Shiobara, dpacks, dratwas, Drew Jaynes, DrLightman, DrProtocols, dsifford, dudo, dushakov, Dustin Bolton, dvershinin, Dylan Kuhn, Earle Davies, ecotechie, Eddie Moya, Eddy, Edi Amin, ehtis, Eileen Violini, Ekaterina, Ella van Durpe, elmastudio, Emanuel Blagonic, Emilie LEBRUN, Emmanuel Hesry, Enej Bajgoric, Enrico Sorcinelli, Enrique Piqueras, Enrique Sánchez, Eric, Eric Andrew Lewis, Eric Binnion, Erik Betshammar, Erin \'Folletto\' Casali, esemlabel, esoj, espiat, Estela Rueda, etoledom, etruel, Ev3rywh3re, Evan Mullins, Fabian Kägy, Fabian Todt, Faisal Ahmed, Felix Arntz, Felix Edelmann, ferdiesletering, finomeno, Florian Brinkmann, Florian TIAR, Florian Truchot, florianatwhodunit, FolioVision, Francesca Marano, Francois Thibaud, Frank Goossens, Frank Klein, Frank.Prendergast, Frankie Jarrett, Franz Armas, fullofcaffeine, Gabriel Koen, Gabriel Maldonado, Gabriel Mays, gadgetroid, Gal Baras, Garavani, garethgillman, Garrett Hyder, Gary Cao, Gary Jones, Gary Pendergast, gchtr, Geert De Deckere, Gemini Labs, Gennady Kovshenin, geriux, Giorgio25b, gisselfeldt, glendaviesnz, goldsounds, Goto Hayato, Govind Kumar, Grégory Viguier, gradina, Greg Ziółkowski, gregmulhauser, grierson, Grzegorz.Janoszka, gsmumbo, Guido Scialfa, guidobras, Gunther Pilz, gwwar, H-var, hakre, Halacious, hankthetank, Hapiuc Robert, Hareesh, haukep, Hauwa, Haz, Hector Farahani, Helen Hou-Sandi, Henry Wright, Herre Groen, hlanggo, hommealone, Hoover, Howdy_McGee, Hronak Nahar, huntlyc, Ian Belanger, Ian Dunn, Ian Stewart, ianjvr, ifrins, infinum, Ipstenu (Mika Epstein), Isabel Brison, ishitaka, J.D. Grimes, jackfungi, jacklinkers, Jadon N, jadpm, jagirbahesh, Jake Spurlock, Jake Whiteley, James Koster, James Nylen, Jan Koch, Jan Reilink, Jan Thiel, Janvo Aldred, Jarret, Jason Adams, Jason Coleman, Jason Cosper, Jason Crouse, Jason LeMahieu (MadtownLems), Jason Rouet, JasWSInc, Javier Casares, Jayson Basanes, jbinda, jbouganim, Jean-Baptiste Audras, Jean-David Daviet, Jeff Chandler, Jeff Farthing, Jeff Ong, Jeff Paul, Jen, Jenil Kanani, Jeremy Felt, Jeremy Herve, Jeremy Yip, Jeroen Rotty, jeryj, Jesin A, Jignesh Nakrani, Jim_Panse, Jip Moors, jivanpal, Joe Dolson, Joe Hoyle, Joe McGill, Joen Asmussen, Johanna de Vos, John Blackbourn, John Dorner, John James Jacoby, John P. Green, John Richards II, John Watkins, johnnyb, Jon Quach, Jon Surrell, Jonathan Bossenger, Jonathan Champ, Jonathan Christopher, Jonathan Desrosiers, Jonathan Stegall, jonkolbert, Jonny Harris, jonnybot, Jono Alderson, Joost de Valk, Jorge Bernal, Jorge Costa, Joseph Dickson, Josepha Haden, Josh Smith, JoshuaWold, Joy, Juanfra Aldasoro, juanlopez4691, Jules Colle, julianm, Juliette Reinders Folmer, Julio Potier, Julka Grodel, Justin Ahinon, Justin de Vesine, Justin Tadlock, justlevine, justnorris, K. Adam White, kaggdesign, Kailey (trepmal), Kaira, Kaitlin Bolling, Kalpesh Akabari, KamataRyo, Kantari Samy, Kaspars, Kavya Gokul, keesiemeijer, Kelly Dwan, kennethroberson5556, Kevin Hagerty, Kharis Sulistiyono, Khokan Sardar, kinjaldalwadi, Kiril Zhelyazkov, Kirsty Burgoine, Kishan Jasani, kitchin, Kite, Kjell Reigstad, Knut Sparhell, Konstantin Obenland, Konstantinos Xenos, ksoares, KT Cheung, Kukhyeon Heo, Kyle B. Johnson, lalitpendhare, landau, Laterna Studio, laurelfulford, Laurens Offereins, Laxman Prajapati, Lester Chan, Levdbas, Lew Ayotte, Lex Robinson, linyows, lipathor, Lisa Schuyler, liuhaibin, ljharb, logig, lucasbustamante, luiswill, Luke Cavanagh, Luke Walczak, lukestramasonder, M Asif Rahman, M.K. Safi, Maarten de Boer, Mahfoudh Arous, mailnew2ster, manojlovic, Manuel Schmalstieg, maraki, Marcin Pietrzak, Marcio Zebedeu, Marco Pereirinha, MarcoZ, Marcus, Marcus Kazmierczak, Marek Dědič, Marek Hrabe, Mario Valney, Marius Jensen, Mark Chouinard, Mark Jaquith, Mark Parnell, Mark Uraine, markdubois, markgoho, Marko Andrijasevic, Marko Heijnen, MarkRH, markshep, markusthiel, Martijn van der Kooij, martychc23, Mary Baum, Matheus Martins, Mathieu Viet, Matias Ventura, matjack1, Matt Cromwell, Matt Gibson, Matt Mullenweg, Matt Radford, Matt van Andel, mattchowning, Matthew Boynes, Matthew Eppelsheimer, Matthew Gerring, Matthias Kittsteiner, Matthias Pfefferle, Matthieu Mota, mattyrob, Maxime Culea, Maxime Pertici, maxme, Mayank Majeji, mcshane, Mel Choyce-Dwan, Menaka S., mensmaximus, metalandcoffee, Michael, Michael Arestad, Michael Arestad, Michael Beckwith, Michael Fields, Michael Nelson, Michele Butcher-Jones, Michelle, Miguel Fonseca, mihdan, Miina Sikk, Mikael Korpela, mikaumoto, Mike Crantea, Mike Glendinning, Mike Haydon, Mike Schinkel [WPLib Box project lead], Mike Schroder, Mikey Arce, Milana Cap, Milind More, mimi, mislavjuric, Mohammad Jangda, Mohammad Rockeybul Alam, Mohsin Rasool, Monika Rao, Morgan Kay, Morten Rand-Hendriksen, Morteza Geransayeh, moto hachi ( mt8.biz ), mrgrt, mrmist, mrTall, msaggiorato, Muhammad Usama Masood, Mukesh Panchal, munyagu, Nabil Moqbel, Nadir Seghir, Nahid Ferdous Mohit, Nalini Thakor, Naoko Takano, narwen, Nate Gay, Nathan Rice, Navid, neonkowy, net, netpassprodsr, Nextendweb, Ngan Tengyuen, Nick Daugherty, Nicky Lim, nicolad, Nicolas Juen, NicolasKulka, Nidhi Jain, Niels de Blaauw, Niels Lange, nigro.simone, Nik Tsekouras, Nikhil Bhansi, Nikolay Bachiyski, Nilo Velez, Niresh, nmenescardi, Noah Allen, NumidWasNotAvailable, oakesjosh, obliviousharmony, ockham, Olga Gleckler, Omar Alshaker, Omar Reiss, onokazu, Optimizing Matters, Ov3rfly, ovann86, overclokk, p_enrique, Paal Joachim Romdahl, Pablo Honey, Paddy, palmiak, Paresh Shinde, Parvand, Pascal Birchler, Pascal Casier, Paul Bearne, Paul Biron, Paul Fernhout, Paul Gibbs, Paul Ryan, Paul Schreiber, Paul Stonier, Paul Von Schrottky, pavelevap, Pedro Mendonça, pentatonicfunk, pepe, Peter \"Pessoft\" Kolínek, Peter Westwood, Peter Wilson, Phil Derksen, Phil Johnston, Philip Jackson, Pierre Gordon, pigdog234, pikamander2, pingram, Pionect, Piyush Patel, pkarjala, pkvillanueva, Prashant Baldha, pratik028, Pravin Parmar, Presskopp, Presslabs, Priyank Patel, Priyo Mukul, ProGrafika, programmin, Puneet Sahalot, pvogel2, r-a-y, Raaj Trambadia, Rachel Peter, raine, rajeshsingh520, Ramanan, Rami Yushuvaev, RavanH, Ravat Parmar, ravenswd, rawrly, rebasaurus, Red Sand Media Group, Remy Perona, Remzi Cavdar, Renatho, renggo888, retlehs, retrofox, riaanlom, Riad Benguella, Rian Rietveld, riasat, Rich Tabor, Ringisha, ritterml, Rnaby, Rob Cutmore, Rob Migchels, rob006, Robert Anderson, Robert Chapin, Robert Peake, Robert Windisch, Rodrigo Arias, Ronald Huereca, Rostislav Wolný, Roy Tanck, rtagliento, ruxandra, Ryan Boren, Ryan Fredlund, Ryan Kienstra, Ryan McCue, Ryan Welcher, Ryota Sakamoto, ryotsun, Sören Wrede, Søren Brønsted, Sachit Tandukar, Sagar Jadhav, Sajjad Hossain Sagor, Sal Ferrarello, Salvatore Formisano, salvoaranzulla, Sam Fullalove, Sam Webster, Samir Shah, Samuel Wood (Otto), samueljseay, Sander van Dragt, Sanjeev Aryal, Sanket Mehta, sarahricker, Sathiyamoorthy V, Sayed Taqui, scarolan, scholdstrom, Scott Kingsley Clark, Scott Reilly, Scott Smith, Scott Taylor, scribu, scruffian, Sean Hayes, seanpaulrasmussen, seayou, senatorman, Sergey Biryukov, Sergey Predvoditelev, Sergio de Falco, sergiomdgomes, Shannon Smith, Shantanu Desai, shaunandrews, Shawn Hooper, shawnz, Shital Marakana, shulard, siliconforks, Simon Wheatley, simonjanin, sinatrateam, sjmur, skarabeq, skorasaurus, skoskie, slushman, snapfractalpop, SpearsMarketing, sphakka, squarecandy, sreedoap, Stanimir Stoyanov, Stefano Minoia, Stefanos Togoulidis, Steph Wells, Stephen Bernhardt, Stephen Cronin, Stephen Edgar, Steve Dufresne, stevegibson12, Steven Stern (sterndata), Steven Word, stevenkussmaul, stevenlinx, Stiofan, Subrata Sarkar, SUM1, Sunny, Sunny Ratilal, Sushyant Zavarzadeh, suzylah, Sybre Waaijer, Synchro, Sérgio Estêvão, Takayuki Miyauchi, Tammie Lister, Tang Rufus, TeBenachi, Tessa Watkins LLC, Tetsuaki Hamano, theMikeD, theolg, Thierry Muller, Thimal Wickremage, Thomas M, Thorsten Frommen, Thrijith Thankachan, Tiago Hillebrandt, Till Krüss, Timothy Jacobs, Tkama, tmdesigned, tmoore41, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), Tofandel, tomdude, Tommy Ferry, Tony G, Toro_Unit (Hiroshi Urabe), torres126, Torsten Landsiedel, Toru Miki, Travis Northcutt, treecutter, truongwp, tsimmons, Tung Du, Udit Desai, Ulrich, Vagios Vlachos, valchovski, Valentin Bora, Vayu Robins, veromary, Viktor Szépe, vinkla, virginienacci, Vladimir, Vladislav Abrashev, vortfu, voyager131, vtieu, webaware, Weston Ruter, William Earnhardt, williampatton, Winstina, wittich, wpdesk, WPDO, WPMarmite, wppinar, Yahil Madakiya, yashrs, yoancutillas, Yoav Farhi, yohannp, yuhin, Yui, Yuri Salame, Yvette Sonneveld, Zack Tollman, zaheerahmad, zakkath, Zebulan Stanphill, zieladam, and Česlav Przywara.\n\n\n\n

 

\n\n\n\n

Many thanks to all of the community volunteers who contribute in the support forums. They answer questions from people across the world, whether they are using WordPress for the first time or since the first release. These releases are more successful for their efforts!

\n\n\n\n

Finally, thanks to all the community translators who worked on WordPress 5.5. Their efforts bring WordPress fully translated to 46 languages at release time, with more on the way.

\n\n\n\n

If you want to learn more about volunteering with WordPress, check out Make WordPress or the core development blog.

\n
\n\n\n\n
\n
\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8799\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"WordPress 5.5 Release Candidate 2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"https://wordpress.org/news/2020/08/wordpress-5-5-release-candidate-2/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 04 Aug 2020 19:12:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8764\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:420:\"The second release candidate for WordPress 5.5 is here! WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time! You can test the WordPress 5.5 release candidate in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding edge nightlies” option) Or download the release […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2503:\"\n

The second release candidate for WordPress 5.5 is here!

\n\n\n\n

WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

For a more detailed breakdown of the changes included in WordPress 5.5, check out the WordPress 5.5 beta 1 post. The WordPress 5.5 Field Guide is also out! It’s your source for details on all the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8764\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n\n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"The Month in WordPress: July 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"https://wordpress.org/news/2020/08/the-month-in-wordpress-july-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 03 Aug 2020 13:54:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8755\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:340:\"July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:11539:\"\n

July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Updates

\n\n\n\n

July was full of WordPress 5.5 updates! The WordPress 5.5 Beta 1 came out on July 7, followed by Beta 2 on July 14, Beta 3 on July 21, and Beta 4 on July 27. Subsequently, the team also published the first release candidate of WordPress 5.5 on July 28. 

\n\n\n\n

WordPress 5.5, which is slated for release on August 11, 2020, is a major update with features like automatic updates for plugins and themes, a block directory, XML sitemaps, block patterns, and lazy-loading images, among others. To learn more about the release, check out its field guide post.

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.5 and 8.6

\n\n\n\n

The core team launched Gutenberg 8.5 and 8.6. Version 8.5 – the last plugin release will be included entirely (without experimental features) in WordPress 5.5, introduced improvements to block drag-and-drop and accessibility, easier updates for external images, and support for the block directory. Version 8.6 comes with features like Cover block video position controls and block pattern updates. For full details on the latest versions on these Gutenberg releases, visit these posts about 8.5 and 8.6.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Reimagining Online WordPress Events

\n\n\n\n

The Community team made the difficult decision to suspend in-person WordPress events for the rest of 2020 in light of the COVID-19 pandemic. The team has also started working on reimagining online events. Based on feedback from the community members, the team decided to make changes to the current online WordCamp format. Key changes include wrapping up financial support for A/V vendors, ending event swag support for newer online WordCamps, and suspending the Global Community Sponsorship program for 2020. The team encourages upcoming online WordCamps to experiment with their events to facilitate an effective learning experience for attendees while avoiding online event fatigue. The team is currently working on a proposal to organize community-supported recorded workshops and synchronous discussion groups to help community members learn WordPress.

Want to get involved with the Community team? Follow the Community blog here, or join them in the #community-events channel in the Making WordPress Slack group. To organize a Meetup or WordCamp, visit the handbook page

\n\n\n\n

WordCamp US 2020 is canceled

\n\n\n\n

The organizers of WordCamp US 2020 have canceled the event in light of the continued pandemic and online event fatigue. The flagship event, which was originally scheduled for October 27-29 as an in-person event, had already planned to transition to an online event. Several WCUS Organizers will be working with the WordPress Community team to focus on other formats and ideas for online events, including a 24-hour contributor day, and contributing to the workshops initiative currently being discussed. Matt Mullenweg’s State of the Word (which typically accompanies WordCamp US) is likely to take place in a different format later in 2020.

\n\n\n\n

Plugin and theme updates are now available over zip files

\n\n\n\n

After eleven years, WordPress now allows users to update plugins and themes by uploading a ZIP file, in WordPress 5.5.  The feature, which was merged on July 7, has been one of the most requested features in WordPress. Now, when a user tries to upload a plugin or theme zip file from the WordPress dashboard by clicking the “Install Now” button, WordPress will direct users to a new screen that compares the currently-installed extension with the uploaded versions. Users can then choose between continuing with the installation or canceling. WordPress 5.5 will also offer automatic plugin and theme updates

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n
  • The Block directory is coming to WordPress with the 5.5 release. Plugin authors can now submit their Block plugins to the directory.
  • The Core team has opened up the call for features in the WordPress 5.6 release. You can comment on the post with features that you’d like to be included, current UX pain points, or maintenance tickets that need to be addressed. August 20 is the deadline for feature requests. 
  • Editor features such as the new Navigation block, the navigation screen, and the widget screen that were originally planned to be merged with WordPress 5.5 have been pushed for the next release
  • The Theme team is inviting proposals on whether to allow themes to place an additional top-level menu link in the admin.
  • BuddyPress 6.2 beta is out in the wild, and the team will soon release the stable version. The update includes changes that will make BuddyPress fully compatible with WordPress 5.5.
  • WordCamp EU 2021, which was being planned as an in-person event in Porto, Portugal, is moving online. The team is considering an in-person WordCamp EU in 2022. 
  • The Polyglots team has prepared and finalized a Translation Editor & Locale Manager Vetting Criteria to provide more clarity on how global mentors assign PTE/GTE/Locale Managers and to help locale teams set their own guidelines. The document, which was finalized after a lot of discussion, is now available in the Polyglots handbook.
  • Members of the Community team are discussing whether WordCamp volunteers, WordCamp attendees, or Meetup attendees should be awarded a WordPress.org profile badge. The ongoing discussion will be open for comments until August 13.
  • The WP Notify project, which aims to create a better way to manage and deliver notifications to the relevant audience, is on to its next steps. The team has finalized the initial requirements, and is kicking off the project build.
  • The WordPress documentation team is considering a ban on links to commercial websites in a revision to its external linking policy. The policy change does not remove external links to commercial sites from WordPress.org and only applies to documentation sites. The idea is to protect documentation from being abused, and to prevent the WordPress project from being biased. Discussion on this post is still ongoing, and a decision has not yet been made. Feel free to comment on the discussion posts, if you would like to share your thoughts on the topic.
\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8755\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"WordPress 5.5 Release Candidate\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://wordpress.org/news/2020/07/wordpress-5-5-release-candidate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 28 Jul 2020 19:08:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8732\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:370:\"The first release candidate for WordPress 5.5 is now available! This is an important milestone in the community’s progress toward the final release of WordPress 5.5. “Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2970:\"\n

The first release candidate for WordPress 5.5 is now available!

\n\n\n\n

This is an important milestone in the community’s progress toward the final release of WordPress 5.5.

\n\n\n\n

“Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

What’s in WordPress 5.5?

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developer notes tag for updates on those and other changes that could affect your products.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

The WordPress 5.5 Field Guide, due very shortly, will give you a more detailed dive into the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8732\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 4\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-4/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 27 Jul 2020 20:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8719\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:313:\"WordPress 5.5 Beta 4 is now available! This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 4 in two ways: Try the WordPress Beta Tester plugin (choose the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"David Baumwald\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3812:\"\n

WordPress 5.5 Beta 4 is now available!

\n\n\n\n

This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 4 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 3 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 3, 43 bugs have been fixed. Here are a few changes in beta 4:

\n\n\n\n
  • Add \"loading\" as an allowed kses image attribute (see #50731).
  • Add filter for the plugin/theme auto-update message in the Info tab of Site health (see #50663).
  • $_SERVER[\'SERVER_NAME\'] not a reliable when generating email host names (see #25239)
  • Several backported fixes from Gutenberg are included in WordPress 5.5 Beta 4 (See PR #24218)
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8719\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 3\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-3/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 21 Jul 2020 17:51:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8706\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:324:\"WordPress 5.5 Beta 3 is now available! This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 3 in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3876:\"\n

WordPress 5.5 Beta 3 is now available!

\n\n\n\n

This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 3 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 2 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 2, 43 bugs have been fixed. Here are a few changes in beta 3:

\n\n\n\n
  • Plugin and theme versions are now shared in the emails when automatically updated (see #50350).
  • REST API routes without a permission_callback now trigger a _doing_it_wrong() warning (see #50075).
  • Over 23 Gutenberg changes and updates (see #24068 and #50712).
  • A bug with the new import and export database Dashicons has been fixed (see #49913).
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8706\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}s:27:\"http://www.w3.org/2005/Atom\";a:1:{s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:4:\"href\";s:32:\"https://wordpress.org/news/feed/\";s:3:\"rel\";s:4:\"self\";s:4:\"type\";s:19:\"application/rss+xml\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:44:\"http://purl.org/rss/1.0/modules/syndication/\";a:2:{s:12:\"updatePeriod\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"\n hourly \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:15:\"updateFrequency\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"\n 1 \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:4:\"site\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"14607090\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:9:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:30 GMT\";s:12:\"content-type\";s:34:\"application/rss+xml; charset=UTF-8\";s:25:\"strict-transport-security\";s:11:\"max-age=360\";s:6:\"x-olaf\";s:3:\"⛄\";s:13:\"last-modified\";s:29:\"Wed, 21 Oct 2020 20:10:31 GMT\";s:4:\"link\";s:63:\"; rel=\"https://api.w.org/\"\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(129,'_transient_timeout_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(130,'_transient_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603384291','no'),(131,'_transient_timeout_feed_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(132,'_transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:16:\"WordPress Planet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"en\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WordPress Planet - http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:50:{i:0;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Loginizer Plugin Gets Forced Security Update for Vulnerabilities Affecting 1 Million Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106557\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users?utm_source=rss&utm_medium=rss&utm_campaign=loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5484:\"

WordPress.org has pushed out a forced security update for the Loginizer plugin, which is active on more than 1 million websites. The plugin offers brute force protection in its free version, along with other security features like two-factor auth, reCAPTCHA, and PasswordLess login in its commercial upgrade.

\n\n\n\n

Last week security researcher Slavco Mihajloski discovered an unauthenticated SQL injection vulnerability, and an XSS vulnerability, that he disclosed to the plugin’s authors. Loginizer version 1.6.4 was released on October 16, 2020, with patches for the two issues, summarized on the plugin’s blog:

\n\n\n\n

1) [Security Fix] : A properly crafted username used to login could lead to SQL injection. This has been fixed by using the prepare function in PHP which prepares the SQL query for safe execution.

2) [Security Fix] : If the IP HTTP header was modified to have a null byte it could lead to stored XSS. This has been fixed by properly sanitizing the IP HTTP header before using the same.

\n\n\n\n

Loginizer did not disclose the vulnerability until today in order to give users the time to upgrade. Given the severity of the vulnerability, the plugins team at WordPress.org pushed out the security update to all sites running Loginizer on WordPress 3.7+.

\n\n\n\n

In July, 2020, Loginizer was acquired by Softaculous, so the company was also able to automatically upgrade any WordPress installations with Loginizer that had been created using Softaculous. This effort, combined with the updates from WordPress.org, covered a large portion of Loginizer’s user base.

\n\n\n\n
\n

Any #WordPress install with @loginizer probably isn\'t using another WAF solution. As you can notice from the graph 600k+500k active installs were updated upside down, so … Preauth SQLi in it, reported by me. Update! Crunching write up :) https://t.co/gkEVWun9wt pic.twitter.com/XWXVMYO1ED

— mslavco (@mslavco) October 19, 2020
\n
\n\n\n\n

The automatic update took some of the plugin’s users by surprise, since they had not initiated it themselves and had not activated automatic updates for plugins. After several users posted on the plugin’s support forum, plugin team member Samuel Wood said that “WordPress.org has the ability to turn on auto-updates for security issues in plugins” and has used this capability many times.

\n\n\n\n

Mihajloski published a more detailed proof-of-concept on his blog earlier today. He also highlighted some concerns regarding the systems WordPress has in place that allowed this kind of of severe vulnerability to slip through the cracks. He claims the issue could have easily been prevented by the plugin review team since the plugin wasn’t using the prepare function for safe execution of SQL queries. Mihajloski also recommended recurring code audits for plugins in the official directory.

\n\n\n\n

“When a plugin gets into the repository, it must be reviewed, but when is it reviewed again?” Mihajloski said. “Everyone starts with 0 active installs, but what happens on 1k, 10k, 50k, 100k, 500k, 1mil+ active installs?”

\n\n\n\n

Mihajloski was at puzzled how such a glaring security issue could remain in the plugin’s code so long, given that it is a security plugin with an active install count that is more than many well known CMS’s. The plugin also recently changed hands when it was acquired by Softaculus and had been audited by multiple security organizations, including WPSec and Dewhurst Security.

\n\n\n\n

Mihajloski also recommends that WordPress improve the transparency around security, as some site owners and closed communities may not be comfortable with having their assets administered by unknown people at WordPress.org.

\n\n\n\n

“WordPress.org in general is transparent, but there isn’t any statement or document about who, how and when decides about and performs automatic updates,” Mihajloski said. “It is kind of [like] holding all your eggs in one basket.

\n\n\n\n

“I think those are the crucial points that WP.org should focus on and everything will came into place in a short time: complete WordPress tech documentation for security warnings, a guide for disclosure of the bugs (from researchers, but also from a vendor aspect), and recurring code audits for popular plugins.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 22 Oct 2020 03:47:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"Post Status: Joe Casabona on creating quality content and courses\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=80099\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"https://poststatus.com/joe-casabona-on-creating-quality-content-and-courses/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1407:\"

David Bisset interviews Joe Casabona, an independent creator and teacher, and discusses what it\'s like to be a creator as his job, plus some news topics.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Sandhills Development

\n\n\n\n

Sandhills Development crafts ingenuity, developed with care:

\n\n\n\n
  • Easy Digital Downloads – Sell digital products with WordPress
  • AffiliateWP – A full-featured affiliate marketing solution
  • Sugar Calendar – WordPress event management made simple
  • WP Simple Pay – A lightweight Stripe payments plugin
\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:17:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: MakeStories 2.0 Launches Editor for WordPress, Rivaling Google’s Official Web Stories Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106327\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin?utm_source=rss&utm_medium=rss&utm_campaign=makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8860:\"Recipe slide from the MakeStories WordPress plugin.\n\n\n\n

Earlier today, MakeStories launched version 2.0 of its plugin for creating Web Stories with WordPress. In many ways, this is a new plugin launch. The previous version simply allowed users to connect their WordPress installs to the MakeStories site. With the new version, users can build and edit their stories directly from the WordPress admin.

\n\n\n\n

Version 2.0 of the plugin still requires an account and a connection with the MakeStories.io website. However, it is simple to set up. Users can log in without leaving their WordPress admin interface. This API connection means that user-created Stories are stored on the MakeStories servers. If an end-user wanted to jump platforms from WordPress to something else, this would allow them to take their Stories with them.

\n\n\n\n

“One of the things we would like to assure is your content is still yours, and none of the user data is being consumed or analyzed by us,” said Pratik Ghela, the founder and product manager at MakeStories. “We only take enough data to help serve you better.”

\n\n\n\n

The plugin is a competing solution to the official Web Stories plugin by Google. While the two share similarities in the final output (they are built to utilize the same front-end format for creating Stories on the web), they take different paths to get there.

\n\n\n\n

The two share similarities on the backend too. However, MakeStories may be more polished in some areas. For example, it allows users to zoom in on the small canvas area. Having the ability to reorder slides from the grid view also feels more intuitive.

\n\n\n\n

“The main unique selling proposition of our plugin is that it comes with a guarantee of the MakeStories team,” said Ghela. “We as a team have been building this for over two years, and we are proud to be one of the tools that has stood the test of time, and competition and is still growing at a very fast pace.”

\n\n\n\n

The team also wants to make the Story-creating process faster, safer, and rewarding. The goal is to cater to designers, developers, and content creators. Ghela also feels like his team’s support turnaround time of a few hours will be the key to success and is a good reason for users to give this plugin a try before settling on something else.

\n\n\n\n

“We feel that our goal is to see Web Stories flourish,” he said. “And we may have different types of users looking out for various options. So, the official plugin from Google and the one from MakeStories at least opens up the options for users to choose from. And we feel that the folks at Google are also building a great editor, and, at the end of the day, it’s up to the user to select what they feel is the best.

\n\n\n\n

Technically, MakeStories is a SaaS (software as a service) product. Even though it is a free plugin, there will eventually be a commercial component to it. Currently, it is free at least until the first quarter of 2021, which may be extended based on various factors. There is no word on what pricing tiers may be available after that.

\n\n\n\n

“There will always be a free tier, and we have always stood for it that your data belongs to you,” said Ghela. “In case you do not like the pricing, we will personally assist you to port out from using our editor and still keep the data and everything totally intact.”

\n\n\n\n

Diving Into the Plugin

\n\n\n\nStory management screen.\n\n\n\n

MakeStories is a drag-and-drop editor for building Web Stories. It works and feels much like typical design editors like Gimp or Photoshop. It shares similarities with QuarkXPress or InDesign, for those familiar with page layout programs. In some ways, it feels a lot like a light-colored version of Google’s Web Stories plugin with more features and a slightly more intuitive interface.

\n\n\n\n

The end goal is simple: create a Story through designing slides/pages that site visitors will click through as the narrative unfolds.

\n\n\n\n

The plugin provides a plethora of shapes, textures, and animations. These features are easy to find and implement. It also includes free access to images, GIFs, and videos. These are made possible via API integrations with Unsplash, Tenor, and Pexels.

\n\n\n\n

MakeStories includes access to 10 templates at the moment. However, what makes this feature stand out is that end-users can create and save custom templates for reuse down the road.

\n\n\n\nEditing a Story from a predesigned template.\n\n\n\n

One of the more interesting, almost hidden, features is the available text patterns. The plugin allows users to insert these patterns from a couple of dozen choices. This makes it easier to visualize a design without having to build everything from scratch.

\n\n\n\nInserting a text pattern and adjusting its size.\n\n\n\n

While the editing process is a carefully-crafted experience that makes the plugin worth a look, it is the actual publishing aspect of the workflow that is a bit painful. Traditional publishing in WordPress means hitting the “publish” button to make content live. This is not the case with the MakeStories plugin. It takes you through a four-step process of entering various publisher details, setting up metadata and SEO, validating the Story content, and analytics. It is not that these steps are necessarily bad. For example, MakeStories lets you know when images are missing alt text, which is needed information screen readers. The problem is that it feels out of place to go through all of these details when I, as a user, simply want my content published. And, many of these details, such as the publisher (author), should be automatically filled in.

\n\n\n\n

Updating a Story is not as simple as hitting an “update” button either. The system needs to run through some of the same steps too.

\n\n\n\n

Ghela said the publishing process might be a bit tough but will prove fruitful in the end. The plugin takes care of the technical aspects of adding title tags, meta, and other data on the front end after the user fills in the form fields.

\n\n\n\n

“We will definitely work on improving the flow as the community evolves and improve it a lot to be easier, faster, and, most importantly, still very customizable,” he said.

\n\n\n\n

The MakeStories team has no plans of stopping at its current point on the roadmap. Ghela sounded excited about some of the upcoming additions they are planning, including features like teams, branding, easy template customization, polls, and quizzes.

\n\n\n\n

On the Web Stories Format

\n\n\n\nUN report on COVID-19 and poverty published with MakeStories.\n\n\n\n

Many will ultimately hesitate to use any plugin that implements Web Stories given Google’s history of dropping projects. There is also a feeling that the format is a bit of a fad and will not stand the test of time.

\n\n\n\n

“We greatly believe in AMP and Web Stories as a content format,” said Ghela. “We, as an agency, have been involved a lot in AMP and have done a lot of experiments with it, including a totally custom WooCommerce site in fully-native, valid AMP with support for variable products, subscriptions, and other functionalities.”

\n\n\n\n

The company is all-in on the format and feels like it will be around for the long term, particularly if there is a good ecosystem around monetization.

\n\n\n\n

“We think that the initial reactions are because there are not enough proven results and because we never imagined the story format to come to the web,” said Ghela. “There were definitely plugins that did this. Few folks tried to build stories using good ol’ HTML, CSS, and JavaScript. But, the performance and UX were not that great. On the other hand, the engineers at the AMP Team are making sure that everything is just perfect. The UX, load time, WCV Score, just everything.”

\n\n\n\n

He feels that some of the early criticisms are unwarranted and that the web development community should give the format a try and provide feedback.

\n\n\n\n

“The more data we all get, actually gives the AMP team a clear idea of what’s needed, and they can design the roadmap accordingly,” he said. “So, just giving out early reactions won’t help, but constructive criticism and getting back to the AMP team with what you are doing will.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:12:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"WordPress.org blog: WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7956:\"

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"WPTavern: WordPress 5.6 Release Team Pulls the Plug on Block-Based Widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106466\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:193:\"https://wptavern.com/wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8762:\"Current block-based widgets admin screen design.\n\n\n\n

I was wrong. I assured our readers that “the block-based widget system will be ready for prime time when WordPress 5.6 lands” in my previous post on the new feature’s readiness. I also said that was on the condition of not trying to make it work with the customizer — that experience was still broken. However, the 5.6 team pulled the plug on block-based widgets for the second time this year.

\n\n\n\n

One week ago, WordPress 5.6 release lead Josepha Haden seemed to agree that it would be ready. However, things can change quickly in a development cycle, and tough decisions have to be made with beta release deadlines.

\n\n\n\n

This is not the first feature the team has punted to a future release. Two weeks ago, they dropped block-based nav menus from the 5.6 feature list. Both features were originally planned for WordPress 5.5.

\n\n\n\n

A new Widgets admin screen has been under development since January 2019, which was not long after the initial launch of the block editor in WordPress 5.0. For now, the block-based widgets feature has been punted to WordPress 5.7. It has also been given the “early” tag, which means it should go into core WordPress soon after the 5.7 release cycle begins. This will give it more time to mature and more people an opportunity to test it.

\n\n\n\n

Helen Hou-Sandì, the core tech lead for 5.6, provided a historical account of the decision and why it was not ready for inclusion in the new ticket:

\n\n\n\n

My question for features that affect the front-end is “can I try out this new thing without the penalty of messing up my site?” — that is, user trust. At this current moment, given that widget areas are not displayed anything like what you see on your site without themes really putting effort into it and that you have to save your changes live without revisions to get an actual contextual view, widget area blocks do not allow you to try this new feature without penalizing you for experimenting.

\n\n\n\n

She went on to say that the current experience is subpar at the moment. Problems related to the customizer experience, which I covered in detail over a month ago, were also mentioned.

\n\n\n\n

“So, when we come back to this again, let’s keep sight of what it means to keep users feeling secure that they can get their site looking the way they want with WordPress, and not like they are having to work around what we’ve given them,” said Hou-Sandì.

\n\n\n\n

This is a hopeful outlook despite the tough decision. Sometimes, these types of calls need to be made for the good of the project in the long term. Pushing back a feature to a future version for a better user experience can be better than launching early with a subpar experience.

\n\n\n\n

“The good part of this is that now widgets can continue to be ‘re-imagined’ for 5.7, and get even more enhancements,” said lead WordPress developer Andrew Ozz in the ticket. “Not sure how many people have tested this for a bit longer but having blocks in the widgets areas (a.k.a. sidebars) opens up many new possibilities and makes a lot of the old, limited widgets obsolete. The ‘widget areas’ become something like ‘specialized posts with more dynamic content,’ letting users (and designers) do a lot of stuff that was either hard or impossible with the old widgets.”

\n\n\n\n

After the letdown of seeing one of my most anticipated features of 5.6 being dropped, it is encouraging to see the positive outlook from community leaders on the project.

\n\n\n\n

“You know, I was really hopeful for it too, and that last-minute call was one I labored over,” said Haden. “When I last looked, it did seem close to ready, but then more focused testing was done and there were some interactions that are a little rough for users. I’m grateful for that because the time to discover painful user experiences is before launch rather than after!”

\n\n\n\n

Despite dropping its second major feature, WordPress 5.6 still has some big highlights that will be shipping in less than two months. The new Twenty Twenty-One theme looks to be a breath of fresh air and will explore block-related features not seen in previous default themes. Haden also pointed out auto-updates for major releases, application passwords support for the REST API, and accessibility improvements as features to look forward to.

\n\n\n\n

WordPress 5.6 Beta 1 is expected to ship today.

\n\n\n\n

Adding New Features To an Old Project

\n\n\n\n

At times, it feels like the Gutenberg project has bitten off more than it can chew. Many of the big feature plans continually miss projections. Between full-site editing, global styles, widgets, nav menus, and much more, it is tough to get hyper-focused on one feature and have it ready to ship. On the other hand, too much focus one way can be to the detriment to other features in the long run. All of these pieces must eventually come together to create a more cohesive whole.

\n\n\n\n

WordPress is also 17 years old. Any new feature could affect legacy features or code. The goal for block-based widgets is to transition an existing feature to work within a new system without breaking millions of websites in the process. Twenty-one months of work on a single feature shows that it is not an easy problem to solve.

\n\n\n\n

“You are so right about complex engineering problems!” said Haden. “We are now at a point in the history of the project where connecting all of the pieces can have us facing unforeseen complications.”

\n\n\n\n

The project also needs to think about how it can address some of the issues it has faced with not quite getting major features to completion. Is the team stretched too thin to focus on all the parts? Are there areas we can improve to push features forward?

\n\n\n\n

“There will be a retrospective where we can identify what parts of our process can be improved in the future, but I also feel like setting stretch goals is good for any software project,” said Haden. “Many contributors have a sense of urgency around bringing the power of blocks to more spaces in WordPress, which I share, but when it’s time to ship, we have to balance that with our deep commitment to usability.”

\n\n\n\n

One problem that has become increasingly obvious is that front-end editing has become tougher over the years. Currently, widgets and nav menus can be edited in two places in WordPress with wildly different interfaces. Full-site editing stands to add an entirely new interface to the mix.

\n\n\n\n

“I think one of the problems that we’re trying to solve with Gutenberg has always been a more consistent experience for editing elements across the WordPress interface,” said Haden. “No user should have to learn five different workflows to make sure their page looks the way they imagined it when it’s published.”

\n\n\n\n

In the meantime, which may be numbered in years, end-users will likely have these multiple interfaces to deal with — overlap while new features are being developed. This may simply be a necessary growing pain of an aging project, one that is trying to lead the pack of hungry competitors in the CMS space.

\n\n\n\n

“There’s a lot of interest in reducing the number of workflows, and I’m hopeful that we can consolidate down to just one beautiful, intuitive interface,” said Haden.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 21:16:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce Tests New Instagram Shopping Checkout Feature, Now in Closed Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106398\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2878:\"

Instagram’s checkout feature, which allows users to purchase products without leaving the app, has become an even more important part of Facebook’s long-term investment in e-commerce now that the pandemic has so heavily skewed consumer behavior towards online shopping. When Instagram introduced checkout in 2019, it reported that 130 million users were tapping to reveal product tags in shopping posts every month.

\n\n\n\nimage credit: Instagram\n\n\n\n

Business owners who operate an existing store can extend their audience to Instagram by funneling orders from the social network into their own stores, without shoppers having to leave Instagram. Checkout supports integration with several e-commerce platform partners, including Shopify and BigCommerce, and will soon be available for WooCommerce merchants.

\n\n\n\n

WooCommerce is testing a new Instagram Shopping Checkout feature for its Facebook for WooCommerce plugin. The free extension is used on more than 900,000 websites and will provide the bridge for store owners who want to tap into Instagram’s market. The checkout capabilities are currently in closed beta. Anyone interested to test the feature can sign up for consideration. Businesses registered in the USA that meet certain other requirements may be selected to participate, and the beta is also expanding to other regions soon.

\n\n\n\n

WooCommerce currently supports shoppable posts, which are essentially products sourced from a product catalog created on Facebook that are then linked to the live store through an Instagram business account. Instagram’s checkout takes it one step further to provide a native checkout experience inside the app. Merchants pay no selling fees until December 31, 2020. After that time, the fee is 5% per shipment or a flat fee of $0.40 for shipments of $8.00 or less. 

\n\n\n\n

On the customer side, shoppers only have to enter their information once and thereafter it is stored for future Instagram purchases. Instagram also pushes shipment and delivery notifications inside the app. Store owners will need to weigh whether the convenience of the in-app checkout experience is worth forking over 5% to Facebook, or if they prefer funneling users over to the live store instead.

\n\n\n\n

Instagram Shopping Checkout is coming to WooCommerce in the near future but the company has not yet announced a launch date, as the feature is just now entering closed beta.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 04:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Past Twenty* WordPress Themes To Get New Block Patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106396\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/past-twenty-wordpress-themes-to-get-new-block-patterns?utm_source=rss&utm_medium=rss&utm_campaign=past-twenty-wordpress-themes-to-get-new-block-patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6608:\"

Mel Choyce-Dwan, the Default Theme Design Lead for WordPress 5.6, kick-started 10 tickets around two months ago that would bring new features to the old default WordPress themes. The proposal is to add unique block patterns, a feature added to WordPress 5.5, to all of the previous 10 Twenty* themes. It is a lofty goal that could breathe some new life into old work from the previous decade.

\n\n\n\n

Currently, only the last four themes are marked for an update by the time WordPress 5.6 lands. Previous themes are on the list to receive their block patterns in a future release. For developers and designers interested in getting involved, the following is a list of the Trac tickets for each theme:

\n\n\n\n\n\n\n\n

If you are wondering where Twenty Eighteen is in that list, that theme does not actually exist. It is the one missing year the WordPress community has had since the one-default-theme-per-year era began with Twenty Ten. It is easy to forget that we did not get a new theme for the 2017-2018 season. With all that has happened in the world this year, we should count ourselves fortunate to see a new default theme land for WordPress this December. WordPress updates and its upcoming default theme are at least one consistency that we have had in an otherwise chaotic time.

\n\n\n\n

More than anything, it is nice to see some work going toward older themes — not just in terms of bug fixes but feature updates. The older defaults are still a part of the face of WordPress. Twenty Twenty and Twenty Seventeen each have over one million active installs. Twenty Nineteen has over half a million. The other default themes also have significant user bases in the hundreds of thousands — still some of the most-used themes in the directory. We owe it to those themes’ users to keep them fresh, at least as long as they maintain such levels of popularity.

\n\n\n\n

This is where the massive theme development community could pitch in. Do some testing of the existing patches. Write some code for missing patterns or introduce new ideas. This is the sort of low-hanging fruit that almost anyone could take some time to help with.

\n\n\n\n

First Look at the New Patterns

\n\n\n\n

None of the proposed patterns have landed in trunk, the development version of WordPress, yet. However, several people have created mockups or added patches that could be committed soon.

\n\n\n\n

One of my favorite patterns to emerge thus far is from Beatriz Fialho for the Twenty Nineteen theme. Fialho has created many of the pattern designs proposed thus far, but this one, in particular, stands out the most. It is a simple two-column, two-row pattern with a circular image, heading, and paragraph for each section. Its simplicity fits in well with the more elegant, business-friendly look of the Twenty Nineteen theme.

\n\n\n\nServices pattern for Twenty Nineteen.\n\n\n\n

It is also fitting that Twenty Nineteen get a nice refresh with new patterns because it was the default theme to launch with the block editor. Ideally, it would continually be updated to showcase block-related features.

\n\n\n\n

While many people will focus on some of the more recent default themes, perhaps the most interesting one is a bit more dated. Twenty Thirteen was meant to showcase the new post formats feature in WordPress 3.6. According to Joen Asmussen, the theme’s primary designer, the original idea was for users to compose a ribbon of alternating colors as each post varied its colors.

\n\n\n\n

“The alternating ribbon of colors did not really come to pass because post formats were simply not used enough to create an interesting ribbon,” he wrote in the Twenty Thirteen ticket. “However, perhaps for block patterns, we have an opportunity to revisit those alternating ribbons of colors. In other words, I’d love to see those warm bold colors used in big swathes that take up the whole pattern background.”

\n\n\n\n
Patterns designed to match post formats.\n\n\n\n

This could be a fun update for end-users who are still using that feature that shall not be named post formats.

\n\n\n\n

There is a lot to like about many of the pattern mockups so far. I look forward to seeing what lands along with WordPress 5.6 and in future updates.

\n\n\n\n

Establishing Pattern Category Standard

\n\n\n\n

With the more recent Twenty Twenty-One theme’s block patterns and the new patterns being added to some of the older default themes, it looks like a specific pattern category naming scheme is starting to become a standard. Of the patches thus far, each is creating a new pattern category named after the theme itself.

\n\n\n\n

This makes sense. Allowing users to find all of their theme’s patterns in one location means that they can differentiate between them and those from core or other plugins. Third-party theme authors should follow suit and stick with this convention for the same reason.

\n\n\n\n

Developers can also define multiple categories for a single pattern. This allows theme authors to create a category that houses all of their patterns in one location. However, they can also split them into more appropriate context-specific categories for discoverability.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 19 Oct 2020 21:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"BuddyPress: BuddyPress 7.0.0-beta1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://buddypress.org/?p=315150\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://buddypress.org/2020/10/buddypress-7-0-0-beta1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4332:\"

BuddyPress 7.0.0-beta1 is now available for testing!

\n\n\n\n

Please note the plugin is still in development, so we recommend running this beta release on a testing site.

\n\n\n\n

You can test BuddyPress 7.0.0-beta1 in 4 ways :

\n\n\n\n\n\n\n\n

The 7.0.0 stable release is slated to the beginning of December, and we’d love you to give us a hand to get there!

\n\n\n\n

Please note BuddyPress 7.0.0 will require at least WordPress 4.9.

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute. Here are some of the big changes and features to pay close attention to while testing (Check out this report on Trac for the full list).

\n\n\n\n
\n\n\n\n

New Administration screens to manage BuddyPress types

\n\n\n\n

In BuddyPress 7.0.0 site administrators will be able to add, edit or delete Member & Group types using their WordPress Administration Screens just like they would do for Post tags.

\n\n\n\n

Read this development note to learn more about it.

\n\n\n\n
\n\n\n\n

Let’s welcome 3 new BP Blocks into our Block Editor

\n\n\n\n
  • The Activity Embed block let authors embed an activity into their post or page.
  • Use the BP Members block to select community users you want to feature into a post or a page.
  • Enjoy the BP Groups block to pick the groups you want to highlight into a post or a page.
\n\n\n\n

Get to know these new blocks reading this development note.

\n\n\n\n
\n\n\n\n

Improved support for WP CLI

\n\n\n\n

WP-CLI is the command-line interface for WordPress. You can update plugins, configure multisite installs, and much more, without using a web browser. In 7.0.0, you will be able to Enjoy new BuddyPress CLI commands to manage BuddyPress Group Meta, BuddyPress Activity Meta, activate or deactivate the BuddyPress signup feature and create BuddyPress specific testing code for plugins.

\n\n\n\n

Discover more about it from this development note.

\n\n\n\n
\n\n\n\n

And so much more such as improvements to the BP REST API, our Template pack, images and iframes lazy loading support…

\n\n\n\n
\n\n\n\n

How You Can Help

\n\n\n\n

Do you speak a language other than English? Help us translate BuddyPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, file one on BuddyPress Trac.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 22:30:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:12:\"Mathieu Viet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:89:\"WPTavern: Using the Web Stories for WordPress Plugin? You Better Play By Google’s Rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105848\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:215:\"https://wptavern.com/using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules?utm_source=rss&utm_medium=rss&utm_campaign=using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4080:\"Web Stories dashboard screen in WordPress.\n\n\n\n

What comes as a surprise to few, Google has updated its content guidelines for its Web Stories format. For users of its recently-released Web Stories for WordPress plugin, they will want to follow the extended rules for their Stories to appear in the “richer experiences” across Google’s services. This includes the grid view on Search, Google Images, and Google Discover’s carousel.

\n\n\n\n

Google released its Web Stories plugin in late September to the WordPress community. It is a drag-and-drop editor that allows end-users to create custom Stories from a custom screen in their WordPress admin.

\n\n\n\n
Visual Stories on Search.
\n\n\n\n

The plugin does not directly link to Google’s content guidelines anywhere. For users who do not do a little digging, they may be caught unaware if their stories are not surfaced in various Google services.

\n\n\n\n

On top of the Discover and Webmaster guidelines, Web Stories have six additional restrictions related to the following:

\n\n\n\n
  • Copyrighted content
  • Text-heavy Web Stories
  • Low-quality assets
  • Lack of narrative
  • Incomplete stories
  • Overly commercial
\n\n\n\n

While not using copyrighted content is one of those reasonably-obvious guidelines, the others could trip up some users. Because Stories are meant to represent bite-sized bits of information on each page, they may become ineligible if most pages have more than 180 words of text. Videos should also be limited to fewer than 60 seconds on each page.

\n\n\n\n

Low-quality media could be a flag for Stories too. Google’s guidelines point toward “stretched out or pixelated” media that negatively impacts the reader’s experience. They do not offer any specific resolution guidelines, but this should mostly be a non-issue today. The opposite issue is far more likely — users uploading media that is too large and not optimized for viewing on the web.

\n\n\n\n

The “lack of narrative” guideline is perhaps the vaguest, and it is unclear how Google will monitor or police narrative. However, the Stories format is about storytelling.

\n\n\n\n

“Stories are the key here imo,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Google specifically states that Stories need a “binding theme or narrative structure” from one page to the next. Essentially, the company is telling users to use the format for the purpose it was created for. They also do not want users to create incomplete stories where readers must click a link to finish the Story or get information.

\n\n\n\nCNN’s Web Story on Remembering John Lennon.\n\n\n\n

Overly commercial Stories are frowned upon too. While Google will allow affiliate marketing links, they should be restricted to a minor part of the experience.

\n\n\n\n

Closing his Twitter thread, Marsland seemed to hit the point. “I’ve seen some initial Google Web Stories where the platform is being used as a replacement for a brochure or website,” he wrote. “In my view that’s a huge missed opportunity. If I was advising brands I would say ‘Tell Stories’ this is a platform for Story Telling.”

\n\n\n\n

If users of the plugin follow this advice, their Stories should surface on Google’s rich search experiences.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 20:51:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:45:\"WPTavern: Stripe Acquires Paystack for $200M+\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106269\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:131:\"https://wptavern.com/stripe-acquires-paystack-for-200m?utm_source=rss&utm_medium=rss&utm_campaign=stripe-acquires-paystack-for-200m\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3196:\"

The big news in the world of e-commerce today is Stripe’s acquisition of Paystack, a Nigeria-based payments system that is widely used throughout African markets. The company, which became informally known as “the Stripe of Africa” picked up $8 million in Series A funding in 2018, led by Stripe, Y Combinator, and Tencent. Paystack has grown to power more than 60,000 businesses, including FedEx, UPS, MTN, the Lagos Internal Revenue Service, and AXA Mansar.

\n\n\n\n

Stripe’s acquisition of the company is rumored to be more than $200M, a small price to pay for a foothold in emerging African markets. In the company’s announcement, Stripe noted that African online commerce is growing 21% year-over-year, 75% faster than the global average. Paystack dominates among payment systems, accounting for more than half of all online transactions in Nigeria.

\n\n\n\n

“In just five years, Paystack has done what many companies could not achieve in decades,” Stripe EMEA business lead Matt Henderson said. “Their tech-first approach, values, and ambition greatly align with our own. This acquisition will give Paystack resources to develop new products, support more businesses and consolidate the hyper-fragmented African payments market.”

\n\n\n\n

Long term, Stripe plans to embed Paystack’s capabilities in its Global Payments and Treasury Network (GPTN), the company’s programmable infrastructure for global money movement.

\n\n\n\n

“Paystack merchants and partners can look forward to more payment channels, more tools, accelerated geographic expansion, and deeper integrations with global platforms,” Paystack CEO and co-founder Shola Akinlade said. He also assured customers that there’s no need to make any changes to their technical integrations, as Paystack will continue expanding and operating independently in Africa.

\n\n\n\n

Paystack is used as a payment gateway for thousands of WordPress-powered stores through plugins for WooCommerce, Easy Digital Downloads, Paid Membership Pro, Give, Contact Form 7, and an assortment of booking plugins. The company has an official WordPress plugin, Payment Forms for Paystack, which is active on more than 6,000 sites, but most users come through the Paystack WooCommerce Payment Gateway (20,000+ active installations).

\n\n\n\n

Stripe’s acquisition was a bit of positive news during what is currently a turbulent time in Nigeria, as citizens are actively engaged in peaceful protests to end police brutality. Paystack’s journey is an encouraging example of the flourishing Nigerian tech ecosystem and the possibilities available for smaller e-commerce companies that are solving problems and removing barriers for businesses in emerging markets.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 22:26:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:10;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WPTavern: Diving Into the Book Review Block Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106273\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:145:\"https://wptavern.com/diving-into-the-book-review-block-plugin?utm_source=rss&utm_medium=rss&utm_campaign=diving-into-the-book-review-block-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6791:\"

Created by Donna Peplinskie, a Product Wrangler at Automattic, the Book Review Block plugin is nearly three years old. However, it only came to my attention during a recent excursion to find interesting block plugins.

\n\n\n\n

The plugin does pretty much what it says on the cover. It is designed to review books. It generally has all the fields users might need to add to their reviews, such as a title, author, image, rating, and more. The interesting thing is that it can automatically fill in those details with a simple ISBN value. Plus, it supports Schema markup, which may help with SEO.

\n\n\n\n

Rain or shine, sick or well, I read every day. I am currently a month and a half shy of a two-year reading streak. When the mood strikes, I even venture to write a book review. As much as I want to share interesting WordPress projects with the community, I sometimes have personal motives for testing and writing about plugins like Book Review Block. Anything that might help me or other avid readers share our thoughts on the world of literature with others is of interest.

\n\n\n\n

Admittedly, I was excited as I plugged in the ISBN for Rhthym of War, the upcoming fourth book of my favorite fantasy series of all time, The Stormlight Archive. I merely needed to click the “Get Book Details” button.

\n\n\n\n

Success! The plugin worked its magic and pulled in the necessary information. It had my favorite author’s name, the publisher, the upcoming release date, and the page count. It even had a long description, which I could trim down in the editor.

\n\n\n\nDefault output of the Book Review block.\n\n\n\n

There was a little work to make this happen before the success. To automatically pull in the book details, end-users must have an API Key from Google. It took me around a minute to set that up and enter it into the field available in the block options sidebar. The great thing about the plugin is that it saves this key so that users do not have to enter each time they want to review a book.

\n\n\n\n

Book Review Block a good starting point. It is straightforward and simple to use. It is not yet at a point where I would call it a great plugin. However, it could be.

\n\n\n\n

Falling Short

\n\n\n\n

The plugin’s Book Review block should be taking its cues from the core Media & Text block. When you get right down to it, the two are essentially doing the same thing visually. Both are blocks with an image and some content sitting next to each other.

\n\n\n\n

The following is a list of items where it should be following core’s lead:

\n\n\n\n
  • No way to edit alt text (book title is automatically used).
  • The image is always aligned left and the content to the right with no way to flip them.
  • The media and content are not stackable on mobile views.
  • Cannot adjust the size of the image or content columns.
  • While inline rich-text controls are supported, users cannot add Heading, List, or Paragraph blocks to the content area and use their associated block options.
\n\n\n\n

That is the shortlist that could offer some quick improvements to the user experience. Ultimately, the problems with the plugin essentially come down to not offering a way to customize the output.

\n\n\n\n

One of the other consistent problems is that the book image the plugin loads is always a bit small. This seems to be more of an issue from the Google Books API than the plugin. Each time I tested a book, I opted to add a larger image — the plugin does allow you to replace the default.

\n\n\n\n

The color settings are limited. The block only offers a background color option with no way to adjust the text color. A better option for plugin users is to wrap it in a Group block and adjust the background and text colors there.

\n\n\n\nBook Review block wrapped inside a Group block.\n\n\n\n

It would also be nice to have wide and full-alignment options, which is an often-overlooked featured from many block plugin authors.

\n\n\n\n

Using the Media & Text Block to Recreate the Book Review Block

\n\n\n\n

The Book Review Block plugin has a lot of potential, and I want to see it evolve by providing more flexibility to end-users. Because the Media & Text block is the closest core block to what the plugin offers, I decided to recreate a more visually-appealing design with it.

\n\n\n\nBook review section created with the Media & Text block.\n\n\n\n

I made some adjustments on the content side of things. I used the Heading block for the book title, a List block for the book metadata, and a Paragraph block for the description.

\n\n\n\n

The Media & Text block also provided me the freedom to adjust the alignment, stack the image and content on mobile views, and tinker with the size of the image. Plus, it has that all-important field for customizing the image alt attribute.

\n\n\n\n

The Media & Text block gave me much more design mileage.

\n\n\n\n

However, there are limitations to the core block. It does not fully capture some of the features available via the Book Review block. The most obvious are the automatic book details via an ISBN and the Schema markup. Less obvious, there is no easy way to recreate the star rating — I used emoji stars — and long description text does not wrap under the image. To recreate that, you would have to opt to use a left-aligned image followed by content.

\n\n\n\n

Overall, the Media & Text block gives me the ability to better style the output, which is what I am more interested in as a user. I want to put my unique spin on things. That is where the Book Review Plugin misfires. It is also the sort of thing that the plugin author can iterate on, offering more flexibility in the future.

\n\n\n\n

This is where many block plugins go wrong, particularly when there is more than one or two bits of data users should enter. Blocks represent freedom in many ways. However, when plugin developers stick to a rigid structure, users can sometimes lose that sense of freedom that they would otherwise have with building their pages.

\n\n\n\n

One of the best blocks, hands down, that preserves that freedom is from the Recipe Block plugin. It has structured inputs and fields. However, it allows freeform content for end-users to make it their own.

\n\n\n\n

When block authors push beyond this rigidness, users win.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 20:44:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:11;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce 4.6 Makes New Home Screen the Default for New and Existing Stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3018:\"

WooCommerce 4.6 was released today. The minor release dropped during WooSesh, a global, virtual conference dedicated to WooCommerce and e-commerce topics. It features the new home screen as the default for all stores. Previously, the screen was only the default on new stores. Existing store owners had to turn the feature on in the settings.

\n\n\n\n
\n\n\n\n

The updated home screen, originally introduced in version 4.3, helps store admins see activity across the site at a glance and includes an inbox, quick access to store management links, and an overview of stats on sales, orders, and visitors. This redesigned virtual command center arrives not a moment too soon, as anything that makes order management more efficient is a welcome improvement, due to the sheer volume of sales increases that store owners have seen over the past eight months.

\n\n\n\n

In stark contrast to industries like hospitality and entertainment that have proven to be more vulnerable during the pandemic, e-commerce has seen explosive growth. During the State of the Woo address at WooSesh 2020, the WooCommerce team shared that e-commerce is currently estimated to be a $4 trillion market that will grow to $4.5 trillion by 2021. WooCommerce accounts for a sizable chunk of that market with an estimated total payment volume for 2020 projected to reach $20.6 billion, a 74% increase compared to 2019.

\n\n\n\n

The WooCommerce community is on the forefront of that growth and is deeply invested in the products that are driving stores’ success. The WooCommerce team shared that 75% of people who build extensions also build and maintain stores for merchants, and 70% of those who build stores for merchants also build and maintain extensions or plugins. In 2021, they plan to invest heavily in unlocking more features in more countries and will make WooCommerce Payments the native payment method for the global platform.

\n\n\n\n

A new report from eMarketer shows that US e-commerce growth has jumped 32.4%, accelerating the online shopping shift by nearly two years. Experts also predict the top 10 e-commerce players will swallow up more of US retail spending to account for 63.2% of all online sales this year, up from 57.9% in 2019.

\n\n\n\n

The increase in e-commerce spending may not be entirely tied to the pandemic, as some experts believe this historic time will mark permanent changes in consumer spending habits. This is where independent stores, powered by WooCommerce and other technologies, have the opportunity to establish a strong reputation for themselves by providing quality products and reliable service, as well as by being more nimble in the face of pandemic-driven increases in volume.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 03:48:32 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:12;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:101:\"WPTavern: The Future of Starter Content: WordPress Themes Need a Modern Onboarding and Importing Tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106177\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool?utm_source=rss&utm_medium=rss&utm_campaign=the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7385:\"Image credit: picjumbo.com on Pexels.\n\n\n\n

Starter content. It was a grand idea, one of those big dreams of WordPress. It was the new kid on the block in late 2016. Like the introduction of post formats in 2011, the developer community was all in for at least that particular release version. Then, it was on to the next new thing, with the feature dropping off the radar for all but the most ardent evangelists.

\n\n\n\n

Some of us were burned over the years, living and dying by the progress of features that we wanted most.

\n\n\n\n

Released in WordPress 4.7, starter content has since seemed to be going the way of post formats. After four years, only 141 themes in the WordPress theme directory support the feature. There has been no movement to push it beyond its initial implementation. And, it never really covered the things that theme authors wanted in the first place. It was a start. But, themers were ultimately left to their own devices, rolling custom solutions for something that never panned out — fully-featured demo and imported content. Four years is an eternity in the web development world, and there is no sense in waiting around to see if WordPress would push forward.

\n\n\n\n

Until Helen Hou-Sandí published Revisiting Starter Content last week, most would have likely assumed the feature would be relegated to legacy code used by old-school fans of the feature and those theme authors who consider themselves completionists.

\n\n\n\n

“Starter content in 4.7 was always meant to be a step one, not the end goal or even the resting point it’s become,” wrote Hou-Sandí. “There are still two major things that need to be done: themes should have a unified way of showing users how best to put that theme to use in both the individual site and broader preview contexts, and sites with existing content should also be able to take advantage of these sort of ‘ideal content’ definitions.”

\n\n\n\n

Step two should have been this four-year-old accompanying ticket to allow users to import starter content into existing, non-fresh sites.

\n\n\n\n

Since the initial feature dropped, the theme landscape has changed. Let’s face it. WordPress might simply not be able to compete with theme companies that are pushing the limits, creating experiences that users want at much swifter speeds.

\n\n\n\n

Look at where the Brainstorm Force’s Starter Templates plugin for its Astra theme is now. Users can click a button and import a full suite of content-filled pages or even individual templates. And, the Astra theme is not alone in this. It has become an increasingly-common standard to offer some sort of onboarding to users. GoDaddy’s managed WordPress service fills a similar need on the hosting end.

\n\n\n\nAstra’s starter templates and content.\n\n\n\n

As WordPress use becomes more widespread, the more it needs a way to onboard users.

\n\n\n\n

This essentially boils down to the question: how can I make it look like the demo?

\n\n\n\n

Ah, the age-old question that theme authors have been trying to solve. Whether it has been limitations in the software or, perhaps, antiquated theme review guidelines related to demo and imported content, this has been a hurdle that has been tough to jump. But, some have sailed over it and moved on. While WordPress has seemingly been twiddling its thumbs for years, Brainstorm Force and other theme companies have solved this and continued to innovate.

\n\n\n\n

This is not necessarily a bad thing. There are plenty of ideas to steal copy and pull into the core platform.

\n\n\n\n

One of the other problems facing the WordPress starter content feature is that it is tied to the customizer. With the direction of the block system, it is easy to ask what the future holds. The customizer — originally named the theme customizer — was essentially a project to allow users to make front-end adjustments and watch those customizations happen in real time. However, new features like global styles and full-site editing are happening on their own admin screens. Most theme options will ultimately be relegated to global styles, custom templates, block styles, and block patterns. There may not be much left for the customizer to do.

\n\n\n\n

Right now, there are too many places in WordPress to edit the front-end bits of a WordPress site. My hope is that all of these things are ultimately merged into one less-confusing interface. But, I digress…

\n\n\n\n

Starter content should be rethought. Whoever takes the reins on this needs a fresh take that adopts modern methods from leading theme companies.

\n\n\n\n

The ultimate goal should be to allow theme authors to create multiple sets of templates/content that end-users can preview and import. It should not be tied to whether it is a new site. Any site owner should be able to import content and have it automagically go live. It should also be extendable to allow themes to support page builders like Elementor, Beaver Builder, and many others.

\n\n\n\n

This seems to be in line with Hou-Sandí’s thoughts. “For a future release, we should start exploring what it might look like to opt into importing starter content into existing sites, whether wholesale or piecewise,” she wrote. “Many of us who work in the WordPress development/consulting space tend not to ever deal in switching between public themes on our sites, but let’s not forget that’s a significant portion of our user audience and we want to continue to enable them to not just publish but also publish in a way that matches their vision.”

\n\n\n\n

Let’s do it right this go-round, keep a broad vision, and provide an avenue for theme authors to adopt a standardized core WordPress method instead of having everyone build in-house solutions.

\n\n\n\n

I haven’t even touched on the recent call to use starter content for WordPress.org theme previews. It will take more than ideas to excite many theme authors about the possibility. That ticket has sat for seven years with no progress, and most have had it on their wish list for much longer. It is an interesting proposal, one that has been tossed around in various team meetings for years.

\n\n\n\n

Like so many other things, theme authors have either given up hope or moved onto doing their own thing. They need to be brought into the fold, not only as third parties who are building with core WordPress tools but as developers who are contributing to those features.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 14 Oct 2020 20:07:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:13;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:116:\"WPTavern: Google Podcasts Manager Adds More Data from Search: Impressions, Top-Discovered Episodes, and Search Terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:271:\"https://wptavern.com/google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms?utm_source=rss&utm_medium=rss&utm_campaign=google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2568:\"

Google announced an expansion of listener engagement metrics today for those using its Podcast Manager. Previously, audience insights included data about the types of devices listeners are using, where listeners tune in and drop off during a given episode, total number of listens, and listening duration, but the service lacked analytics regarding how visitors were discovering the podcast.

\n\n\n\n

Google is remedying that today by expanding the dashboard to show impressions, clicks, top-discovered episodes, and search terms that brought listeners to the podcast. This information can help podcasters understand how their content is getting discovered so they can better tailor their episodes to attract more new listeners.

\n\n\n\n

The podcasting industry has seen remarkable growth over the past five years, which previously led experts to project that marketers will spend over $1 billion in advertising by 2021. After the pandemic hit, podcast listening took a downturn in the U.S. but at the same time, podcast creators have found more time to create new shows and episodes. Businesses are turning to the medium to supplement traditional marketing methods that no longer have the same impact now that consumer spending habits heavily favor online products.

\n\n\n\n

Along with the new metrics available inside Google Podcasts Manager, the company also published a guide to optimizing podcasts for Google Search. It highlights four important items for making sure a podcast can be found:

\n\n\n\n
  • Detailed show and episode metadata
  • Ensure the podcast’s webpage and RSS data match
  • Include cover art
  • Ensure Googlebot can access your audio files
\n\n\n\n

A detailed breakdown of your audience’s listening habits isn’t worth much if you’re having trouble getting your podcast discovered. Any podcasting plugin for WordPress should handle these basic optimization recommendations, but if you are still having trouble being found via Google, you can dig deeper into the podcast setup guide for more detailed recommendations.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 22:57:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:14;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Are Block-Based Widgets Ready To Land in WordPress 5.6?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/are-block-based-widgets-ready-to-land-in-wordpress-5-6?utm_source=rss&utm_medium=rss&utm_campaign=are-block-based-widgets-ready-to-land-in-wordpress-5-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8214:\"

Two weeks ago, the Gutenberg team put out an open call for block-based widgets feedback. I had already written a lengthy review of the new system earlier in September but was asked by a member of the team to share my thoughts on the most recent iteration. With the upcoming freeze for WordPress 5.6 Beta 1 just a week away, I figured it would not hurt to do another deep dive.

\n\n\n\n

For reference, my latest testing is against version 9.2.0-alpha-172f589 of the Gutenberg plugin, which was a build from earlier today. Gutenberg development moves fast, but everything should be accurate to that point.

\n\n\n\n

Ultimately, many of the problems I pointed out over a month ago still exist. However, the team has cleaned most of the minor issues, such as pointing the open/close arrows for sidebars (block areas) in the correct direction and making it more consistent with the post-editing screen. The UI is much more polished.

\n\n\n\n

Before I dive into all the problems, I want to answer the question I am proposing. Yes, the block-based widget system will be ready for prime time when WordPress 5.6 lands. It is not there yet, but it is at a point where there is a clear finish line that is reachable in the next two months.

\n\n\n\n

I will ignore the failure of block-based widgets in the customizer, which landed in Gutenberg 8.9 and was removed in 9.1. I will also look past the recent proposal to reconstruct the widgets screen to use the Customize API, at least for now. There is a boatload of problems that block-based widgets present for the customizer, and those problems are insurmountable for WordPress 5.6. Long term, WordPress needs to have a single place for editing widget/block areas. Users will likely have to live with some inconsistencies for a while.

\n\n\n\n

Assuming the team does not try to throw a last-minute Hail Mary and implement full editing of blocks in the customizer this round, it is safe to say that block-based widgets are well on their way toward a successful WordPress 5.6 debut.

\n\n\n\n

The User Experience

\n\n\n\nBlock-based widgets screen.\n\n\n\n

As a user, I genuinely enjoy using the new Widgets admin screen. The open-ended, free-form block areas create untold possibilities for designing my WordPress sites. Traditional widgets were limited in scope. Users were buckled down to a handful of core widgets, possibly some plugin widgets, and whatever their theme author offered up. However, with blocks, the pool of choices expands to at least triple the out-of-the-box options (I am not counting embed-type blocks individually). Plus, blocks provide a far more extensive set of design options than a traditional widget.

\n\n\n\n

In comparison, traditional widgets are outdated. Blocks are superior in almost every way. However, there are still problems with this new system.

\n\n\n\n

The biggest issue right now is that end-users can exit the Widgets screen without saving their changes. There is no warning to let them know that all their work is about to be lost in the ether. This is one of those OMGBBQ-level items that need to happen before WordPress 5.6 drops.

\n\n\n\n

One nice-to-have-but-not-necessary feature would be the ability to drag blocks from one block area to another. In the old widgets system, users could move widgets from sidebar to sidebar. The current alternative is to copy a widget, paste it in a new block area, and remove the original.

\n\n\n\n

I am also not a fan of not having an option for the top toolbar, which is available on the post-editing screen. One of the reasons for using this toolbar is because I dislike the default popup toolbar on individual blocks. It is distracting and often gets in the way of my work.

\n\n\n\n

Legacy widgets seem to still be a work in progress. The Legacy Widget block did not work at all for me at times. Then, it magically began to work. However, Gutenberg does now automatically add registered third-party widgets to the block inserter just as if they were blocks.

\n\n\n\nGetting a plugin’s widget to work.\n\n\n\n

This presented its own problems. The only way I managed to make third-party plugin widgets work was to insert the widget, save, and refresh the widgets screen. At that point, the widgets appeared and became editable.

\n\n\n\n

The Theme Author Experience

\n\n\n\n

One of my biggest concerns for theme authors right now is that there does not seem to be any documentation in the block editor handbook. There is plenty of time to make that happen, but there are things theme authors need to be aware of. Having a centralized location, even while the feature is under development, would help them gear up for the 5.6 release.

\n\n\n\n

Some of these questions, which may be answered in various Make blog posts, should exist on a dedicated documentation page:

\n\n\n\n
  • How can a theme opt out of block-based widgets?
  • What are the hooks to add custom styles for the Widgets screen?
  • Can themes target specific sidebar styles on the Widgets screen?
  • Is it possible to consistently style sections like traditional widgets on the front end?
  • Can themes opt into wide and full-alignment within block areas, which could essentially be used similarly to the post content area?
\n\n\n\n

These are some of the questions I would want to be answered as a former theme author. I am no longer in the thick of the theme design game and presume that those who are would have a larger list of questions.

\n\n\n\n

One less-obvious piece of documentation should center on how to handle fallbacks or default widgets. Traditionally, themes that needed to show a default set of widgets would check if the sidebar has widgets and fall back to using the_widget() to output one or more defaults. While theme authors can still do that, we should start to transition them across the board to the block system.

\n\n\n\n

Should theme authors copy/paste block HTML as a fallback? Would the starter content system be better for this, and can starter widget content handle blocks? What is the recommended method for widget fallbacks in WordPress 5.6?

\n\n\n\n

There is still the ongoing issue of how theme authors should handle the traditional widget and widget title wrapper HTML in the new block paradigm. One patch added since the Gutenberg 9.1 release wraps every top-level block with the widget wrapper. If this lands in the 9.2 release, it will likely make the issue worse.

\n\n\n\n

In the traditional system, both the widget title and content are wrapped within a container together. However, if a user adds a Heading block (widget title) and another block (widget content), each block is wrapped separately with the theme’s widget wrappers. The only way to rectify the situation as it stands is for end-users to add a Group block for each “widget” they want, which would require an extensive amount of re-education for WordPress users. It is not an ideal scenario.

\n\n\n\nEach block is wrapped as an individual section.\n\n\n\n

Instead of attempting to directly “fix” this issue, WordPress should instead do nothing to the output. Blocks and traditional widgets are fundamentally different.

\n\n\n\n

Let theme authors take the reins on this one and explore possibilities. However, give them the tools to do so, such as supporting block patterns.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 21:35:39 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:15;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: WordCamp Austin 2020 Finds Success with VR Experience for Sessions and Networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106119\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking?utm_source=rss&utm_medium=rss&utm_campaign=wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7246:\"

WordCamp Austin 2020 attendees are raving about their experiences attending the virtual event last Friday. It was no secret that the camp’s organizers planned to use Hubs Virtual Rooms by Mozilla to create a unique environment, but few could imagine how much more interactive and personalized the experience would be than a purely Zoom-based WordCamp.

\n\n\n\n

After selecting a custom avatar, attendees entered the venue using a VR headset or the browser to check out sessions or network in the hallway track.

\n\n\n\n
\n

Here’s a small taste of the experience at @WordCampATX today. #WordPress logos and no sponsor banners on any elevator doors. #WCATX pic.twitter.com/Nv2p2VchXf

— David Bisset (@dimensionmedia) October 9, 2020
\n
\n\n\n\n

Speaker and Q&A sessions were broadcast through Zoom but organizers can also embed YouTube videos and streams within the standalone VR environment.

\n\n\n\n

“The VR experience was the most life-like WordCamp experience I’ve had since the start of global lockdowns,” attendee and speaker David Vogelpohl said. “You could attend sessions in one of two virtual presentation halls depending on what track you wanted to see at that time. The speaker presented on a virtual stage and you could see the other attendees watching the presentation.”

\n\n\n\n

Vogelpohl said he enjoyed his experience getting to know others in the Slack and VR venue. Organizers preserved the general vibe of the “hallway track” to recreate what is arguably one of the most valuable aspects of in-person WordCamps.

\n\n\n\n
\n

So cool – checking out the Virtual Space of WordCamp Austin – love the background noise of people talking, ran into @ChrisWiegman and @Josh412 #WCATX pic.twitter.com/68EdgDN2Om

— Birgit Pauli-Haack (@bph) October 9, 2020
\n
\n\n\n\n

“In the hallway track between the virtual presentation halls was a large foyer where you could meet new people, spot a friend speaking with someone else, and virtually step aside from a group conversation to have a private conversation,” Vogelpohl said.

\n\n\n\n

“It was great to see folks like Josepha circling around speaking with attendees, Josh Pollock nerding out in a corner with a group of advanced WP developers, and having random friends drop into a conversation I was having with a group of others. While VR WordCamp doesn’t wholly replace the value of attending a WordCamp live, a lot of the best parts of meeting and collaborating with others was captured in the VR context.”

\n\n\n\n

The live music interludes, which showcased talents from around the community, also provided a way for virtual attendees to stay connected while waiting for the next session.

\n\n\n\n

Behind the Scenes with Anthony Burchell: Creative Director for WordCamp Austin’s Virtual World

\n\n\n\n

WordPress core contributor Anthony Burchell, who started a company dedicated to creating interactive XR sound and art experiences, was the creative director behind the WordCamp Austin’s VR backdrop.

\n\n\n\n

“For WordCamp Austin we wanted to give folks something to be excited about outside of the typical webcam and chat networking,” Burchell said. “I feel that virtual events are not utilizing the networking layer nearly enough to make folks feel like they are really at an event. I’ve seen many compelling formats for virtual events utilizing webcams and chat rooms, but in the end, it feels like there’s been a missing element of presence; something video games and virtual reality excel at.”

\n\n\n\n
\n

Virtual mission control for #WCATX pic.twitter.com/WyrFkIsW2Q

— Anthony Burchell (@antpb) October 9, 2020
\n
\n\n\n\n

Setting up the virtual world involves spinning up a self-hosted instance of Hubs Cloud, which Burchell said is very similar to the complexity of making a WordPress site.

\n\n\n\n

“The most time consuming part of creating a 3D world for an event is making the 3D assets for the space,” Burchell said. “In total I streamed 11 hours of video leading up to the event to give a glimpse into the process.”

\n\n\n\n

Burchell’s YouTube playlist documents the incredible amount of work that went into creating the WordCamp’s virtual venue for attendees to enjoy.

\n\n\n\n

“While it took quite a bit of time to prepare, the code and assets are completely reusable for another event,” Burchell said. “A lot of the time was spent trying to make the space purpose built for the goals of the camp. Much like a real WordCamp, I found the majority of folks packing into the theater rooms for presentations and dipping out a little early to network with friends in the hallway area. That was very much by design!”

\n\n\n\n

Burchell and the other organizers were careful to ensure that the Hubs space was not the primary viewing experience of the camp but rather an extension of the networking activities that attendees could drop in on. The event had nearly identical numbers of attendees joining the virtual space as it did for those joining the video channels. At the end of the afterparty, Burchell turned on flying for all attendees to conclude the successful event:

\n\n\n\n
\n\n
\n\n\n\n

“With Hubs we were able to give attendees the ability to express themselves within a venue vs within a camera and chat box,” Burchell said. “It was incredible to see characteristics of folks in the community shine through a virtual avatar! Just the simple act of seeing your WordCamp friends in the hallway joking and chatting just as they would at a real life event was enough to make me feel like I was transported to a real WordCamp.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 22:31:02 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:16;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Privacy-Conscious WordPress Plugin Caches and Serves Gravatar Images Locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105825\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally?utm_source=rss&utm_medium=rss&utm_campaign=privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5285:\"

Ari Stathopoulos released his new Local Gravatars plugin last week. The goal of the plugin is to allow site owners to take advantage of the benefits of a global avatar system while mitigating privacy concerns by hosting the images locally.

\n\n\n\n

In essence, it is a caching system that stores the images on the site owner’s server. It is an idea that Peter Shaw proposed in the comments on an earlier Tavern article covering local avatar upload. It is a middle ground that may satisfy some users’ issues with how avatars currently work in WordPress.

\n\n\n\n

“I am one of the people that blocks analytics, uses private sessions when visiting social sites, I use DuckDuckGo instead of Google, and I don’t like the ‘implied’ consents,” said Stathopoulos. “I built the plugin for my own use because I don’t know what Gravatar does, I don’t understand the privacy policies, and I am too lazy to spend two hours analyzing them. It’s faster for me to build something that is safe and doesn’t leave any room for misunderstandings.”

\n\n\n\n

He is referring to Automattic’s extensive Privacy Policy. He said it looks benign. However, he does not like the idea of any company being able to track what sites he visits without explicit consent.

\n\n\n\n

“And when I visit a site that uses Gravatar, some information is exposed to the site that serves them — including my IP,” said Stathopoulos. “Even if it’s just for analytics purposes, I don’t think the company should know that page A on site B got 1,000 visitors today with these IPs from these countries. There is absolutely no reason why any company not related to the page I’m actually visiting should have any kind of information about my visit.”

\n\n\n\n

The Local Gravatars plugin must still connect to the Gravatar service. However, the connection is made on the server rather than the client. Stathopoulos explained that the only information exposed in this case is the server’s IP and nothing from the client, which eliminates any potential privacy concerns.

\n\n\n\n

The Latest Plugin Update

\n\n\n\n

Stathopoulos updated the plugin earlier today to address some performance concerns for pages that have hundreds or more Gravatar images. In the version 1.0.1 update, he added a maximum processing time of five seconds and changed the cache cleanup process from daily to weekly. Both of these are filterable via code.

\n\n\n\n

“Now, if there are Gravatars missing in a page request, it will get as many as it can, and, after five seconds, it will stop,” said Stathopoulos. “So if there are 100 Gravatars missing and it gets the first 20, the rest will be blank (can be filtered to use a fallback URL, or even fall back to the remote URL, though that would defeat the privacy improvement). The next page request will get the next 20, and so on. At some point, all will be there, and there will be no more delays.”

\n\n\n\n

He did point out that performance could temporarily suffer when installing it on a site that has individual posts with 1,000s of comments and a lot of traffic. However, nothing would crash on the site, and the plugin should eventually lead to a performance boost in this scenario. For such large sites, owners could use the existing filter hooks to tweak the settings.

\n\n\n\n

Right now, the plugin is primarily an itch he wanted to scratch for his own purposes. However, if given enough usage and feedback, he may include a settings screen to allow users to control some of the currently-filterable defaults, such as the cleanup timeframe and the maximum process time allowed.

\n\n\n\n

The Growing List of Alternatives

\n\n\n\n

With growing concerns around privacy in the modern world, Local Gravatars is another tool that end-users can employ if they have any concerns around the Gravatar service. For those who are OK with an auto-generated avatar, Pixel Avatars may be a solution.

\n\n\n\n

“I’ve seen some of them, and they are wonderful!” Stathopoulos said of alternatives for serving avatars. “However, this plugin is slightly different in that the avatars the user already has on Gravatar.com are actually used. They can see the image they have uploaded. The user doesn’t need to upload a separate avatar, and an automatic one is not used by default.”

\n\n\n\n

He would not mind using an auto-generated avatar when commenting on blogs or news sites at times. However, Stathopoulos prefers Gravatar for community-oriented sites.

\n\n\n\n

“My Gravatar is part of my online identity, and when I see, for example, a comment from someone on WordPress.org, I know who they are by their Gravatar,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 21:06:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:17;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: WordPress 5.6 to Introduce Application Passwords for REST API Authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105997\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2604:\"

In 2015, WordPress 4.4 introduced a REST API, but one thing that has severely limited its broader use is the lack of authentication capabilities for third-party applications. After considering the benefits and drawbacks of many different types of authentication systems, George Stephanis published a proposal for integrating Application Passwords, into core.

\n\n\n\n

Stephanis highlighted a few of the major benefit that were important factors in the decision to use Application Passwords: the ease of making API requests, ease of revoking credentials, and the ease of requesting API credentials. The project is available as a standalone feature plugin, but Stephanis and his collaborators recommended WordPress merge a pull request that is based off the feature plugin’s codebase.

\n\n\n\n

After WordPress 5.6 core tech lead Helen Hou-Sandi gave the green light for Application Passwords to be merged into core, the developer community responded enthusiastically to the news.

\n\n\n\n

“I am/we are 100% in favor of this,” Joost deValk commented on the proposal. “Opening this up is like opening the dawn of a new era of WordPress based web applications. Suddenly authentication is not something you need to fix when working with the API and you can just build awesome stuff.”

\n\n\n\n

Stephanis’ proposal also mentioned how beneficial a REST API authentication system would be for the Mobile teams‘ contributors who are relying on awkward workarounds while integrating Gutenberg support.

\n\n\n\n

“This would be a first step to replace the use of XMLRPC in the mobile apps and it would allow us to add more features for self hosted users,” Automattic mobile engineer Maxime Biais said.

\n\n\n\n

After the REST API was added to WordPress five years ago, many had the expectation that WordPress-based web applications would start popping up everywhere. Without a reliable authentication system, it wasn’t easy for developers to just get inspired and build something quickly. Application Passwords in WordPress 5.6 will open up a lot of possibilities for those who were previously deterred by the lack of core methods for authenticating third-party access.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 23:01:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:18;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"WPTavern: WP Agency Summit Begins Its Second Annual Virtual Event October 12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105160\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:197:\"https://wptavern.com/wp-agency-summit-begins-its-second-annual-virtual-event-october-12?utm_source=rss&utm_medium=rss&utm_campaign=wp-agency-summit-begins-its-second-annual-virtual-event-october-12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6357:\"

Jan Koch, the founder and host of WP Agency Summit, is kicking off his second annual event on October 12. The five-day event will feature 37 speakers from a wide range of backgrounds across the WordPress industry. It is a free virtual event that anyone can attend.

\n\n\n\n

“The focus for the 2020 WP Agency Summit is showing attendees how to bring back the fun into scaling their agencies,” said Koch. “It is all about reducing the daily hustle by teaching how to successfully build and manage teams, how to work with enterprises (allowing for fewer customers but bigger projects), how to build sustainable recurring revenue, and how to position your agency to dominate your niche.”

\n\n\n\n

This year’s event includes three major changes to make the content more accessible to a larger group of people. Each session will be available between October 12 – 16 instead of the previous 48-hour window that attendees had to find time for in 2019.

\n\n\n\n

After the event has concluded, access to the content will be behind a paywall. Koch reduced the price to $77 for lifetime access for those who purchase pre-launch, which will increase to $127 during the event. Last year’s prices ballooned to $497, which meant that it was simply not affordable for many who found it too late.

\n\n\n\n

Some of the proceeds this year are going toward transcribing all the videos so that hearing-impaired users can enjoy the content.

\n\n\n\n

This year’s event will also focus on a virtual networking lounge for attendees. “I’ve seen how well it worked at the WP FeedBack Summit — we even had BobWP record a podcast episode on the fly in that lounge!” said Koch. “I’ve seen many new friendships develop, people connecting with new suppliers or getting themselves booked on podcasts, and sharing experiences about their businesses.”

\n\n\n\n

The lounge will be open during the entirety of the summit, which will allow attendees to jump into the conversation on their own time.

\n\n\n\n

A More Diverse Speaker Lineup

\n\n\n\n

Koch received some backlash for the lack of gender diversity last year. The 2019 event had over 20 speakers from a diverse male lineup. However, only four women from our industry led sessions.

\n\n\n\n

When asked about this issue in 2019, Koch responded, “I recognize this as a problem with my event. The reason I have so much more male than female speakers is quite simple, the current speaker line-up is purely based on connections I had when I started planning for the event. It was a relatively short amount of time for me, so I wasn’t able to build relationships with more female WP experts beforehand.”

\n\n\n\n

The host said he paid attention to the feedback he received. While not hitting the 50/50 split goal he had for 2020’s event, 16 of the 37 speakers are women.

\n\n\n\n

Koch said he strived to get speakers from a wider range of backgrounds. He wanted to bring in both freelancers and multi-million dollar agency owners. He also focused on getting people from multiple countries to represent WordPress agencies.

\n\n\n\n

“I did reach out to around 130 people four months before the event to make new connections,” he said. “The community around the Big Orange Heart (a non-profit for mental well-being) also helped a lot with introducing me to new members of the WP community.”

\n\n\n\n

Koch said he learned two valuable lessons when branching out beyond his existing connections for this year’s event:

\n\n\n\n

Firstly, don’t hesitate to reach out to people you think will never talk to you because they’re running such big companies. For example, I immediately got confirmations from Mario Peshev from Devrix, Brad Touesnard from Delicious Brains, or Marieke van de Rakt from Yoast. When first messaging them, I had little hope they’d set aside time to jump on an interview with me – but they were super supportive and accommodating! The WordPress community really is a welcoming environment if you approach people in a humble way.

Secondly, build connections with sincerity. Do not just focus on what you can get from that connection but how you can help the other person. I know this sounds cheesy and you’ve heard this quite often — but it is true. Once I got the first response from new contacts and explained my goal of connecting fellow WordPress community members virtually, most immediately agreed because they also benefit from new connections and being positioned as a thought-leader in this event.

\n\n\n\n

WP Agency Summit? WP FeedBack Summit?

\n\n\n\n

For readers who recall the Tavern’s coverage of the WP FeedBack Summit earlier this year, the article specifically stated that the WP FeedBack Summit was a continuation of 2019’s WP Agency Summit. The official word at the time from WP FeedBack’s public relations team was the following:

\n\n\n\n

Last year’s event, the WP Agency Summit has been rebranded under the umbrella of WP FeedBack’s brand when Jan Koch the host of last’s year WP Agency Summit joined WP FeedBack as CTO.

\n\n\n\n

Koch said that it was a standalone event and not directly connected to WP Agency Summit but had the same target audience. However, the WP FeedBack Summit did use the previous WP Agency Summit’s stats and data to promote the event.

\n\n\n\n

“The WP FeedBack Summit was hosted under the WP FeedBack brand because I joined their team as CTO in March this year,” he said. “Vito [Peleg] and I had the idea to host a virtual conference around WordPress because of WordCamp Asia being canceled — we wanted to help connect the community online through our summit.

\n\n\n\n

Koch left WP FeedBack soon after the summit ended and is currently back on his own and has a goal of making WP Agency Summit a yearly event.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 17:01:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:19;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: Navigation Screen Sidelined for WordPress 5.6, Full-Site Editing Edges Closer to Public Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105839\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:247:\"https://wptavern.com/navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta?utm_source=rss&utm_medium=rss&utm_campaign=navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4676:\"

The new block-based navigation screen is once again delayed after it was originally slated for WordPress 5.5 and then put on deck for 5.6. Contributors have confirmed that it will not be landing in WordPress core until 2021 at the earliest.

\n\n\n\n

“The Navigation screen is still in experimental state in the Gutenberg plugin, so it hasn’t had any significant real-world use and testing yet,” Editor Tech Lead Isabel Brison said. She made the call to remove it from the 5.6 lineup after the feature missed the deadline for bringing it out of the experimental state. It still requires a substantial amount of development work and accessibility feedback before moving forward.

\n\n\n\n

Contributors will focus instead on making sure the Widgets screen gets out the door for 5.6 and plan to pick up again on Navigation towards the end of November.

\n\n\n\n

WordPress 5.6 lead Josepha Haden gave an update this week on the progress of all the anticipated features, including the planned public beta for full-site editing (FSE).

\n\n\n\n

“I don’t expect FSE to be feature complete by the time WP5.6 is released,” Haden said. “What I expect is that FSE will be functional for simple, routine user flows, which we can start testing and iterating on. That feedback will also help us more confidently design and build our complex user flows.”

\n\n\n\n

Frank Klein, an engineer at Human Made, asked in the comments of another update why full-site editing is being tied to 5.6 progress in the first place, since it will still only be available in the plugin at the time of release.

\n\n\n\n

“The main value is that it provides a good checkpoint along the path of FSE’s development,” Kjell Reigstad said. “Full-site editing is very much in progress. It is still experimental, but the general approach is coming into view, and becoming clearer with every plugin release.”

\n\n\n\n

Reigstad posted an update on what developers can expect regarding block-based theming and the upcoming release, since the topic is closely tied to full-site editing. He emphasized that the infrastructure is already in place and that, despite it still being experimental, future block-based themes should work in a similar way to how they are working now.

\n\n\n\n

“The focus is now shifting towards polishing the user experience: using the site editor to create templates, using the query block, iterating on the post and site blocks, and implementing the Global Styles UI,” Reigstad said.

\n\n\n\n

“The main takeaway is that when 5.6 is released, the full-site editing feature set will look similar to where it is today, with added polish to the UI, and additional features in the Query block.”

\n\n\n\n

Theme authors are entering a new time of uncertainty and transition, but Reigstad reassured the community that themes as we know them today are not on track to be phased out in the immediate future.

\n\n\n\n

“There is currently no plan to deprecate the way themes are built today,” Reigstad said. “Your existing themes will continue to work as they always have for the foreseeable future.” He also encouraged contributors to get involved in an initiative to help theme authors transition to block-based themes. (This project is not targeted for the 5.6 release.)

\n\n\n\n

Developers can follow important FSE project milestones on GitHub, and subscribe to the weekly Gutenberg + Themes updates to track progress on block-based theming. A block-based version of the Twenty Twenty-One theme is in the works and should pick up steam after 5.6 beta 1, expected on October 20.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 22:57:37 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:20;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"WPTavern: EditorPlus 1.9 Adds Animation Builder for the Block Editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105678\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:181:\"https://wptavern.com/editorplus-1-9-adds-animation-builder-for-the-block-editor?utm_source=rss&utm_medium=rss&utm_campaign=editorplus-1-9-adds-animation-builder-for-the-block-editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4535:\"

Munir Kamal shows no signs of slowing down. He continues to push forward with new features for his EditorPlus plugin, which allows end-users to customize the look of the blocks in their posts and pages. He calls it the “no-code style editor for WordPress.”

\n\n\n\n

The latest addition to his plugin? Animation styles for every core block.

\n\n\n\n

My first thought was that this would bloat the plugin with large amounts of unnecessary CSS and JavaScript for what is essentially a few bells and whistles. However, Kamal pulled it off with minimal custom CSS.

\n\n\n\n

Inspired by features from various website builders, he wanted to bring more and more of those things to the core block editor. The animations feature is just another ticked box on a seemingly never-ending checklist of features. And, so far, it’s all still free.

\n\n\n\n

Since we last covered EditorPlus in June, Kamal has added the ability to insert icons via any rich-text area (e.g., paragraphs, lists, etc.). He has also added shape divider, typography, style copying, and responsive editing options for the core WordPress blocks.

\n\n\n\n

How Do Animations Work?

\n\n\n\n

In the version 1.9 release of EditorPlus, Kamal added “entrance” animations. These types of animations happen when a visitor sees the block for the first time on the screen. For example, users could set the Image block to fade into visibility as a reader views the block.

\n\n\n\n

Currently, the plugin adds seven animations:

\n\n\n\n
  • Fade
  • Slide
  • Bounce
  • Zoom
  • Flip
  • Fold
  • Roll
\n\n\n\nAdding a Slide animation for the Cover block text.\n\n\n\n

Each animation has its own subset of options to control how it behaves on the page. The bounce animation, for example, allows users to select the bounce direction. Other options include duration, delay, speed curve, delay, and repeat. There are enough choices to spend an inordinate amount of time tinkering with the output.

\n\n\n\n

One of the best features of this new feature is that Kamal has included an Animation Player under the block options. By clicking the play button, users can view the animation in action without previewing the post.

\n\n\n\n

Watch a quick video of the Animations feature:

\n\n\n\n
\n\n
\n\n\n\n

After testing and using each animation, everything seemed to work well. The one downside — and this is not limited to animations — is that applying styles on the block level sometimes does not make sense. In many cases, it would help users to have options to style or animate the items within the block, such as the images in the Gallery block. When I broached the subject with Kamal, he was open to the idea of finding a solution to this in the future.

\n\n\n\n

What Is Next for EditorPlus?

\n\n\n\n

At a certain point, too many block options can almost feel like overkill and become unwieldy. EditorPlus does allow users to disable specific features from its settings screen, which can help get rid of some unwanted options. Kamal said he would like to continue making it more modular so that users can use only the features they need.

\n\n\n\n

“What I plan is to have micro-level feature control for this extension so that a user can switch off individual styling panels like, Typography, Background, etc.,” he said. “Even further, I plan to bring these controls based on the user role as well. So an admin can disable these features for the editor, author, etc.”

\n\n\n\n

That may be a bit down the road though. For now, he wants to focus on adding new features that he already has planned.

\n\n\n\n

“I do plan to add more animation features,” said Kamal. “I got too many ideas, such as scroll-controlled animation, hover animation, text animation, Lottie animation, background animation, animated shape dividers, and more. But, having said that, I will be careful adding only those features that don’t affect page performance much.”

\n\n\n\n

Outside of extra styles and animations for existing blocks, he plans to jump on the block-building train in future releases. EditorPlus users could see accordion, toggle, slider, star rating, and other blocks in an upcoming release.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:53:40 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:21;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"Donncha: Hide featured image if it’s in the post\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://odd.blog/?p=89503242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://odd.blog/2020/10/08/hide-featured-image-if-its-in-the-post/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3885:\"

I’ve been running a photoblog at inphotos.org since 2005 on WordPress. (And thanks to writing this I noticed it’s 15 years old today!)

\n\n\n\n
\n\n\n\n

In that time WordPress has changed dramatically. At first I used Flickr to host my images, but after a short time I hosted the images myself. (Good thing too since Flickr limited free user accounts to 1000 images, so I wrote a script to download the Flickr images I used in posts.)

\n\n\n\n
\n\n\n\n

For quite a long time I used the featured image instead of inserting the image into the post content, but then about two years ago I went back to inserting the photo into the post. Unfortunately that meant the photo was shown twice, once as a featured image, and once in the post content.

\n\n\n\n

The last theme I used supported custom post types, one of which was a photo type that displayed the featured image but hid the post content. It was an ok compromise, but not perfect.

\n\n\n\n
\n\n\n\n

Recently I started using Twenty Twenty, but after 15 years I had a mixture of posts with:

\n\n\n\n
  • Featured image with no image in the post.
  • Featured image with the same image in the post.
\n\n\n\n

I knew I needed something more flexible. I wanted to hide the featured image if it also appeared in the post content. I procrastinated and never got around to it until this evening when I discovered it was actually quite easy.

\n\n\n\n\n\n\n\n

Copy the following code into the function.php of your child theme and you’ll be all set! It relies on you having unique filenames for your images. If you don’t then remove the call to basename(), and that may help.

\n\n\n
\nfunction maybe_remove_featured_image( $html ) {\n        if ( $html == \'\' ) {\n                return \'\';\n        }\n        $post = get_post();\n        $post_thumbnail_id = get_post_thumbnail_id( $post );\n        if ( ! $post_thumbnail_id ) {\n                return $html;\n        }\n\n        $image_url = wp_get_attachment_image_src( $post_thumbnail_id );\n        if ( ! $image_url ) {\n                return $html;\n        }\n\n        $image_filename = basename( parse_url( $image_url[0], PHP_URL_PATH ) );\n        if ( strpos( $post->post_content, $image_filename ) ) {\n                return \'\';\n        } else {\n                return $html;\n        }\n}\nadd_filter( \'post_thumbnail_html\', \'maybe_remove_featured_image\' );\n
\n\n\n

The post_thumbnail_html filter acts on the html generated to display the featured image. My code above gets the filename of the featured image, checks if it’s in the current post and if it is returns a blank string. Feedback welcome if you have a better way of doing this!

\n\n\n\n
\n\n\n\n

\n\n

Related Posts

\n

Source

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:43:35 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Donncha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:22;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches Automatic Platform Optimization for WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105641\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-automatic-platform-optimization-for-wordpress?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-automatic-platform-optimization-for-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6128:\"

Just a day after launching its new privacy-first web analytics product last week, Cloudflare announced Automatic Platform Optimization (APO) for WordPress. The new service boasts staggering performance improvements for sites that might otherwise be slowed down by shared hosting, slow database lookups, or sluggish plugins:

\n\n\n\n

Our testing… showed a 72% reduction in Time to First Byte (TTFB), 23% reduction to First Contentful Paint, and 13% reduction in Speed Index for desktop users at the 90th percentile, by serving nearly all of your website’s content from Cloudflare’s network. 

\n\n\n\n

APO uses Cloudflare Workers to cache dynamic content and serve the website from its edge network. In most cases this eliminates origin requests and origin processing time. That means visitors requesting your website will get near instant load times. Cloudflare reports that its testing shows APO delivers consistent load times of under 400ms for HTML Time to First Byte (TTFB).

\n\n\n\n

The effects of using APO are similar to hosting static files on a CDN, but without the need to manage a complicated tech stack. Content creators retain their ability to create dynamic websites without any changes to their workflow for the sake of performance.

\n\n\n\n

Version 3.8 of Cloudflare’s official WordPress plugin was recently updated to include support for APO. It detects when users make changes to their content and purges the content stored on Cloudflare’s edge.

\n\n\n\n

The new service is available to Cloudflare users with a single click of a button. APO is included at no cost for existing Cloudflare customers on the Professional, Business, and Enterprise plans. Users on the Free plan can add it to their sites for $5/month. The service is a flat fee and is not metered.

\n\n\n\n

Cloudflare’s announcement has so far been well-received by WordPress professionals and hosting companies and many have already begun testing it.

\n\n\n\n
\n

So the week after @Cloudflare Birthday Week I try and play with as many of the new products as possible. Today was the WordPress APO on my simple demo site. You can see TTFB dropped from ~350ms to ~75ms! https://t.co/zg976EjrZI pic.twitter.com/KuaHqtHLom

— Matt Bullock (@mibullock) October 6, 2020
\n
\n\n\n\n

WordPress lead developer Mark Jaquith called APO “incredible news for the WordPress world.”

\n\n\n\n

“On sites I manage this is going to lower hosting complexity and easily save hundreds of dollars a month in hosting costs,” Jaquith said.

\n\n\n\n

After running several speed tests from six different locations around the world, early testers at Kinsta got remarkable results using APO:

\n\n\n\n

“By caching static HTML on Cloudflare’s edge network, we saw a 70-300% performance increase. As expected, the testing locations furthest away from Tokyo saw the biggest reduction in load time.

“If your WordPress site uses a traditional CDN that only caches CSS, JS, and images, upgrading to Cloudflare’s WordPress APO is a no-brainer and will help you stay competitive with modern Jamstack and static sites that live on the edge by default.”

\n\n\n\n

George Liu, a “self-confessed page speed addict” and Cloudflare Community MVP, performed a series of detailed tests on the new APO product with his blog. After many comparisons, he found that Cloudoflare’s WordPress plugin with APO turned on delivers results similar to his heavily optimized WordPress blog that uses a custom Cloudflare Worker caching configuration.

\n\n\n\n

“You’ll find that Cloudflare WordPress plugin’s one click Automatic Platform Optimization button does wonders for page speed for the average WordPress user not well versed in page speed optimizations,” Liu said.

\n\n\n\n

“Cloudflare’s WordPress plugin Automatic Platform Optimization will in theory beat all other WordPress caching solutions other than you rolling out your own Cloudflare Worker based caching like I did. So you get a good bang for your buck at US$5/month for Cloudflare’s WordPress plugin APO.”

\n\n\n\n

Liu also warned of some speed bumps with the initial rollout, as Cloudflare’s APO supports a limited set of WordPress cookies for bypassing the Cloudflare CDN cache, leaving certain use cases unsupported. APO does not seem to work on subdomains and users are also reporting that it’s not compatible with other caching plugins. It also disables real visitor IP address detection.

\n\n\n\n

Cloudflare is aware of many of these issues, which have been raised in the comments of the announcement, and is in the process of adding more cookies to the list to bypass caching. Due to some plugin conflicts, APO may not be as plug-and-play as it sounds for some users right now, but the product is very promising and should improve over time with more feedback.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 04:18:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:23;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Kick off Block-Based WordPress Theme Development With the Theme.json Creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105832\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator?utm_source=rss&utm_medium=rss&utm_campaign=kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4674:\"

Gutenberg 9.1 made a backward-incompatible change to its theme.json file (experimental-theme.json while full-site editing is under the experimental flag). This is the configuration file that theme developers will need to create as part of their block-based themes. Staying up to date with such changes can be a challenge for theme authors, but Ari Stathopoulos, a Themes Team representative, wrote a full guide for developers.

\n\n\n\n

Jon Quach, a Principal Designer at Automattic, has also been busy creating a tool to help theme authors transition to block-based themes. He recently built a UI-based project called Theme.json Creator that builds out the JSON code for theme authors. Plus, it is up to date with the most recent changes in the Gutenberg plugin.

\n\n\n\n

Tools like these will be what the development community needs as it gets over the inevitable hump of moving away from the traditional theme development paradigm and into a new era where themes are made almost entirely of blocks and a config file.

\n\n\n\n

While plugin development is becoming more complex with the addition of JavaScript, theme development is taking a sharp turn toward its roots of HTML and CSS. We are barreling toward a future in which far more people will be able to create WordPress themes. Even the possibility of sharing pieces of themes (e.g., template parts and patterns) is on the table. This could not only empower theme designers by lowering the barrier to entry, it could also empower some end-users to make the jump into theme building.

\n\n\n\n

However, the theme.json file is one aspect of future theme authorship that is extremely developer-oriented. JSON is a universal format shared between various programming languages. It is meant to be read by machines and is not quite as human-friendly as other formats. As the theme.json file grows to accommodate more configuration options over time, the less friendly it will become to simply typing keys and values in.

\n\n\n\n

It makes sense to build tools to simplify this part of the theme building process.

\n\n\n\n

That is where the Theme.json Creator tool comes in. Theme authors pick and choose the options they want to support and input custom values. Then, the tool spits out everything in properly-formatted JSON.

\n\n\n\nUsing the Theme.json Creator tool.\n\n\n\n

One big thing the tool does not yet cover is custom CSS variables. This feature is a recent addition to the theme.json specification. It allows theme authors to create any custom property that WordPress will automatically output as CSS. In his announcement post, Stathopoulos covered how to create a typographic scale with custom properties and use those variables for editor features, such as line-height and font-size values.

\n\n\n\n

Currently, Theme.json Creator’s primary focus is on global styles. However, Gutenberg allows theme authors to configure default styles on the block level. For example, theme designers can set the color or typography options for the core Heading block to be different from the default global styles. This provides theme authors with fine-tuned control over every block.

\n\n\n\n

Theme.json Creator does not yet support configuration at this level. However, it would be interesting to see if Quach adds it in the future.

\n\n\n\n

The focus on setting up global styles is a good start for now. This is still an experimental feature. The great thing about it is that it can help theme authors begin to see how one piece of the block-based themes puzzle fits in. It is a starting point for an entirely new method of adding theme support for features when most are accustomed to adding multiple add_theme_support() PHP function calls.

\n\n\n\n

With the direction that theme development seems to be heading, it is easy to imagine that it could evolve into a completely UI-based affair at some point down the line. If templates are made up of blocks and patterns, which anyone can already build with the block editor, and if styles will essentially boil down to a config file, there will be little-to-no programming required to build a basic WordPress theme.

\n\n\n\n

If someone is not already at least jotting down notes for a plugin that allows users to create and package a block-based theme, I would be surprised. For now, Theme.json Creator is removing the need to write code for at least one part of the theme design process.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 07 Oct 2020 20:53:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:24;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: Jetpack 9.0 Introduces Loom Block, Twitter Threads Feature, and Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105743\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4033:\"
\n\n\n\n

Jetpack’s highly anticipated 9.0 release has landed, introducing some of the new features the team has previewed over the past week. Users can now publish WordPress posts to Twitter as threads. This new feature is available as part of the Publicize module when you have connected a Twitter account.

\n\n\n\n

Posting Twitter threads is a feature that only works with the block editor, as it takes advantage of how content is naturally split into chunks (blocks).

\n\n\n\n

In the comments on his demo post, Automattic engineer Gary Pendergast gave a more detailed breakdown of the logic Jetpack uses to ensure full sentences aren’t broken up in the tweets.

\n\n\n\n

“With the mental model now being focused on mapping blocks to tweets, it’s much easier to make logical decisions about how to handle each block,” Pendergast said. “So, a paragraph block is the text of a tweet, if the paragraph is too long for a single tweet, it tries to split the paragraph up by sentences. If a sentence is too long, then it resorts to splitting by words. Then, if there’s an embed/image/video/gallery block following that paragraph, we can attach it to the tweet containing that paragraph. There are additional rules for other blocks, but that’s the basic process. It then just iterates over all of the supported blocks in the post.”

\n\n\n\n

Pendergast published his post as thread to demonstrate the new feature in action. The advantage of posting a thread from your WordPress site is that it doesn’t end up getting lost in Twitter’s fast-moving timeline. Most important Twitter threads evaporate from public consciousness almost as soon as they are published. Publishing threads from your website ensures they are better indexed and easier to reference in the future.

\n\n\n\n

Jetpack Adds Loom Block for Embedding Screen Recordings

\n\n\n\n

Loom was added to Jetpack as a new oEmbed provider three weeks ago. The video recording service allows for recording camera, microphone, and desktop simultaneously. The service is especially popular in educational settings. Jetpack 9.0 introduces a new Loom block for embedding recordings.

\n\n\n\n\n\n\n\n

“Loom is growing in popularity as it is being recommended more and more to assist in distance learning efforts,” Jetpack Director of Innovation Jesse Friedman said. “Now more than ever we want to be able to help those working, learning, and teaching from home. The Loom block was a natural addition to join the other Jetpack video blocks which now include YouTube, TikTok, DailyMotion, and Vimeo.”

\n\n\n\n

Loom’s free tier allows users to record up to 25 videos, but the Pro plan is free for educators. Friedman confirmed that Jetpack does not have any kind of partnership with Loom. The team decided to support the product to assist professionals, educators, and students. Having it available as a block also makes it more convenient for those using P2 for communication.

\n\n\n\n

As anticipated, Jetpack 9.0 also provides a seamless transition necessary to ensure Instagram and Facebook embeds will continue working after Facebook drops unauthenticated oEmbed support on October 24. The Jetpack team reports that it “partnered with Facebook” to make sure these embeds continue to work with the WordPress.com REST API.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 23:28:38 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:25;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:51:\"Post Status: Joost de Valk on WordPress marketshare\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=79914\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:62:\"https://poststatus.com/joost-de-valk-on-wordpress-marketshare/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1193:\"

David Bisset makes his podcast debut for Post Status, as he interviews Joost de Valk, Founder and Chief Product Officer of Yoast, and discusses all things WordPress marketshare related.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Jilt

\n\n\n\n

Jilt offers powerful email marketing built for eCommerce. From newsletters to highly segmented automations, Jilt is your one-stop show for eCommerce email. Join thousands of stores that have already earned tens of millions of dollars in extra sales using Jilt. Try Jilt for free

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 22:28:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:26;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"WPTavern: iThemes Buys WPComplete, Complementing Its Recent Restrict Content Pro Acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105631\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition?utm_source=rss&utm_medium=rss&utm_campaign=ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4395:\"

Just one month after publicly announcing its acquisition of Restrict Content Pro (RCP), iThemes purchased WPComplete for an undisclosed amount. The acquisition is for the product, website, and customers only.

\n\n\n\n

Paul Jarvis and Zack Gilbert created the WPComplete plugin in 2016. However, it has outgrown what the duo could maintain and support alone. After the transition period in which the new owners take over, the two will step away from the project.

\n\n\n\n

In essence, WPComplete is a “course completion” plugin. Site owners can create online courses while allowing students/users to mark their work as completed. It also gives students a way to track their progress through courses, which can often boost the potential for them to finish.

\n\n\n\n

“Paul and Jack believe a key to their success has been their ability to keep their team small and manageable,” wrote Matt Danner, the COO at iThemes, in the announcement. “The growth of WPComplete has presented a number of challenges for a team of two people, so the decision was made to start looking towards alternative ownership solutions that could continue to grow WPComplete and provide it with a stable team. iThemes is a perfect fit.”

\n\n\n\n

iThemes customers who have a Plugin Suite or Toolkit membership will get automatic access to the pro version of the WPComplete plugin. For current WPComplete users, Danner said everything should be “business as usual.” However, iThemes has assigned a few of its team members to work on the product and site, so customers should see some new faces.

\n\n\n\n

RCP and WPComplete are obviously complementary products. RCP is a membership plugin that allows site owners to restrict content based on that membership. WPComplete allows site members to mark lessons or coursework as completed. “We’ll be rolling out a new bundle later this month that combines both RCP and WPComplete for course and membership creators to take advantage of these two plugins,” said AJ Morris, the Product Innovation and Marketing Manager at iThemes.

\n\n\n\n

WPComplete is still a young product. The free version of the plugin currently has 2,000+ active installs and a solid 4.7 rating on WordPress.org. If marketed as an extension of the RCP plugin, it automatically puts it in front of the eyes of 1,000s of more potential customers. It should be much easier to grow the plugin as part of a membership bundle.

\n\n\n\n

iThemes is making some bold moves in the membership space. It will be interesting to see if the company makes any other acquisitions that could strengthen its product line and help it become more dominant. There is still a ton of room for growth in the membership segment of the market. There is also the potential for integrations with other major plugins.

\n\n\n\n

“Adding WPComplete to the iThemes product lineup also allows us to move more quickly on some plans we have for Restrict Content Pro,” said Danner in the initial announcement. He also vaguely mentioned a couple of ideas the team had in the works but did not go into detail.

\n\n\n\n

With a little prodding, Morris provided some insight into what they are planning for the immediate future. The biggest first step is tackling integration with the block editor. Currently, WPComplete uses shortcodes. The team’s next step is likely to begin with creating block equivalents for those shortcodes.

\n\n\n\n

“After that, we’ve touched on a few deeper integrations with Restrict Content Pro, like the possibility to restrict courses to memberships,” said Morris.

\n\n\n\n

The iThemes team does not plan to stop with WPComplete as part of its product lineup. One of the goals is to use the plugin for the iThemes website itself.

\n\n\n\n

“We always try to eat our own dogfood when we can,” said Morris. “You’ll see that with RCP and WPComplete early next year as we look to integrate them into our iThemes Training membership.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 20:59:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:27;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: Exploring Full-Site Editing With the Q WordPress Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105676\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/exploring-full-site-editing-with-the-q-wordpress-theme?utm_source=rss&utm_medium=rss&utm_campaign=exploring-full-site-editing-with-the-q-wordpress-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7492:\"

I have been eagerly awaiting the moment when I could install a theme and truly test Gutenberg’s full-site editing feature. By and large, each time I have tested it over the past few months, the experience has felt utterly broken. This is why I have remained skeptical of seeing the feature land in WordPress 5.6 this December.

\n\n\n\n

The Q theme by Ari Stathopoulos is the first theme that seems to be a decent working example. Whether that is a stroke of luck with timing or that this particular theme is simply built correctly is hard to tell — Stathopoulos is a team rep for the Themes Team. Gutenberg 9.1 dropped last week with continued work toward site editing.

\n\n\n\n

Q is as experimental as it gets. The Themes Team put out an open call for experimental, block-based themes as far back as March this year. However, not many have taken the team up on this offer. If approved, Q stands to be the first block-based theme to go live in the official WordPress directory. It still has to work its way through the standard review process, awaiting its turn in the coming weeks.

\n\n\n\n

On the whole, full-site editing remains a frustrating and confusing experience. I still remain skeptical about its readiness, even in beta form, to show off to the world in WordPress 5.6.

\n\n\n\n

However, Q is an interesting theme to explore at this point for both end-users and theme developers. Users can install it and start tinkering with the site editing screen via the Gutenberg plugin. Developers can learn how global styles, templates, and template parts fit together from a working theme.

\n\n\n\n

Using the Site Editor

\n\n\n\nEditing a single post in the site editor.\n\n\n\n

The Q theme requires the Gutenberg plugin and its full-site editing mode to be enabled. Generally, requiring a plugin is not allowed for themes in the directory. However, experimental Gutenberg themes are allowed to bypass this guideline.

\n\n\n\n

Stathopoulos pointed out that the theme is highly experimental and should not be used on a production site. However, he is hopeful that it will get more eyes focused on full-site editing.

\n\n\n\n

He mentioned that several items are broken, such as category archives not showing the correct posts. This is a current limitation of the Query block in Gutenberg. However, one of the best ways to find and recognize these types of issues is to have a theme that stays up with the pace of development.

\n\n\n\n

Currently, the site editor feels like it is biting off more than it can chew. Not only can users edit the layout and design of the page, but they can also directly edit existing post content — don’t try this at home unless you are willing for your post titles to get switched to the hyphenated slug. Should the site editor be handling the double-duty of design and content editing? If so, should design and content editing be handled in separate locations in the long term or be merged into one feature?

\n\n\n\n

It feels raw. It is not geared toward users at this point.

\n\n\n\n

The bright spot with the site editor is the current progress on template parts in the editor. Template parts are essentially “modules” that handle one part of the page. For example, the typical theme will have a header and footer template part. Currently, end-users can insert custom template parts or switch one template part for another. This opens a world of possibilities, such as users choosing between multiple header designs (template parts) for their sites.

\n\n\n\nSwitching the header template part.\n\n\n\n

The downside to the entire template system is that it seems so divorced from the site editor that it is hard to believe the average user would understand what is going on. Templates and template parts reside under the Appearance menu in the admin. The Site Editor is a separate, top-level menu item. Without any preexisting knowledge of how these pieces work together, it can be confusing.

\n\n\n\n

Template parts worked for me in the site editor from the outset. However, they did not work on the front end at first. I continually received the “template part not found” message for hours. Then, at some point — whether through magic or a random save that pulled everything together — the feature began to output the previously-missing header and footer template parts.

\n\n\n\n

Glimpse Into the Future of Theme Development

\n\n\n\n

The Q theme has a scant few style rules, which it loads directly in the <head> section of the site in lieu of adding an extra stylesheet. It relies on the stock Gutenberg block styles on the front end with a few minor overrides. Most other custom styles are handled via the global styles system, which pulls from the theme’s experimental-theme.json config file (will be theme.json in the future).

\n\n\n\n

It begs the question of whether themes will necessarily need much in the way of CSS when full-site editing lands.

\n\n\n\n

If WordPress allows users to configure most styles via block options and global styles overrides, themes may not need much more than their config files. After that, it would come down to registering custom block styles and patterns.

\n\n\n\n

If this is the future that we are headed toward, anyone could essentially create a WordPress theme. And, those pieces, such as template parts and patterns, could all be shared between any site. In that future, themes may simply not matter anymore.

\n\n\n\n

Last year, Mike Schinkel proposed deprecating the theme system altogether and replacing it with web components.

\n\n\n\n

“Rather than look for a theme that has all the features one needs — which I have found always limits the choices to zero — a site owner could look for the components and modules they need and then assemble their site from those modules,” he said. “They could pick a header, a footer, a home-page hero, a set of article cards, a pricing module, and so on.”

\n\n\n\n

The more I tinker with full-site editing, the more it feels like that is the lane that it will ultimately merge into. Imagine a future where end-users could pick and choose the pieces they wanted and simply have it look right on the front end.

\n\n\n\n

It is exciting to think about that possibility. Both Schinkel and I have more of a background in programming than we do in design. It makes sense from that sort of analytical mindset to put everything into neat, reusable boxes because reuse is a cornerstone of smart programming.

\n\n\n\n

However, I worry about the state of design in such a system with so many replaceable parts. Will designers be able to take holistic approaches to theme development, creating truly intricate pieces of art? Will that system essentially create a web of cookie-cutter sites? Or, will designers simply find ways to think outside the box while within the constraints of the block system?

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 21:21:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:28;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Virtual Jamstack Conf to Feature Fireside Chat with Matt Mullenweg and Matt Biilmann, October 6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105680\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6?utm_source=rss&utm_medium=rss&utm_campaign=virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2618:\"
image credit: Jamstack Conf
\n\n\n\n

The greater Jamstack community is coming together on October 6-7, 2020, for a virtual conference. Organizers expect more than 15,000 attendees from around the globe over a two-day span that includes keynotes, sessions, interactive topic tables, workshops, speaker Q&As, and networking opportunities.

\n\n\n\n

Matt Mullenweg will be joining Netlify CEO Matt Biilmann on day 1 at 12PM PDT for a fireside chat moderated by CSS-Tricks Creator Chris Coyier. The chat will go deeper on recent topics of contention, including developer sentiment, complexity, security, and performance. Coyier also plans to discuss how the Jamstack and WordPress communities intersect through headless implementations of the CMS.

\n\n\n\n

A provocative post from TheNewStack at the end of August quoted Mullenweg as saying that “JAMstack is a regression for the vast majority of the people adopting it.” This sparked multiple heated exchanges across blogs and social media. Biilimann, who originally coined the term “Jamstack,” wrote a response to Mullenweg’s remarks, hailing “the end of the WordPress era.”

\n\n\n\n

Live conversations tend to be more cordial than shots fired across the blogosphere. It will be interesting to see if Biilimann cares to join Stackbit CEO Ohad Eder-Pressman in his wager that Jamstack will become the predominant architecture for the web by 2025. The fireside chat should be recorded, in case you cannot catch the live session. Recordings of talks from the previous virtual Jamstack event held in May are available on YouTube.

\n\n\n\n

Today is the last call for registration. Many of the workshops have already sold out, but tickets to the regular sessions on October 6 are still available. Sign up on the event website to get your free ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 20:12:50 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:29;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Gutenberg 9.1 Adds Patterns Category Dropdown and Reverts Block-Based Widgets in the Customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105629\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5615:\"

Gutenberg 9.1 was released to the public on Wednesday. The team announced over 200 commits from 77 contributors in its release post yesterday. One of the biggest changes to the interface was the addition of a new dropdown selector for block pattern categories. The team also reverted the block-based widgets section in the customizer and added an image size control to the Media & Text block.

\n\n\n\n

One of the main focuses of this release was improving the block-based widgets editor. The feature was taken out of the experimental stage in Gutenberg 8.9 and continues to improve. The widgets screen now uses the same inserter UI as the post-editing screen. However, users can currently only insert regular blocks. Patterns and reusable blocks are still not included.

\n\n\n\n

Theme authors can now control aspects of the block editor via a custom theme.json file. This is part of the ongoing Global Styles project, which will allow theme authors to configure features for their users.

\n\n\n\n

The development team has also added an explicit box-sizing style rule to the Cover and Group blocks. This is to avoid any potential issues with the new padding/spacing options. Theme authors who rely on the block editor styles should test their themes to make sure this change does not break anything.

\n\n\n\n

Better Pattern Organization

\n\n\n\nNew block patterns UI in the inserter.\n\n\n\n

I have been calling for the return of the tabbed pattern categories since Gutenberg 8.0, which was a regression from previous versions. For 11 versions, users have had to scroll and scroll and scroll through every block pattern just to find the one they wanted. The development team has sought to address this issue by using a category dropdown selector. When selecting a specific category, its patterns will appear.

\n\n\n\n

At first, I was unsure about this method over the old tabbed method. However, after some use, it feels like the right direction.

\n\n\n\n

As more and more theme and plugin authors add block pattern categories to users’ sites, the dropdown is a more sensible route. Even tabs could become unwieldy over time. The dropdown better organizes the list of categories and makes the UI cleaner. More than anything, I am enjoying the experience and look forward to this eventually landing in WordPress 5.6 later this year.

\n\n\n\n

Customizer Widgets Reverted

\n\n\n\nReverted widgets panel in the customizer.\n\n\n\n

On the subject of WordPress 5.6, one of its flagship features has been hitting some roadblocks. Block-based widgets are expected to land in core with the December release, but the team just reverted part of the feature. They had to remove the widgets block editor from the customizer they added just two major releases ago.

\n\n\n\n

It was for the best. The customizer’s block-based widgets editor was fundamentally broken. It was not ready for primetime and should have remained in the experimental stage until it was somewhat usable.

\n\n\n\n

“I will approve this since the current state of the customizer in the Gutenberg plugin is broken, and there is no clear path forward about how to fix that,” wrote Andrei Draganescu in the reversion ticket. “With this patch, the normal widgets can still be edited in the customizer and the block ones don’t break it anymore. This is NOT to mean that we won’t proceed with fixing the block editor in the customizer, that is still an ongoing discussion.”

\n\n\n\n

The current state of editing widgets via the customizer is at least workable with this change. If end-users add a block via the admin-side widgets editor, it will merely appear as an uneditable, faux widget named “Block” in the customizer. They will need to edit blocks via the normal widgets screen.

\n\n\n\n

There is no way that WordPress can ship the current solution when 5.6 rolls out. However, we are still two months out. This leaves plenty of time for a fix, but Draganescu’s note that “there is no clear path forward” may make some people a bit uneasy at this stage of development.

\n\n\n\n

Control Image Size for Media & Text

\n\n\n\nImage size dropdown selector for the Media & Text block.\n\n\n\n

One of the bright spots in this update is the addition of an image size control to the Media & Text block. Like the normal Image block, end-users can choose from any registered image size created for their uploaded image.

\n\n\n\n

This is a feature I have been looking forward to in particular. Previously, using the full-sized image often made the page weight a bit heftier than necessary. It is also nice to go along with themes that register sizes for both landscape and portrait orientations, giving users more options.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 20:56:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:30;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"WordPress.org blog: The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8711:\"

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:31;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches New Web Analytics Product Focusing on Privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105446\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-new-web-analytics-product-focusing-on-privacy?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-new-web-analytics-product-focusing-on-privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2448:\"

In pursuit of “democratizing web analytics,” Cloudflare announced it is launching privacy-first analytics as a new standalone product. The company is entering a market that has been dominated by Google Analytics for years but with a major differentiating feature – it will not track individual users by a cookie or IP address to show unique visits.

\n\n\n\n

Cloudflare Web Analytics defines a visit as “a successful page view that has an HTTP referer that doesn’t match the hostname of the request.” It’s not the same as Google’s “unique” metric, and Cloudflare says it may differ from other reporting tools. Weeding out bots from the total traffic numbers is a nascent feature that Cloudflare is improving as part of its Bot Management product.

\n\n\n\n
\n\n\n\n

Cloudflare Web Analytics is launching with features that are largely similar to Google Analytics but with some unique ways of zooming into different traffic segments and time ranges to see where traffic is originating from.

\n\n\n\n

“The most popular analytics services available were built to help ad-supported sites sell more ads,” Cloudflare product manager Jon Levine said. “But, a lot of websites don’t have ads. So if you use those services, you’re giving up the privacy of your users in order to understand how what you’ve put online is performing.

\n\n\n\n

“Cloudflare’s business has never been built around tracking users or selling advertising. We don’t want to know what you do on the Internet — it’s not our business.”

\n\n\n\n

Paying customers on the Pro, Biz, and Enterprise plans can access their analytics from their dashboards immediately. Cloudflare is also offering the product for free as JavaScript-based analytics for users who are not currently customers. Those who want access to the free plan can sign up for the waitlist.

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 04:03:01 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:32;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"WPTavern: Virtual WordPress Page Builder Summit Kicks Off October 5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105570\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:179:\"https://wptavern.com/virtual-wordpress-page-builder-summit-kicks-off-october-5?utm_source=rss&utm_medium=rss&utm_campaign=virtual-wordpress-page-builder-summit-kicks-off-october-5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6348:\"

From October 5 through October 9, the first Page Builder Summit will open its virtual doors to all attendees for free. Nathan Wrigley, the podcaster behind WP Builds, and Anchen le Roux, the founder and lead developer of Simply Digital Design, are hosting the five-day online event that focuses on the vast ecosystem of page builders for WordPress.

\n\n\n\n

The summit will include 35 sessions spread out over the event schedule. Each session will last around 30 minutes, so it will be easy to pop in and watch one in your downtime. Sessions will cover a range of builders, including the default WordPress block editor, Elementor, Beaver Builder, Oxygen, Brizy, and Divi.

\n\n\n\n

“It’s an event specifically for users of WordPress page builders, or those curious about what they can do,” said Wrigley. “I feel like a page builder style interface for creating websites is the future for our industry. WordPress itself is moving in this direction with the block editor (a.k.a. Gutenberg). With that in mind, it seemed like a good idea to create a dedicated event to share knowledge about this side of WordPress. We’ve tried to include presentations from as many page builders as we could.”

\n\n\n\n

Wrigley made sure to point out that it is not all geared toward developers, discussing the inner-workings of builders. Some of the sessions focus on marketing, optimization, and conversion, which provides a wider range of topics for potential attendees.

\n\n\n\n

The summit hosts created an online quiz for those who are unsure about which sessions to watch.

\n\n\n\n

There is a small catch. The sessions will be freely available only from the time they begin and the following 24 hours. After that, accessing the videos will come at a premium. Attendees can gain lifetime access to the PowerPack for $47 if they purchase within 15 minutes of signing up. Then, prices will rise to $97 until the event kicks off on October 5. Beyond, the price jumps to $147. The lifetime access includes access to the presentations, transcripts, a workbook, and other bonuses from the speakers.

\n\n\n\n

For those unsure about forking over the cash, they can still watch the sessions during the 24-hour window.

\n\n\n\n

The proceeds from the event will go out to paying affiliate commissions to speakers and partners. Some of it will go into planning and investing in a second summit down the road.

\n\n\n\n

“Both myself and Nathan have specific charities that we want to donate to after the event,” said le Roux. “It was part of our goals to be able to do this, but we didn’t want to make this an official contribution.”

\n\n\n\n

Why a Page Builder Summit?

\n\n\n\n

Both Wrigley and le Roux have their preferred builders. But, the goal of the summit is to offer a wide look at the tools available and help freelancers and agencies better streamline their businesses and create happier clients.

\n\n\n\n

“I’ve been a user of page builders for many years, but only at the point where they truly showed in the editing interface something that almost perfectly reflected what the end-user would see did I get really immersed,” said Wrigley. “Having come from a background in which I built entire websites from a collection of text files (HTML, CSS, PHP, etc.), I was fascinated that we’d reached a point where the learning curve for building a good website was significantly reduced.”

\n\n\n\n

He pointed out that it is not always so simple though. While the same level of coding skills may not be necessary, people must figure out how to navigate their preferred page builder, which can come with its own learning curve.

\n\n\n\n

“You need to learn their way of doing things and how to achieve your design choices,” he said. “It’s always going to work out better if you know the code, but the WordPress mission of democratizing publishing certainly seems to align quite nicely with the adoption of tools, like page builders, which mean that once-difficult tasks are now easier.”

\n\n\n\n

For le Roux, her interest in hosting the Page Builder Summit falls back to her design studio.

\n\n\n\n

“As a developer, my main reason for switching to page builders was around streamlining and creating more efficient but quality websites in the shortest amount of time,” she said. “Especially now that we focus on day rates, creating the best possible website that clients would love fast would not have been possible without page builders.”

\n\n\n\n

The Hosts’ Go-To Builders

\n\n\n\n

“We prefer using Beaver Builder with Themer at Simply Digital Design,” said le Roux. “We use Gutenberg for blog posts or where possible with custom post types or LMS software. However, we’ve also taken on a few Elementor projects where that’s the client’s preferred option.”

\n\n\n\n

Wrigley uses some of the same tools. His main work is on the WP Builds website where he hosts podcasts.

\n\n\n\n

“I have used Beaver Builder’s Themer to create templates for specific layouts, but for content creation within those layouts I’m using the block editor,” said Wrigley. “My content is mainly text and the WordPress editor is utterly remarkable in this situation. I kept the classic editor installed for a few months after WordPress 5.0 came about, but I soon realized that this was folly and that the editing interface of Gutenberg is superior. The ability to insert and move text, buttons, etc. is such a joy to work with, and the iterations that have been made in the last two years make it, in my opinion, the best text editing experience on the web.”

\n\n\n\n

Wrigley sees a future in which the WordPress block editor takes over much of the work that page builders are currently handling. However, that future is “still over the horizon.”

\n\n\n\n

“I’m excited about this future though, and we’ve got a few crystal ball-gazing presentations; trying to work out what that future might look like,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 20:31:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:33;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"WPTavern: Jetpack 9.0 to Introduce New Feature for Publishing WordPress Posts to Twitter as Threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105448\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3318:\"

Jetpack 9.0, coming on October 6, will debut a new feature that allows users to share blog posts as Twitter threads in multiples tweets. A recent version of Jetpack introduced the ability to import and unroll tweetstorms for publishing inside a post. The 9.0 release will run it back the other way so the content originates in WordPress, yet still reaps all the same benefits of circulation on Twitter as a thread.

\n\n\n\n

The new Twitter threads feature is being added as part of Jetpack’s Publicize module under the Twitter settings. After linking up a Twitter account, the Jetpack sidebar options for Publicize allow users to publish to Twitter as a link to the blog or a set of threaded tweets. It’s not just limited to text content – the threads feature will also upload and attach any images and videos included in the post.

\n\n\n\n\n\n\n\n

When first introduced to the idea of publishing a Twitter thread from WordPress, I wondered if threads might lose their trademark pithy punch, since users aren’t forced to keep each segment to the standard length of a tweet. Would each tweet be separated in an odd, unreadable way? The Jetpack team anticipated this, so the thread option adds more information to the block editor to show where the paragraphs will be split into multiple tweets.

\n\n\n\n

“Threads are wildly underused on Twitter,” Gary Pendergast said in a post introducing the feature. “I think a big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.” The tool Pendergast has been working on for Jetpack gives users the best of both worlds.

\n\n\n\n

In response to a comment requesting Automattic “concentrate on tools to get people off social media,” Pendergast said, “If we’re also able to improve the quality of conversations on social media, I think it’d be remiss of us to not do so.” He also credits IndieWeb discussions on Tweetstorms and POSSE (Publish (on your) Own Site, Syndicate Elsewhere) as inspirations for the feature.

\n\n\n\n

For years, blogging advocates have tried to convince those who post lengthy tweetstorms to switch to a publishing medium that is more suitable to the length of their thoughts. The problem is that Twitter users lose so much of the immediate feedback and momentum that their thoughts would have generated when composed as a tweetstorm.

\n\n\n\n

Instead of lecturing people about how they should really be blogging instead of tweetstorming, Jetpack is taking a fresh approach by enabling full content ownership with effortless social syndication. You can test out the experience for yourself by adding the Jetpack Beta Testers plugin and running the 9.0 RC version on your site.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 02:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:34;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: Ask the Bartender: How To WordPress in a Block World?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105491\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:167:\"https://wptavern.com/ask-the-bartender-how-to-wordpress-in-a-block-world?utm_source=rss&utm_medium=rss&utm_campaign=ask-the-bartender-how-to-wordpress-in-a-block-world\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9755:\"

I love your articles. And now, in the middle of the WordPress revolution, I realized I’m constantly searching for an answer regarding WP these days.

So many things are being said, so many previsions of the future, problems, etc., but, right now, I think I, as a designer, just want to understand one thing that seemed answered already but it’s never clear:

Is WordPress a good choice to build a client’s template where he just has to insert the info that will show in the frontend where I want to? And he doesn’t have to worry about formatting blocks? I love blocks, don’t get me wrong, but will normal templating end?

I just think that having a super CMS, HTML, CSS, and being able to play with a database with ACF is so powerful, that I’m wondering if it’s lost. After so much reading, I still don’t understand if this paradigm is going to disappear.

Right now, I don’t know if it’s best to stop making websites as I used to and adopt block patterns instead.

Ricardo
\n\n\n\n

WordPress is definitely changing. Over the past two years, we have seen much of it reshaped into something different from the previous decade and more. However, this is not new. WordPress has always been a constantly-changing platform. It just feels far too different this time around, almost foreign to many. The platform had to make a leap. Otherwise, it would have started falling behind.

\n\n\n\n

And, it is a big ask of the existing community to come along with it, to take that leap together.

\n\n\n\n

It can be scary as a developer whose livelihood has depended on things working a certain way or who has built tools and systems around pre-block WordPress. Many freelancers and agencies had their world turned upside down with the launch of the block editor. It is perfectly OK to feel a bit lost.

\n\n\n\n

Now, it is time for a little tough love. It has been two years. As a professional, you need to have a plan in place already. Whether that is an educational plan for yourself or a transitional plan for your clients, you should already be tackling projects that leverage the block editor. If you are at a point where you have not been building with blocks, you are now behind. However, you can still catch up and continue advancing in your WordPress career.

\n\n\n\n

There are so many changes coming down the pipeline that anyone who plans to develop for WordPress will be in continual education mode for years to come.

\n\n\n\n

When building for clients, the biggest thing to remember is that it is not about you. It is about getting something into the hands of your clients that addresses their specific needs. Freelancers and agencies need to often be the Jacks and Jills of all trades. Sometimes, this even means having a backup CMS or two that you can use that are not named WordPress. It helps to be well-rounded enough to jump around when needed, especially if you are not at a point in your career where you can demand specific work and pass on jobs that would put food on the table.

\n\n\n\n

It is also easy to look at every job as a nail and WordPress as the hammer. Or, even specific plugins as the tool that will always get the job done. I have seen developers in the past rely on tools like ACF, CMB2, or Meta Box but could not code a custom metadata solution when necessary to save their life. Sometimes a bigger toolbox is necessary.

\n\n\n\n

Every WordPress developer needs a solid, foundational understanding of the languages that WordPress uses. Gone are the days of skating by on HTML, CSS, and PHP knowledge. You need to learn JavaScript deeply. Matt Mullenweg, the co-founder of WordPress, was not joking around when he said this back in 2015. It holds true more and more each day. In another five years, it will tough to be a developer in the WordPress world without knowing JavaScript, at least for backend work.

\n\n\n\n

It also depends on what types of sites you are building. If you are primarily handling front-end design, you will likely be able to get by with a lower skill level. You will just need to know the “WordPress way” of building themes.

\n\n\n\n

Within the next year, you should be able to build just about any theme design with decent CSS and HTML knowledge along with an understanding of how the block system works. Full-site editing and block-based themes will change how we build the front end of the web. It is going to be a challenging transition at first, especially for those of us who are steeped in traditional theme development, but client sites will often be far easier to build. I highly recommend the twice-monthly block-based themes meetings if your focus is on the front end.

\n\n\n\n

Block Templates

\n\n\n\n

Based on your question, I am going to make some assumptions. You have a history of essentially building out meta boxes via ACF where the client just pops in their data. Then, you format that data on the front end. You are likely mixing this with custom post types (CPTs). This is a fairly common scenario.

\n\n\n\n

One of the great things about the block system is that you can lock the post editor for individual CPTs. WordPress already has you covered with its block templates feature, which allows you to define just what a post should look like. You can set up which blocks you want to appear and have the client drop their content in. At the moment, this feature is limited to the post type level. However, it should grow more robust over time, particularly when it works alongside the traditional “page templates” system.

\n\n\n\n

Block templates are a powerful tool in the ol’ toolbox that will come in handy when building client sites.

\n\n\n\n

Block Patterns

\n\n\n\n

You do not have to stop making websites as you are accustomed to at the moment. However, you should start leveraging new block features as they become available and make sense for a specific project. I am a fanatic when it comes to block patterns, so my bias will definitely show.

\n\n\n\n

The biggest thing with block patterns and clients is education. For the uninitiated, you will need to spend some time teaching them how to insert a pattern and how it can be used to their advantage. That is the hurdle you must jump.

\n\n\n\n

For many of the users that I have seen introduced to well-designed patterns, they have fallen in love with the feature. Even many who were reluctant to switch to the block editor became far more comfortable working with it after learning how patterns worked. This is not the case for every user or client, but it has been a good introduction point to the block editor for many.

\n\n\n\n

To answer your question regarding patterns: yes, you should absolutely begin to adopt them.

\n\n\n\n

ACF Is Evolving

\n\n\n\n

Because you are accustomed to ACF, you should be aware that the framework is evolving to keep up with the block editor. Version 5.8.0 introduced a PHP framework for creating custom blocks over a year ago. And, it has been improving ever since. There are even projects like ACF Blocks, which will provide even more tools for your arsenal.

\n\n\n\n

It is important to learn from what some of the larger agencies are doing. Read up on how WebDevStudios is tackling block development. The company also has an open-source block library for ACF.

\n\n\n\n

Solving Problems

\n\n\n\n

Your job as a developer is to be a problem solver. Whatever system you are building with is merely a part of your toolset. You need to be able to solve issues regardless of what tool you are using. At the end of the day, it is just code. If you can learn HTML, you can learn CSS. If you can learn those, you can learn PHP. And, if you can manage PHP, you can certainly pick up JavaScript.

\n\n\n\n

A decade or two from now, you will need to learn something else to stay relevant in your career. Web technology changes. You must change with it. Always consider yourself a student and continue your education. Surround yourself and learn from those who are more advanced than you. Emulate, borrow, and steal good ideas. Use what you have learned to make them great.

\n\n\n\n

There is no answer I can give that will be perfect for every scenario. Each client is unique, and you will need to decide the best direction for each.

\n\n\n\n

However, yes, you should already be on the path to building with a block-first mindset if you plan to continue working with WordPress for the long haul. Immerse yourself in the system. Read, study, and build something any chance you get.

\n\n\n\n

This is the first post in the Ask the Bartender series. Have a question of your own? Shoot it over.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 30 Sep 2020 20:35:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:35;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: Supercharge the Default WordPress Theme With Twentig, a Toolbox for Twenty Twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105344\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:225:\"https://wptavern.com/supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty?utm_source=rss&utm_medium=rss&utm_campaign=supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6455:\"Custom page pattern from the Twentig plugin.\n\n\n\n

I am often on the hunt for those hidden gems when it comes to block-related plugins. I like to see the interesting places that plugin authors venture. That is why it came as a surprise when someone recommended I check out the Twentig plugin a few days ago. Somehow, it has flown under my radar for months. And, it has managed to do this while being one of the more interesting plugins for WordPress I have seen in the past year.

\n\n\n\n

Twentig is a plugin that essentially gives superpowers to the default Twenty Twenty theme. Diane and Yann Collet are the sibling co-founders and brains behind the plugin.

\n\n\n\n

While I have been generally a fan of Twenty Twenty since it was first bundled in core, it was almost a bit of a letdown in some ways. It was supposed to be the theme that truly showcased what the block editor could do — and it does a fine job of styling the default blocks — but there was a lot of potential left on the table. The Twentig plugin turns Twenty Twenty into something worthier of a showcase for the block editor. It is that missing piece, that extra mile in which WordPress should be marching its default themes.

\n\n\n\n

While the new Twenty Twenty-One default theme is just around the corner, Twentig is breathing new life into the past year’s theme. The developers behind the plugin are still fixing bugs and bringing new features users.

\n\n\n\n

Of its 34 reviews on WordPress.org, Twentig has earned a solid five-star rating. That is a nice score for a plugin with only 4,000 active installations. As I said, it has flown under the radar a bit, but the users who have found it have obviously discovered something that adds those extra touches to their sites they need.

\n\n\n\n

What Does Twentig Do?

\n\n\n\n

It is a toolbox for Twenty Twenty. The headline feature is its block editor features, such as custom patterns and page layouts. It also offers a slew of customizer options that allow end-users to put their own design spin on the default theme. However, my interest is primarily in how it extends the block editor.

\n\n\n\n

Let’s get this out of the way up front. Twentig’s one downside is that it adds a significant amount of additional CSS on top of the already-heavy Twenty Twenty and block editor styles. I will blame the current lack of a full design system from WordPress on most of this. Styling for the block editor can easily bloat a stylesheet. Adding an extra 100+ kb per page load might be a blocker for some who would like to try the plugin. Users will need to weigh the trade-offs between the additional features and the added page size.

\n\n\n\n

The thing that makes Twentig special is its extensive patterns and pages library, which offers one-click access to hundreds of layouts specifically catered to the Twenty Twenty theme.

\n\n\n\nInserting one of the hero patterns.\n\n\n\n

It took me a few minutes to figure out how to access the patterns — mainly because I did not read the manual. I expected to find them mixed in with the core patterns inserter. However, the plugin adds a new sidebar panel to the editor, which users can access by clicking the “tw” icon. After seeing the list of options, I can understand why they probably would not fit into WordPress’s limited block and patterns inserter UI.

\n\n\n\n

It would be easier to list what the plugin does not have than to go through each of the custom patterns and pages.

\n\n\n\n

The one thing that truly sets this plugin apart from the dozens of other block-library types of plugins is that there are no hiccups with the design. Almost every similar plugin or tool I have tested has had CSS conflicts with themes because they are trying to be a tool for every user. Twentig specifically targets the Twenty Twenty theme, which means it does not have to worry about whether it looks good with the other thousands of themes out there. It has one job, which is to extend its preferred theme, and it does it with well-designed block output.

\n\n\n\n

The other aspect of this is that it does not introduce new blocks. Every pattern and page layout option uses the core WordPress blocks, which includes everything from hero sections to testimonials to pricing tables to event listings. And more.

\n\n\n\n

Twentig does not stop adding features to the block editor with custom patterns. The useful and sometimes fun bits are on the individual block level, and I have yet to explore everything. I continue to discover new settings each time I open my editor.

\n\n\n\n

Whether it is custom pullquote styles, a photo image frame, or an inner border tweak to the Cover block (shown below), the plugin adds little extras that push what users can do with their content.

\n\n\n\nInner border style for the Cover block.\n\n\n\n

Each block also gets some basic top and bottom margin options, which comes in handy when laying out a page. At this point, I am simply looking forward to discovering features I have yet to find.

\n\n\n\n

Areas Themes Should Explore

\n\n\n\n

One of the things I dislike about many of these features being within the Twentig plugin is that I would like to see them within the Twenty Twenty theme instead. Obviously not every feature belongs in the theme — some features firmly land in plugin territory. The default WordPress themes should also leave some room for plugin authors to explore. But, shipping some of the more prominent patterns and styles with Twenty Twenty would make a more robust experience for the average end-user looking to get the most out of blocks.

\n\n\n\n

Block patterns were not a core WordPress feature when Twenty Twenty landed. However, for the upcoming Twenty Twenty-One theme, which is expected to bundle some unique patterns, the design team should explore what the Twentig plugin has brought to the current default. That is the direction that theme development should be heading, and theme developers can learn a lot by stealing borrowing from this plugin.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 22:00:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:36;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Coming in Jetpack 9.0: Shortcode Embeds Module Updated to Handle Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105381\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2938:\"

Facebook and Instagram are dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers in an upcoming release. After evaluating third-party solutions, WordPress VIP is recommending its partners enable Jetpack’s Shortcode Embeds module. Jetpack will be shipping the update in its 9.0 release, which is anticipated to land prior to the October 24th deadline.

\n\n\n\n

The module is being updated to provide a seamless transition for users who might otherwise be negatively impacted by Facebook’s upcoming API change. WordPress contributors have run some simulations but are not yet sure what will happen to the display for previously embedded content.

\n\n\n\n

“It is possible that they change the contents of the JS file to manipulate cached embeds, perhaps to display a warning that the site is using an old method to embed content or that the request is not properly authenticated,” Jonathan Desrosiers commented on the trac ticket for removing the oEmbed providers.

\n\n\n\n

WordPress.com VIP roughly outlined what users can expect if they do not enable a solution to begin authenticating oEmbeds:

\n\n\n\n

By default, WordPress caches oEmbed contents in post metadata. These embeds will continue to display in previously-published content. If you edit older posts in the Block Editor, regardless of whether you update the post by saving changes, the embeds in the post will no longer be cached and will stop displaying. If you view these older posts using the Classic Editor, so long as the post is not re-saved, the embeds will continue to function and display properly. If you update the post content, the embed will cease functioning unless you have a mitigation installed.

\n\n\n\n

Although WordPress VIP recommends using the Jetpack module as the best solution, self-hosted WordPress users may want to investigate other options if they are not already using Jetpack. oEmbed Plus is a free plugin created specifically for solving the problem of WordPress dropping Facebook and Instagram as oEmbed providers but it is more work to set up and configure. It requires users to register as a Facebook developer and create an app to get API credentials.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 21:18:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:37;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:52:\"WPTavern: W3C Selects Craft CMS for Redesign Project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105265\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:149:\"https://wptavern.com/w3c-selects-craft-cms-for-redesign-project?utm_source=rss&utm_medium=rss&utm_campaign=w3c-selects-craft-cms-for-redesign-project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9407:\"

W3C has selected Craft CMS over Statamic for its redesign project, after dropping WordPress from consideration in an earlier round of elimination:

\n\n\n\n

In the end, our decision mostly came down to available resources. Craft had already committed to reach AA compliance in Craft 4 (it is currently on version 3.5, the release of version 4 is planned for April 2021). They had also arranged for an external agency to provide them with accessibility issues to tackle weekly. In the end, they decided instead to hire an in-house accessibility specialist to perform assessments and assist the development team in adopting accessibility patterns in the long run.

W3C CMS Selection Report
\n\n\n\n

Last week we published a post urging W3C to revisit Gutenberg for a fair shake against the proprietary CMS’s or consider adopting another open source option. During the selection process, Studio 24, the agency contracted for the redesign, cited its extensive experience with WordPress as the reason for not performing any accessibility testing on more recent versions of Gutenberg.

\n\n\n\n

When asked if the team contacted anyone from WordPress’ Accessibility Team during the process or put Gutenberg through the same tests as the proprietary CMS’s, Studio 24 founder Simon Jones confirmed they had not.

\n\n\n\n

“No, we only reached out to the two shortlisted CMS’s” Jones said. “I’m afraid we didn’t have time to do more. We did test GB a few months ago based on editing content – though it wasn’t the only factor in our choice. As an agency we do plan to keep reviewing GB in the future.”

\n\n\n\n

In response to our concerns regarding licensing, Jones penned an update titled “On not choosing WordPress,” which further elaborated on the reasons why the agency was not inclined towards using or evaluating the new editor:

\n\n\n\n

From a business perspective I also believe Gutenberg creates a complexity issue that makes it challenging for use by many agencies who create custom websites for clients; where we have a need to create lots of bespoke blocks and page elements for individual client projects.

The use of React complicates front-end build. We have very talented front-end developers, however, they are not React experts – nor should they need to be. I believe front-end should be built as standards-compliant HTML/CSS with JavaScript used to enrich functionality where necessary and appropriate.

As of yet, we have not found a satisfactory (and profitable) way to build custom Gutenberg blocks for commercial projects. 

\n\n\n\n

The CMS selection report also stated that W3C needs the CMS to be “usable by non-sighted users” by the launch date, since some members of the staff who contribute to the website are non-sighted.

\n\n\n\n

Since the most recent version of WordPress was not tested in comparison with the proprietary CMS’s, it’s unclear how much better they handle accessibility. Ultimately, W3C and Studio 24 were more comfortable moving forward with a proprietary vendor that was able to make certain assurances about the future accessibility of its authoring tool, despite having a smaller pool of contributors.

\n\n\n\n

“[I’m] also deeply curious since the cursory notes on accessibility for both of the reviewed CMSes seem to highlight a ton of issues like ‘Buttons and Checkboxes are built using div elements’ or most inputs lacking clear focus styles,” Gutenberg technical lead Matías Ventura said. “An element like the Calendar for choosing a post date seems entirely inoperable with keyboard on Craft, for example, while WordPress’ has had significant effort and rounds of feedback poured into that element alone to make it fully operable.”

\n\n\n\n

WordPress developer Anthony Burchell commented on how using a relatively new proprietary CMS seemed counter to W3C’s stated goal to select an option on the basis of longevity. Craft CMS’s continued success is contingent upon its business model and the company’s ability to remain profitable.

\n\n\n\n

“FOSS have the same opportunity of direct access to developers,” Burchell said. “I recognize there are many accessibility shortcomings in popular software, but I think it’s more constructive to rally behind and contribute, not use a proprietary CMS that boasts beer budget in their guidelines.”

\n\n\n\n

On the other side of the issue, accessibility advocates took the W3C’s decision as a referendum on Gutenberg’s continued struggles to meet WCAG AA standards. WordPress accessibility specialist Amanda Rush said it was “nice to see the W3C flip tables over this.”

\n\n\n\n

“Gutenberg is not mature software,” accessibility consultant and WordPress contributor Joe Dolson said in a post elaborating on his comments at WPCampus 2020 Online. He emphasized the lack of stability in the project that Studio 24 alluded to when documenting the reasons against using WordPress.

\n\n\n\n

“It is still undergoing rapid changes, and has grand goals to add a full-site editing experience for WordPress that almost guarantees that it will continue to undergo rapid changes for the next few years,” Dolson said. “Why would any organization that is investing a large amount into a site that they presumably hope will last another 10 years want to invest in something this uncertain?”

\n\n\n\n

Dolson also said the accessibility improvements he referenced regarding the audit were only a small part of the whole picture.

\n\n\n\n

“They only encompass issues that existed in the spring of 2019,” he said. “Since then, many features have been added and changed, and those features both resolve issues and have created new ones. The accessibility team is constantly playing catch up to try and provide enough support to improve Gutenberg. And even now, while it is more or less accessible, there are critical features that are not yet implemented. There are entirely new interface patterns introduced on a regular basis that break prior accessibility expectations.”

\n\n\n\n

WordPress is also being used by millions of people who are constantly reporting issues to fuel the software’s continued refinement, which increases the backlog of issues. Unfortunately, Studio 24 did not properly evaluate Gutenberg against the proprietary CMS’s in order to determine if these software projects are in any better shape.

\n\n\n\n

Instead, they decided that Craft CMS’s community was more receptive to collaborating on issues without reaching out to WordPress. Given the W3C’s stated preference for open source software, WordPress, as the only CMS under consideration with an OSD-compliant license, should have received the same accessibility evaluation.

\n\n\n\n

“I can’t make any statements that would be meaningful about the other content management systems under consideration; but if WordPress wants to be taken seriously in environments where accessibility is a legal, ethical, and mission imperative, there’s still a lot of work to be done,” Dolson said.

\n\n\n\n

Studio 24’s evaluation may not have been equitable to the only open source CMS under consideration, but the situation serves to highlight a unique quandary: when using open source software becomes the impractical choice for organizations requiring a high level of accessibility in their authoring tools.

\n\n\n\n

“Studio 24 ultimately determined that working with a CMS to make it better was more possible with a smaller, proprietary vendor than with a large open-source project,” accessibility advocate Brian DeConinck said. “Project leadership would be more receptive, and the smaller community means changes can be made more quickly. That should prompt a lot of soul-searching for…well, everyone. What does that say about the future of open source?”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 04:56:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:38;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"Gary: More than 280 characters\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:25:\"https://pento.net/?p=5405\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://pento.net/2020/09/29/more-than-280-characters/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5187:\"

It’s hard to be nuanced in 280 characters.

\n\n\n\n

The Twitter character limit is a major factor of what can make it so much fun to use: you can read, publish, and interact, in extremely short, digestible chunks. But, it doesn’t fit every topic, ever time. Sometimes you want to talk about complex topics, having honest, thoughtful discussions. In an environment that encourages hot takes, however, it’s often easier to just avoid having those discussions. I can’t blame people for doing that, either: I find myself taking extended breaks from Twitter, as it can easily become overwhelming.

\n\n\n\n

For me, the exception is Twitter threads.

\n\n\n\n

Twitter threads encourage nuance and creativity.

\n\n\n\n

Creative masterpieces like this Choose Your Own Adventure are not just possible, they rely on Twitter threads being the way they are.

\n\n\n\n
\n

Being Beyoncé’s assistant for the day: DONT GET FIRED THREAD pic.twitter.com/26ix05Hkhp

— green chyna (@CORNYASSBITCH) June 23, 2019
\n
\n\n\n\n

Publishing a short essay about your experiences in your job can bring attention to inequality.

\n\n\n\n
\n

DOWNTOWN BROOKLYN: I\'m working arraignments tonight, representing poor New Yorkers who were arrested yesterday on Thanksgiving.

It was the coldest Thanksgiving in more than a century. Tonight\'s also bitterly cold, even in the courtroom. I\'m wearing my scarf & coat.

— Rebecca Kavanagh (@DrRJKavanagh) November 24, 2018
\n
\n\n\n\n

And Tumblr screenshot threads are always fun to read, even when they take a turn for the epic (over 4000 tweets in this thread, and it isn’t slowing down!)

\n\n\n\n
\n

Tumblr textposts thread, probably?

— we are a family forged in bureaucracy (@ex_aItiora) August 26, 2019
\n
\n\n\n\n

Everyone can think of threads that they’ve loved reading.

\n\n\n\n

My point is, threads are wildly underused on Twitter. I think I big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.

\n\n\n\n

To help make this easier, I’ve been working on a tool that will help you publish an entire post to Twitter from your WordPress site, as a thread. It takes care of transforming your post into Twitter-friendly content, you can just… write. \"?\"

\n\n\n\n

It doesn’t just handle the tweet embeds from earlier in the thread: it handles handle uploading and attaching any images and videos you’ve included in your post.

\n\n\n\n\n\n\n\n

All sorts of embeds work, too. \"?\"

\n\n\n\n
\n
\n
\n\n\n\n

It’ll be coming in Jetpack 9.0 (due out October 6), but you can try it now in the latest Jetpack Beta! Check it out and tell me what you think. \"?\"

\n\n\n\n

This might not fix all of Twitter’s problems, but I hope it’ll help you enjoy reading and writing on Twitter a little more. \"?\"

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 02:33:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Gary\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:39;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Themes Team Releases a Web Fonts Loader, Likely To Prohibit Hotlinking Any Off-Site Assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105363\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets?utm_source=rss&utm_medium=rss&utm_campaign=themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5815:\"

Last Friday, the WordPress Themes Team announced the release of its new Webfonts Loader project. It is a drop-in script that allows theme authors to load web fonts from the user’s site instead of a third-party CDN. The secondary message included in the team’s announcement is that it no longer plans to allow themes to hotlink Google Fonts in the future.

\n\n\n\n

Throughout most of the team’s history, it has not allowed themes to hotlink or use CDNs for hosting theme assets, such as CSS, JavaScript, and fonts. The one exception to this rule was the use of Google Fonts. This allowed themes to have richer typography options at their disposal from what the team has generally declared a reliable source.

\n\n\n\n

“The exception was made because there was no practical way to not have the exception at the time,” said Aria Stathopoulos, a Themes Team representative and developer behind the Webfonts Loader project. “The exception for Google Fonts was made out of necessity. Now that there is another way, the exception will not be necessary.”

\n\n\n\n

In effect, disallowing the Google Fonts CDN would not be a new ban. It would be a removal of an exception to the existing ban.

\n\n\n\n

Google Fonts has become so embedded into the theme developer toolset over the years, there was no way the team could simply pull the plug and prohibit the use of the CDN overnight. If the Themes Team members wanted to focus more on privacy, they would need to build a tool that made it dead simple for theme authors to use.

\n\n\n\n

There is no hard deadline for when the team will remove the exception for Google Fonts, and it is not set in stone at this point. Stathopoulos said removing it has been the goal from the beginning, disallowing all CDNs. However, it took a while to find an efficient way to handle this. With a viable alternative in place, they can discuss moving forward.

\n\n\n\n

Webfonts Loader for Themes

\n\n\n\n

The Webfonts Loader project keeps it simple for theme authors. It introduces a new wptt_get_webfont_styles() function that developers can plug in a stylesheet URL. Once a page is loaded with that function call, it will download the fonts locally to a /fonts folder in the user’s /wp-content directory. This way, fonts will always be served from the user’s site.

\n\n\n\n

The system is not limited to Google Fonts either. Any URL that serves CSS with an @font-face {} rule will work. It does not currently include authentication for CDNs that require API keys, such as Adobe Fonts. However, that is something the team might add in the future.

\n\n\n\n

“For end-users, moving away from CDNs and locally hosting web fonts will improve performance (fewer handshake roundtrips for SSL), and is the privacy-conscious choice,” said Stathopoulos. “The only ‘valid privacy concern’ is that the web fonts’ CDN does not disclose information that is fundamental to the GDPR: what information gets logged, for how long these logs remain, how they are processed, if there is any cross-referencing with all the other wealth of information the company has from users, etc. The concern is a lack of disclosure and information. If a site owner doesn’t know what kind of information a third-party logs for its visitors, then they should ethically not enforce that on their visitors. With this package, the CDN is removed from the equation and the font still gets served fast — if not faster.”

\n\n\n\n

A Path to Core WordPress

\n\n\n\n

Today, there is now a broader focus on privacy concerns related to third-party resources, particularly with tech giants like Google. Such concerns extend to whether third parties are tracking users or collecting data. Additional concerns are around whether sites are disclosing the use of third-party resources, which may be required in some jurisdictions. Site owners who are often unable to work through the web of potential issues are stuck in the middle.

\n\n\n\n

Jono Alderson opened a ticket to create an API for loading web fonts locally in core WordPress in February 2019. It is a lengthy and detailed proposal, but it has yet to see much buy-in outside of a handful of developers.

\n\n\n\n

“If such a script is standardized and included in WordPress core, one of the main benefits would be more respect for the end-user’s privacy,” said Stathopoulos. “In the end, that’s all privacy is about: respecting users.”

\n\n\n\n

A standard API like Alderson proposes could solve some issues. Namely, it would virtually eliminate any privacy concerns. However, loading fonts locally could allow WordPress to optimize font loading and would create a shared system where plugins and themes do not load duplicate assets because of the current limitations of the enqueuing system. A standard API would also put the responsibility of efficiently loading fonts on WordPress’s shoulders instead of theme and plugin developers.

\n\n\n\n

The Themes Team’s new project is a solid start and strengthens the current proposal.

\n\n\n\n

“If we’re serious about WordPress becoming a fast, privacy-friendly platform, we can’t rely on theme developers to add and manage fonts without providing a framework to support them,” wrote Alderson in the ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 28 Sep 2020 20:58:48 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:40;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: Fuxia Scholz First to Pass 100K Reputation Points on WordPress Stack Exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105282\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange?utm_source=rss&utm_medium=rss&utm_campaign=fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5096:\"

Fuxia Scholz, a prolific WordPress Stack Exchange (WPSE) contributor, is the first member to reach 100,000 reputation points. The popular Q&A community site rewards expert advice by floating the highest quality answers to the top, allowing users to earn reputation points. The gamified help community has proven to be more motivating for developers than many traditional forums, since the upvotes communicate how useful their answers are to others.

\n\n\n\n
\n\n\n\n

Scholz started on Stack Overflow a few months before WordPress had its own site. She wrote around 50 answers and made connections with other WordPress developers ahead of the site’s beta phase in June 2010. Once the site graduated and got its own logo and design, Scholz started writing more.

\n\n\n\n

“One core idea for all Stack Exchange sites is gamification: You earn reputation, and you get access to certain privileges,” Scholz said.

\n\n\n\n

“You can say I got a bit addicted. My favorite questions were about problems for which I didn’t know the answer, and couldn’t find one with a search engine, because no one else had solved that before. I used my answers to teach myself, and I learned a lot this way! In May 2011 my reputation on WPSE was already higher than on Stack Overflow, and for the next years it went up in a steep curve.” Ten years after WPSE launched, Scholz has become the first to reach 100,000 reputation points.

\n\n\n\n

“What reputation and karma do is send a message that this is a community with norms, it’s not just a place to type words onto the internet. (That would be 4chan.)” Stack Overflow co-creator Joel Spolsky said. “We don’t really exist for the purpose of letting you exercise your freedom of speech. You can get your freedom of speech somewhere else. Our goal is to get the best answers to questions. All the voting makes it clear that we have standards, that some posts are better than others, and that the community itself has some norms about what’s good and bad that they express through the vote.”

\n\n\n\n

The reputation points were originally inspired by Reddit Karma. Spolsky admits that the points not a perfect system but they do tend to “drive a tremendous amount of good behavior.” Gamification can shape and encourage certain behaviors but Spolsky said it’s a weak force that cannot motivate people to do things they are not already interested in doing. For Scholz, it was the community aspect and an earned sense of ownership and responsibility that kept her hooked.

\n\n\n\n

“In 2012, the community elected me as a moderator, and that changed a lot,” she said. “Now it wasn’t just a game anymore, it was a duty. I felt responsible for the site. I still do. I also found some friends on there. We met at WordCamps and in private, and worked together on different projects.”

\n\n\n\n

Scholz no longer works in development and said she doesn’t care about WordPress anymore, but she is still a regular contributor on the WPSE.

\n\n\n\n

“I switched careers and work as a writer, translator, and community manager for Chess24.com now,” she said. “But I still care about the site WordPress Stack Exchange! I keep an eye on new tags, handle flagged posts and comments, try to make every new user feel welcome, and I search for people who are abusing the system — vote fraud and spam. And, very rarely, I even write an answer, because I still know all this stuff.

\n\n\n\n

“Checking the site has become a part of my daily routine, like feeding the cat.”

\n\n\n\n

This daily habit has snowballed into Scholz racking up more than 2,000 answers. She is getting upvotes on many of her old answers nearly every day, which is what pushed her over the 100k milestone.

\n\n\n\n

“There is a lot to say about the way our site developed over the years,” Scholz said. “I’m not happy about some things. The enthusiasm of the early days is gone. We don’t have enough regulars, there is no discussion about the site on WordPress Development Meta Stack Exchange, and our chat, once very active, funny, and friendly, is now almost dead.

\n\n\n\n

“Maybe that’s normal, I don’t know. But it’s still ‘my’ site. Reputation and badges don’t really mean anything for a long time now, but keeping the site working, useful and friendly is more important.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 26 Sep 2020 15:27:03 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:41;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:82:\"WPTavern: PhotoPress Plugin Seeks to Revolutionize Photography for WordPress Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=104770\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:209:\"https://wptavern.com/photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users?utm_source=rss&utm_medium=rss&utm_campaign=photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5638:\"

Peter Adams, the owner of the PhotoPress plugin, announced a couple of weeks ago that now is the time for his project to take center stage. “It’s Time for PhotoPress,” read the title of his post in which he laid out a four-phase plan for the future of his project.

\n\n\n\n

Adams is no stranger to manipulating WordPress to suit the needs of photographers. He described photography as his first love and second career. He initially found the art of taking photos in high school and set off to college to become a professional photographer in the early ’90s.

\n\n\n\n

As his university graduation loomed, he was recruited to run web development for an internet ad agency that built websites for Netscape, Bill Clinton’s White House, and dozens of Fortune 500 companies. He spent the next 15 years starting or running tech companies before returning to his roots as a photographer.

\n\n\n\n

Today, he photographs for various magazines and companies. And, that’s where his PhotoPress project comes in.

\n\n\n\n

“As far as WordPress has come, it is at risk of losing an entire generation of photographers to photo website services such as Photoshelter, SmugMug, Squarespace, and PhotoFolio,” he said. Adams wants to change that, making WordPress the go-to platform for photographers around the world.

\n\n\n\n

The Jetpack of Photography Plugins

\n\n\n\n

If you dig into the history of the PhotoPress plugin on WordPress.org, it seems to have a 15-year history. However, this is not the same plugin that was published a decade and a half ago by a different developer. The original plugin is now defunct, and Adams took over when the name was freed up on the directory.

\n\n\n\n

Adams wrote in his announcement post that WordPress has done a great job of delivering several media features over the years. “Yet despite that, there are still many rough edges and missing features that keep WordPress from being the first choice for a photographer that needs to publish a beautiful portfolio of their work, put their image catalog/archive online, or showcase a photo editorial/project.”

\n\n\n\n

He outlined a list of 10 specific problem areas that he wants to address in a “Jetpack-like” plugin for photographers. This is the bread and butter of the first of the planned four phases, which he said is about 80% finished. He had originally planned to develop PhotoPress as a series of separate plugins, each addressing a specific problem. Now, it is a single plugin with modules than can be enabled or disabled.

\n\n\n\n

When asked why the “right time” is now, Adams explained it is because the Gutenberg (block editor) project is a giant leap forward in usability in terms of creating photography blogs.

\n\n\n\nPhotoPress Gallery block in the editor.\n\n\n\n

“Photogs are a rare breed of non-technical users with high design sense,” he said. “Things that I used to have to teach photographers to do using shortcode syntax and custom CSS can now be simple controls with live feedback inside a Gutenberg block. It’s really a game-changer for getting people comfortable with customizing things like gallery styling — which is the number one thing photographers need to do.”

\n\n\n\n

The primary piece of the PhotoPress plugin is its custom PhotoPress Gallery block. It allows users to choose between a range of gallery styles, such as columns, masonry, justified, and mosaic. Each style has its own options. Images can also be launched into a slideshow when one is clicked.

\n\n\n\n

Based on some quick tests, the block’s front-end output will go farther with some themes than others. This is mainly because of conflicting CSS and issues which can be solved by testing against more themes.

\n\n\n\n

Aside from the block, the plugin can automatically extract image metadata and group that data through custom taxonomies, such as cameras, lenses, locations, keywords, and more. WordPress stores this information out of the box, but it is hidden away as post meta. The plugin uses the taxonomy system to make it manageable for end-users.

\n\n\n\n

Ultimately, Adams set out to create a photography plugin that fits in with the WordPress admin user interface and experience, which he has accomplished.

\n\n\n\n

The Future of PhotoPress

\n\n\n\n

The project is still a work in progress. Adams is still moving through Phase I of the four-phase plan. Once it is complete, he can move on to the next steps in the process.

\n\n\n\n

Phase II is to create themes that are designed specifically to work with the PhotoPress plugin. He has three planned thus far. One for handling portfolio sites. Another for creating a stock photo archive. And the last for photojournalism and exhibits. Each will be built on top of his photography theme framework.

\n\n\n\n

The themes in Phase II will likely be commercial products. Adams said he needs a way to fund the next phases of the project. He hopes to have this step underway by the end of the year.

\n\n\n\n

For 2021, he wants to begin tackling Phases III and IV. The former will be a website-as-a-service (WaaS) similar to WordPress.com but for photographers. It will begin as a paid project but could have some free options for emerging photographers and students. The final phase is to build an onboarding system.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 25 Sep 2020 19:08:15 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:42;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"WPTavern: Google Officially Releases Its Web Stories for WordPress Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105227\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:191:\"https://wptavern.com/google-officially-releases-its-web-stories-for-wordpress-plugin?utm_source=rss&utm_medium=rss&utm_campaign=google-officially-releases-its-web-stories-for-wordpress-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5593:\"Web Stories for WordPress dashboard.\n\n\n\n

Two and a half months after the launch of its public beta, Google released its Web Stories for WordPress plugin. So far, the plugin has over 10,000 active installations and has garnered a solid five-star rating from four reviews.

\n\n\n\n

Google created the Web Stories format through its AMP Project to allow publishers to create visually-rich stories. It is primarily geared toward mobile site visitors, allowing them to quickly jump through story pages with small chunks of content.

\n\n\n\n

The Web Stories plugin creates a visual interface within WordPress for creating Stories. It breaks away from the traditional WordPress interface and introduces users to an almost Photoshop-like experience for building out individual Stories. The Stories editor is completely drag-and-drop.

\n\n\n\n

The plugin also offers eight predesigned templates out of the box that cover a small range of niches. However, according to Google’s announcement, the company plans to add more templates in future updates.

\n\n\n\n

Web Stories Are for Storytelling

\n\n\n\n

“Firstly…the power of Stories,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Stories are how we (humans) see the world and share our experiences. Up to now the platforms that we have to tell stories have been limited to books/films/tv/websites/blogs/instagram stories etc.”

\n\n\n\n

“Websites are ok for telling stories but in many ways the format doesn’t really fit the linear arc of storytelling. When Marshall McLuhan said ‘the medium is the message’ in 1964 he was talking about how the medium itself has a social impact, and change the communication itself…and the possibilities for what is communicated and how it is perceived. But we should keep coming back to Stories. Stories are the key here imo. Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Marsland finished his thread by saying that using Stories as a replacement for a brochure or website is a missed opportunity. He said that it was a platform for storytelling and should be used as such.

\n\n\n\n

It is far too early to tell if Web Stories will simply be a fad or still in wide use years from now. The technology certainly lends itself well to telling stories, particularly in mobile format, but I doubt we have seen the best of what is possible on the web. The format feels too limited to be the end-all-be-all of storytelling. It is merely one medium that will live and die by its popularity with users.

\n\n\n\n

With the right design skills, some people will craft beautiful Web Stories. And, that is just what Marsland has done with the first Story he shared:

\n\n\n\nPage from the Wilson and Pootle Web Story by Jamie Marsland.\n\n\n\n

I agree with his conclusion. Web Stories should be about storytelling. When you move outside of that zone, the technology feels out of place.

\n\n\n\n

Where I disagree is that websites are not ideal for storytelling. Ultimately, the WordPress block editor will allow artistic end-users to craft intricate stories, mixing content and design in ways that we have not seen. We are just now scratching the surface. I expect our community of developers to build more intricate tools than what the Web Stories plugin currently allows, and we can do so in a way that revolutionizes storytelling on the web.

\n\n\n\n

New Features

\n\n\n\nStory editor with Unsplash photo integration.\n\n\n\n

The Web Stories plugin now adds support for Unsplash images and Coverr videos out of the box. The plugin adds a new tab with a “media” icon. For users of the first beta version of the plugin, this may be a bit confusing. The previous media icon was for a tab that displayed the user’s media. Now, the user’s media is under the tab with the “upload” icon.

\n\n\n\n

It is also not immediately clear that the Unsplash images and Coverr videos are not hosted on the site itself. There is a “powered by” notice at the bottom of the tab, but it can be easy to miss because it blends in with the media in the background.

\n\n\n\n

Media from Unsplash and Coverr is hosted off-site and not downloaded to the user’s WordPress media library. I could find no mention of this in the plugin’s documentation. Such hotlinking was a cause for debate over the recent official release of the Unsplash plugin.

\n\n\n\n

Google also announced it planned to add more “stock media integrations” in the near future. According to a document shared via a GitHub ticket, such future integrations may include Google Photos and GIF-sharing site Tenor.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 21:13:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:43;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"WPTavern: W3C Drops WordPress from Consideration for Redesign, Narrows CMS Shortlist to Statamic and Craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105108\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft?utm_source=rss&utm_medium=rss&utm_campaign=w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:11563:\"

The World Wide Web Consortium (W3C), the international standards organization for the web, is redesigning its website and will soon be selecting a new CMS. Although WordPress is already used to manage W3C’s blog and news sections of the website, the organization is open to adopting a new CMS to meet its list of preferences and requirements.

\n\n\n\n

Studio 24, the digital agency selected for the redesign project, narrowed their consideration to three CMS candidates:

\n\n\n\n
  1. Statamic
  2. Craft CMS
  3. WordPress
\n\n\n\n

Studio 24 was aiming to finalize their recommendations in July but found that none of them complied with the W3C’s authoring tool accessibility guidelines. The CMS’s that were better at compliance with the guidelines were not as well suited to the other project requirements.

\n\n\n\n

In the most recent project update posted to the site, Studio 24 reported they have shortlisted two CMS platforms. Coralie Mercier, Head of Marketing and Communications at W3C, confirmed that these include Statamic and Craft CMS.

\n\n\n\n

WordPress was not submitted to the same review process as the Studio 24 team claims to have extensive experience working with it. In the summary of their concerns, Studio 24 cited Gutenberg, accessibility issues, and the fact that the Classic Editor plugin will stop being officially maintained on December 31st, 2021:

\n\n\n\n

First of all, we have concerns about the longevity of WordPress as we use it. WordPress released a new version of their editor in 2018: Gutenberg. We have already rejected the use of Gutenberg in the context of this project due to accessibility issues.

If we choose to do away with Gutenberg now, we cannot go back to it at a later date. This would amount to starting from scratch with the whole CMS setup and theming.

Gutenberg is the future of WordPress. The WordPress core development team keeps pushing it forward and wants to roll it out to all areas of the content management system (navigation, sidebar, options etc.) as opposed to limiting its use to the main content editor as is currently the case.

This means that if we want to use WordPress long term, we will need to circumvent Gutenberg and keep circumventing it for a long time and in more areas of the CMS as time goes by. 

\n\n\n\n

Another major factor in the decision to remove WordPress from consideration was that they found “no elegant solution to content localization and translation.”

\n\n\n\n

Studio 24 also expressed concerns that tools like ACF, Fewbricks, and other plugins might not being maintained for the Classic Editor experience “in the context of a widespread adoption of Gutenberg by users and developers.”

\n\n\n\n

“More generally, we think this push to expand Gutenberg is an indication of WordPress focusing on the requirements of their non-technical user base as opposed to their audience of web developers building custom solutions for their clients.”

\n\n\n\n

It seems that the digital agency W3C selected for the project is less optimistic about the future of Gutenberg and may not have reviewed recent improvements to the overall editing experience since 2018, including those related to accessibility.

\n\n\n\n

Accessibility consultant and WordPress contributor Joe Dolson recently gave an update on Gutenberg accessibility audit at WPCampus 2020 Online. He reported that while there are still challenges remaining, many issues raised in the audit have been addressed across the whole interface and 2/3 of them have been solved. “Overall accessibility of Gutenberg is vastly improved today over what it was at release,” Dolson said.

\n\n\n\n

Unfortunately, Studio 24 didn’t put WordPress through the same content creation and accessibility tests that it used for Statamic and Craft CMS. This may be because they had already planned to use a Classic Editor implementation and didn’t see the necessity of putting Gutenberg through the paces.

\n\n\n\n

These tests involved creating pages with “flexible components” which they referred to as “blocks of layout,” for things like titles, WYSIWYG text input, and videos. It also involved creating a template for news items where all the content input by the user would be displayed (without formatting).

\n\n\n\n

Gutenberg would lend itself well to these uses cases but was not formally tested with the other candidates, due to the team citing their “extensive experience” with WordPress. I would like to see the W3C team revisit Gutenberg for a fair shake against the proprietary CMS’s.

\n\n\n\n

W3C Is Prioritizing Accessibility Over Its Open Source Licensing Preferences

\n\n\n\n

The document outlining the CMS requirements for the project states that “W3C has a strong preference for an open-source license for the CMS platform” as well as “a CMS that is long-lived and easy to maintain.” This preference may be due to the economic benefits of using a stable, widely adopted CMS, or it may be inspired by the undeniable symbiosis between open source and open standards.

\n\n\n\n

“The industry has learned by experience that the only software-related standards to fully achieve [their] goals are those which not only permit but encourage open source implementations. Open source implementations are a quality and honesty check for any open standard that might be implemented in software…”

Open Source Initiative
\n\n\n\n

WordPress is the only one of the three original candidates to be distributed under an OSD-compliant license. (CMS code available on GitHub isn’t the same.)

\n\n\n\n

Using proprietary software to publish the open standards that underpin the web isn’t a good look. While proprietary software makers are certainly capable of implementing open standards, regardless of licensing, there are a myriad of benefits for open standards in the context of open source usage:

\n\n\n\n

“The community of participants working with OSS may promote open debate resulting in an increased recognition of the benefits of various solutions and such debate may accelerate the adoption of solutions that are popular among the OSS participants. These characteristics of OSS support evolution of robust solutions are often a significant boost to the market adoption of open standards, in addition to the customer-driven incentives for interoperability and open standards.”

International Journal of Software Engineering & Applications
\n\n\n\n

Although both Craft CMS and Statamic have their code bases available on GitHub, they share similarly restrictive licensing models. The Craft CMS contributing document states:

\n\n\n\n

Craft isn’t FOSS
Let’s get one thing out of the way: Craft CMS is proprietary software. Everything in this repo, including community-contributed code, is the property of Pixel & Tonic.

That comes with some limitations on what you can do with the code:

– You can’t change anything related to licensing, purchasing, edition/feature-targeting, or anything else that could mess with our alcohol budget.
– You can’t publicly maintain a long-term fork of Craft. There is only One True Craft.

\n\n\n\n

Statamic’s contributing docs have similar restrictions:

\n\n\n\n

Statamic is not Free Open Source Software. It is proprietary. Everything in this and our other repos on Github — including community-contributed code — is the property of Wilderborn. For that reason there are a few limitations on how you can use the code:

\n\n\n\n

Projects with this kind of restrictive licensing often fail to attract much contribution or adoption, because the freedoms are not clear.

\n\n\n\n

In a GitHub issue requesting Craft CMS go open source, Craft founder and CEO Brandon Kelly said, “Craft isn’t closed source – all the source code is right here on GitHub,” and claims the license is relatively unrestrictive as far as proprietary software goes, that contributing functions in a similar way to FOSS projects. This rationale is not convincing enough for some developers commenting on the thread.

\n\n\n\n

“I am a little hesitant to recommend Craft with a custom open source license,” Frank Anderson said. “Even if this was a MIT+ license that added the license and payment, much like React used to have. I am hesitant because the standard open source licenses have been tested.”

\n\n\n\n

When asked about the licensing concerns of Studio 24 narrowing its candidates to two proprietary software options, Coralie Mercier told me, “we are prioritizing accessibility.” A recent project update also reports that both CMS suppliers W3C is reviewing “have engaged positively with authoring tool accessibility needs and have made progress in this area.”

\n\n\n\n

Even if you have cooperative teams at proprietary CMS’s that are working on accessibility improvements as the result of this high profile client, it cannot compare to the massive community of contributors that OSD-compliant licensing enables.

\n\n\n\n

It’s unfortunate that the state of open source CMS accessibility has forced the organization to narrow its selections to proprietary software options for its first redesign in more than a decade.

\n\n\n\n

Open standards go hand in hand with open source. There is a mutually beneficial connection between the two that has caused the web to flourish. I don’t see using a proprietary CMS as an extension of W3C values, and it’s not clear how much more benefit to accessibility the proprietary options offer in comparison. W3C may be neutral on licensing debates, but in the spirit of openness, I think the organization should adopt an open source CMS, even if it is not WordPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 20:13:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:44;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:79:\"WPTavern: First Look at Twenty Twenty-One, WordPress’s Upcoming Default Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105166\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme?utm_source=rss&utm_medium=rss&utm_campaign=first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6907:\"

Fashion is ephemeral. Art is eternal. Indeed what is a fashion really? A fashion is merely a form of ugliness so absolutely unbearable that we have to alter it every six months!

\n\n\n\n

Thus wrote Oscar Wilde on Victorian-era fashion in an article titled “The Philosophy of Dress” for the New-York Tribune in 1885.

\n\n\n\n

In many ways, WordPress theming is the same as the ever-changing landscape of fashion. Rounded corners are in one day and out the next. Box shadows are in one year after being frowned up just months earlier. Perhaps web design is so intolerable that we must change it every six months. Or, at least freshen it up every year in the case of WordPress.

\n\n\n\n

If art is eternal, there are only two default, Twenty* themes that I can truly recall from past years: Twenty Ten and Twenty Fourteen — yes, Twenty Twenty is memorable, but it is also still the current default. Twenty Ten was a classic that paid homage to WordPress’s past. Twenty Fourteen was such a leap away from tradition that it is hard to forget. Everything else has seemed to fade to varying degrees.

\n\n\n\n

With WordPress 5.6 and the end of the year looming, it is time to look forward to the latest trend. As Mel Choyce-Dwan noted in the announcement of Twenty Twenty-One, the next default theme, “Pastels and muted colors are pretty in right now.”

\n\n\n\n

She is not wrong. The colors are a refreshing change of pace. Now that we are into the second day of autumn, I am getting the good kind of vibes from some of the more earthy-tones from a couple of the color palettes expected to ship with the theme.

\n\n\n\nPotential color palette options for Twenty Twenty-One.\n\n\n\n

Whether Twenty Twenty-One will be a fashionable theme for the year or art that we can remember a decade from now, only history will be able to judge. For now, let’s enjoy the creation and take a look at what we should expect from the next default WordPress theme.

\n\n\n\n

The Current Twenty Twenty-One

\n\n\n\n

The new default theme is a fork of Automattic’s Seedlet, a project in which I lauded as the next step in the evolution of theming. It is a theme that is focused on WordPress’s future of being completely comprised of blocks. It gives us an ideal insight into where theme development is heading. It makes sense as the foundation for the new default. Few other themes would make for a good starting point right now. With WordPress theme development in flux, Seedlet is simply ahead of the pack in terms of foundational elements.

\n\n\n\nSeedlet WordPress theme screenshot.\n\n\n\n

“This provides us with a thorough system of nested CSS variables to make child theming easier, and to help integrate with the global styles functionality that’s under development for full-site editing,” wrote Choyce-Dwan of using Seedlet as a starting point.

\n\n\n\n

There are no plans to spin up a Google Web Font for this theme. The design team is going native and sticking with the default system font stack. Choyce-Dwan listed several reasons for the choice, such as keeping a neutral font that allows broad use, speed, and customizability via a child theme.

\n\n\n\n

Despite the neutral font, the default pastel green is a fairly opinionated design decision. It will not be used broadly across industries. However, the team plans to create multiple color palettes that will give it more range. Presumably, these palettes can also be overwritten.

\n\n\n\nPastel green color scheme on single post view.\n\n\n\n

Other than the colors, the design is relatively simple. Choyce-Dwan said that the theme’s block patterns support is where it will be truly unique.

\n\n\n\n

I was initially unhappy with the patterns that were going to ship with WordPress 5.5. However, an 11th-hour update improved the situation so that they did not feel entirely experimental. The foundational Seedlet theme for Twenty Twenty-One has some unique patterns that begin to scratch the surface of what’s possible with this WordPress feature. My hope is that the new default theme steps it up a notch.

\n\n\n\n

Currently, the theme does not register any custom patterns. However, it has a placeholder file and a note that they are a work in progress. Choyce-Dwan shared some patterns the team has already designed in the announcement.

\n\n\n\nCurrently-designed block patterns.\n\n\n\n

“We’ll be relying on our talented community designers for more ideas,” she wrote. The team has also created a GitHub template for anyone to contribute pattern design ideas.

\n\n\n\n

Currently, the theme does not support the upcoming full-site editing feature of WordPress. After the Beta 1 release of WordPress 5.6, the team plans to begin exploring the addition of this support. WordPress is expected to ship a public beta of full-site editing in its next major release, but it is unclear whether it will be far enough along to be a headline feature for the Twenty Twenty-One theme.

\n\n\n\n

The team and volunteers have less than a month before the October 20th deadline for committing the new theme to trunk, the core WordPress development branch. At that stage, the theme should be nearly complete and ready for production. Of course, there will be several rounds of patches, bug fixes, and updates before WordPress 5.6 lands in December. Right now is the best time for anyone who wants to get involved with Twenty Twenty-One to do so.

\n\n\n\n

Useful links with more information:

\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 20:01:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:45;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:37:\"HeroPress: Hello World – Hevo Nyika\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=3308\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:176:\"https://heropress.com/essays/hello-world-discovering-the-world-through-wordpress/#utm_source=rss&utm_medium=rss&utm_campaign=hello-world-discovering-the-world-through-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14438:\"\"Pull

Unokwanisa kuverenga rondedzero iyi muChiShona

\n

So I chose a career in Web Development!!

\n

To be honest it’s kind of funny when I think about it and quite surreal to be here talking about my story. It has been a journey and I would like to share my story with you.

\n

I have been lucky in the Dad department. My Dad encouraged me to work hard and dream big from a very young age. I remember occasionally having ‘when I grow up’ talks.

\n

For quite some time I wanted to be a Judge, however awesome this dream sounds it was not very inspired. After binge-watching Judge Judy for a whole weekend, I started calling myself Judge Thelma. Though I don’t remember much of this my sister says that I used to say I would arrest all the men in the World if I ever became a Judge. HAHAHA! (clearly I didn’t understand how the World works)

\n

I did not understand what being a Judge meant or what was required for me to start banging that gavel to my heart’s desire. Eventually, I learnt that I had to become a lawyer first then magistrate before I could be nominated to be a Judge and let us just say that is how I sentenced that dream to a lifetime down the drain.

\n

See what I did there? hahaha!

\nWith Daddy Dearest\n

A few years later, I was in High School and that is when I decided to pursue a career in Computer Science. I did not know what I would be doing or how I would get there but I just knew that I was going to pursue a career in ICT. I wrote my first line of code when I was 16 years old.

\n

This was after I had joined the school’s computer class, initially, I thought I would be learning about Excel Sheets and Word Documents until I was assigned to write my first program in C (talk about a double-take!!). It was not easy but it was very exciting, l remember writing up simple code for a Video Club – a simple check-in/out for VHS tapes and CDs. Dear World, thus began my fascination with computers.

\n

Seven years later, I was now in university studying ICT as I had always wanted. I was doing a Bachelors in Business Management & Information Technology. In my third year, I was interning at a local Webdesign and hosting company. This was never my plan, I only took on that job after I had failed to get a job with local banks or telecommunications companies. Before I was introduced to Website Design I envisioned myself suiting up and working in IT Audit or offering IT support. Even though things did not go as I had planned, I am glad they did not exactly go my way in that aspect. So in 2017, I was designing websites using HTML, CSS, PHP, JavaScripts and Joomla which was the prefered content management system at that company. I knew about WordPress but I was not using it for anything. People have this misconception that WordPress is not for real developers and it is not secure and at that time I was one of those people.

\n

Finding my tribe

\n

One day when I was working at the front desk Thabo Tswana came to give a colleague of mine a purple WooCommerce pen. I did not know what WooCommerce was at that time but I was taken by the purple shirt and pen he was carrying. I asked him about it and he explained what WooCommerce was and that what he was carrying was called ‘swag’. So the love of freebies led me to the WordCamp Harare website, instead of buying a ticket I applied to volunteer. I learnt more about WordPress, I was a volunteer, without any knowledge on WordPress.org or WordPress.com. I only started using WordPress because of the awesome people that l had met at that Wordcamp.

\n

Everyone was so welcoming, a week later with help from Thabo I designed my first ever WP website.

\n

Soon after I was part of the community and a bit more involved in the meetups. We had our first-ever Women Who WordPress meetup in 2018. So many ladies came on board bloggers and developers alike. We were free to talk and discuss a lot of things. We had more time to discuss the difference between WordPress.com and WordPress.org we shared views on how to handle discrimination at work, how to promote your website and a whole lot of other things.

\n

\n

Establishing roots

\n

In 2018, Harare had its first-ever female Lead Organiser Tapiwanashe Manhobo whoop whoop! I was also part of the organising team that year, I was assigned to handle Harare’s first Kids Camp. The planning process was stressful because the economic crisis in Zimbabwe was getting worse, luckily we had over 8 months to plan and with help from sponsors, we managed to pull through. In the end, everything turned out great. I wrote an article about the Kids Camp here.

\n

After the first Kids Camp, we had several WordPressors that were enthusiasts about encouraging kids to embrace ICT. In 2019 we had not planned to have a Kids Camp because of financial constraints but to our surprise, we had some anonymous donations and we managed to have a WordPress Community outreach to a youth centre a week after our WordCamp. We had the outreach at the Centre for Total Transformation which is a non-formal school that caters for underprivileged and vulnerable children. We taught them about WordPress, Computer Hardware and Software.

\n

Here is a small video I took with Ellen when we were about to leave. Did l mention that I am terrible on camera? hahaha!

\n\n

Kids Camp 2019 – Centre for Total Transformation

\n

I have fallen deeply for WordPress because of the Community, I enjoy attending WordCamps, meeting new people and just learning new stuff. I have a huge list of WordCamps I need to attend before l kick the bucket, hopefully. Last year I managed to cross WordCamp

\n

Johannesburg off my bucket list. This year I was going to attend WordCamp Capetown but unfortunately, 2020 had other plans for the whole world. Anyway when everything is back to normal my plan to travel to WordCamps will proceed. (fingers crossed)

\n

Reaping Fruits

\n

Meanwhile, my plan to improve my developing skills has not been on hold. Even though I can still cook up code in C and Java, for now, I have also included WordPress PHP functions to the mix. It was not easy to get to this point, daring myself got me to this slightly better stage. My IQ is not way up there, however, I try to do my best where I can and I am happy to say it has paid off so far.

\n

Around November last year, I was designing as a freelancer while job hunting. Out of the blue l got a call for a job offer from Trust Nhokovenzo who is big on Digital marketing and also part of the WordPress Community. He had asked someone in the community about developers and my name happened to come up. So since February, I have been part of his team at Calmlock Digital Marketing Agency.

\n

There is so much more in the world of WordPress that l am yet to tap into so even though I am ending my write up here, for now, my story is going to continue …

\n

Until next time…

\n

Hevo Nyika

\n

Saka ini ndakasarudza kugadzira mawebhusayiti.

\n

Ndakaita rombo rakanaka pana baba vandakapihwa naMwari. Baba vangu vaindikurudzira kuti ndishande nesimba. Ndinoyeuka pano neapo tichiita hurukuro dzedu dzekuti ‘kana ndakura ndoda kuveyi’.

\n

Kwenguva yakati rebei ndaida kuve Mutongi. Kunyangwe ini ndisingazvirangariri mukoma wangu anotaura kuti ndaiti ndaizosunga varume vese vari pasi rino kana ndikangoita mutongi HAHAHA zveshuwa handaiziva kuti mitemo yenyika inofambiswa seyi.
\nNdanga ndisinga nzwisisi kuti kuva mutongi kwairevei kana zvaidikanwa kwandiri kuti nditange kurova iro ghavheu kuchishuwo chemoyo wangu. Pakupedzisira, ndakadzidza kuti ndaifanirwa kuzoita gweta ipapo magistrate ndisati ndasarudzwa kuita Mutongi naizvozvo ndokupera kwakaita chiroto chekuva Mutongi.

\nNa Baba Vangu\n

Gare gare papfura makore mashoma pandakanga ndave kuHigh School ndakanga ndakuda kuita basa rema kombiyuta. Ndakanyora mutsara wekutanga wekodhi pandaive nemakore gumi nematanhatu. Izvi zvakaitika mushure mekunge ndapinda mukirasi yemakombiyuta, pakutanga ndaifunga kuti ndinenge ndichidzidza nezveExcel Sheets neWord zvisineyi ndakaona ndakunyora kodhi yangu yekutanga muC. Zvaisave nyore kunyora kodhi asi zvainakidza kwazvo, ndorangarira ndichinyora kodhi yeVhidhiyo Kirabhu.

\n

Makore manomwe apfura, ndakanga ndava kuyunivhesiti ndichidzidza ICT zvandakagara ndakaronga. Ndaiita Bachelors muBusiness Management & Information Technology. Mugore rangu rechitatu ndainge ndave kushanda kune imwe kambani yaita zvekugadzira mawebhusaiti. Ndakawana basa iri mushure mekunge ndatadza kuwana basa kumabhanga. Kunyangwe hazvo zvinhu zvisina kuenda sezvandaive ndakaronga, ndinofara kuti hazvina kunyatso enda nenzira yangu. Saka muna 2017 ndaigadzira mawebhusaiti ndichishandisa HTML, CSS, PHP, JavaScript uye Joomla iyo yaive iyo inokurudzirwa kukambani kwandaive. Panguva iyi ndaiziva nezve WordPress asi ndakanga ndisingaishandisi.

\n

Kuwanana neWordPress

\n

Rimwe zuva pandakanga ndichishanda ndakaona Thabo Tswana akauya kuzopa mumwe mukomana wandayishanda naye chinyoreso cheWooCommerce. Ndakanga ndisingazive kuti WooCommerce yaive chii asi ndakafarira chinyoreso nehembe ye WooCommerce yaanga akapfeka. Ndakamubvunza nezvazvo akatsanangura kuti WooCommerce yaive chii. Saka nekudawo zvakanaka, zvemahara ndakaenda pawebhusaiti yeWordCamp Harare ndikabata zvimbo zvegore iroro. Ndakazvipira kubatsirawo vamwe vekuWordPress kuWordCamp Harare. Nerubatsiro kubva kunaThabo ndakagadzira webhusaiti yangu yekutanga yeWordPress vhiki rakatevera .

\n

Mushure mekunge ndaitawo chipato cheavo vanoshandisa WordPress ndakanga ndakuenda kumisangano yeWordPress yaitwa muHarare. Takaita musangano wevakadzi chete muna 2018. Vakadzi vazhinji vakauya kumusangano uyu. Tainga takasununguka kukurukura zvinhu zvakawanda. Takakurukura pamusoro pemutsauko uripo pakati peWordPress.com neWordPress.org takagovana maonero ekugadzirisa rusarura kubasa nezvimwewo.

\n

\n

Nguva yandakatanga kushandisa WordPress

\n

Muna 2018, kurongwa kweWordCamp Harare kwakatungamirwa kekutanga nemusikana ainzi Tapiwanashe Manhobo (waiva mufaro mukuru). Ndakanga ndiri mumwe wevairongawo naye. Hurongwa hwekuronga WordCamp Harare mugore iri hwainetsa pamusaka pekuoma kwehupfumi wemuZimbabwe, zvisineyi takaita rombo rakanaka nokuti takawana rubatsiro kubva kunevamwewo vanhu vakatiwedzera mari. Pakupedzisira, zvese zvakabudirira zvakanaka. Takarongawo WordCamp yevana varipasi pemakore gumi nechishanu, munokwanisa kuverenga pamusoro pezuva iri pawebhisaiti yangu apa.

\n

Mushure mekuita WordCamp yevana, takave nevamwe vanhu veWordPress aifarira kukurudzira vana kuti vagamuchire ICT. Muna 2019 takanga tisina kuronga kuve neWordCamo yeVana nekuda kwezvimhingamupinyi zvemari asi chakatishamisa ndechekuti takawana mari kubvawo kune vamwe. Takaita Camp iyi paCentre for Total Transformation chinova chikoro chisiri chepamutemo chinodzidzisa vana vanotambura. Tadzidzisa vana ava pamusoro peWordPress, Computer Hardware uye Software.

\n\n

Ndofarira WordPress zvakanyanya nekuda kweavo varimu nharaunda yacho, ini ndinonakidzwa nekuenda kumaWordCampi, kusangana nevanhu vatsva uye kungo dzidza zvinhu zvitsva. Gore rakapera ndakakwanisa kuyambuka muganhu weZimbabwe ndichienda kuWordCamp Johannesburg, dai pasina kuti 2020 nyika dzepasi rino dzakawirwa nedenda reCOVID 19 zvimwe ndingadayi ndakaenda kuWordCamp Capetown. Zvisinei hazvo kana denda ranani zvimwe ndichakwanisa kufamba ndichienda kumaWordCamp edzimwe nyika.

\n

Kukowa zvandakadyara

\n

Zvichakadaro, chirongwa changu chekuvandudza hunyanzvi hwangu hachina kumira. Kunyangwe ini ndichiri kukwanisa kubika kodhi muC uye Java, ikozvino, ndasanganisirawo WordPress PHP. Zvaive zvisiri nyore kusvika apa, zvakatora kuzvishingisa nekushanda nesimba. Ndinofara mwari aiva neni pamufambo wangu uyu.

\n

Muna Mbudzi gore rakapera, ndaive ndichigadzira mawebhusayiti apo nditsvaga basa. Pasina nguva ndakataura naTrust Nhokovenzo uyo akaandipa basa mukambani yake, kambani iyi inonzi Calmlock Digital Marketing Agency.

\n

Pane zvimwe zvakawanda kuWordPress zvandisati ndapinda mazviri. Nhaizvozvo kunyangwe ndiri kupedzisa kunyora kwangu apa, nyaya yehupenyu wangu ichaenderera mberi…

\n

Kusvikira nguva inotevera …

\n

…. tsvaga chinangwa chako, chiite mushe mushe ..

\n

The post Hello World – Hevo Nyika appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 06:00:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Thelma Mutete\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:46;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: WordPress Contributors Debate Dashboard Notice for Upcoming Facebook oEmbed Provider Removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105132\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5885:\"

WordPress contributors are discussing different strategies for responding to Facebook and Instagram dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers. When a user attempts to embed content by pasting a URL as they have in the past, they may not understand why it no longer works. They may assume that WordPress broke embeds, causing an increase in the support burden for this change.

\n\n\n\n

A few participants on the trac ticket for this issue have suggested WordPress detect users who will be impacted and attempt to warn them with a notice.

\n\n\n\n

“Since this may impact users unknowingly, it is possible to push a dashboard notice to users who have Facebook/Instagram embeds in their content, showing for site admins, as a one-off that can be dismissed,” Marius Jensen said.

\n\n\n\n

“We’ve previously done post-update-processing to clean up comments, so the idea of looking over content for an embed isn’t completely outlandish, and would help with those who don’t follow WordPress’ usual channels to learn of this.”

\n\n\n\n

Others don’t see the necessity. “Why should we make exception here?” Milan Dinić said. “It’s not the first time oEmbed support was discontinued for a provider, and I don’t remember anything specific was done then.”

\n\n\n\n

There is still some uncertainty about what will happen with existing oEmbeds after Facebook updates its API. During a recent core developer meeting, Helen Helen Hou-Sandí confirmed that WordPress does not clear oEmbed caches regularly. “Technically oEmbed caches are cleared if you save and a valid response is returned, we do not do cron-based garbage collection,” Hou-Sandí said.

\n\n\n\n

In a post today on the core development blog, Jake Spurlock assured users and developers that the existing embeds added before Facebook’s API change should still work:

\n\n\n\n

Because oEmbed responses are cached in the database using the hidden oembed_cache post type, any embed added prior to the October 24th deadline will be preserved past the deprecation date. These posts are not purged by default in WordPress Core, so the contents of the embed will persist unless manually deleted.

\n\n\n\n

Marius Jensen cautioned that there is still the possibility that existing embeds may not work going, depending on what Facebook does.

\n\n\n\n

“We don’t know how they plan on implementing the use of unauthorized embed attempts,” Jensen said. “It could not return an embed code and your link would remain a plain link, or maybe they decide to return some kind of embedded ‘unauthorized’ content. I don’t think anyone has heard any specifics on how Facebook plans on doing this, so we’re all just kinda waiting to either hear more, or see what happens.”

\n\n\n\n

Jensen said WordPress doesn’t re-check the cached results except when something changes with the post, but there may be plugins that clean up temporary data that may create an unpredictable outcome.

\n\n\n\n

“The reliability of the caches are hard to determine (and being caches, it’s sort of in the term that it’s not guaranteed to always be there, but rather fetched and saved for a while when needed),” Jensen said.

\n\n\n\n

Ideally WordPress’ oEmbed caches will prevent millions of embeds from breaking, but it’s still unknown how Facebook and third party plugins could change things.

\n\n\n\n

Coming off a rocky 5.5 core update that deprecated jQuery Migrate and flooded official support forums with reports of broken sites, some contributors are wary of having another situation where users are left in the dark.

\n\n\n\n

“I think a dashboard notice is desirable,” Jon Brown said. “Otherwise we’re not preemptively warning people in a way they can prepare and transition to another solution. We’re letting them know the same instant it’s going to break (when editing a specific post). I don’t think we can safely assume cached data is going to persist forever either, plenty of routines out there purge transient data before its stated expiration date.

\n\n\n\n

“I see this as potentially being similar to the problems seen in dropping JQM. It’ll cause avoidable and silent breakage client side without even any error logging for a site developer to pick up on. In hindsight, what ideally would have happened with JQM would have been incorporating the detection code from Enable jQuery Migrate Helper into core temporarily, or simply installing that plugin automatically on behalf of users.”

\n\n\n\n

Brown suggested WordPress detect calls to the cached embeds and warn users before the calls have the chance to fail so they can consider enabling a plugin to keep their embeds working more reliably.

\n\n\n\n

The discussion remains open in the make.wordpress.org/core post and the corresponding trac ticket. Spurlock said WordPress will likely remove Facebook and Instagram oEmbed providers in the upcoming 5.6 release (scheduled for December 8) but it could also be shipped in a 5.x minor release that happens after October 24.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 04:28:56 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:47;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Gutenberg Hub Launches Landing Page Templates Directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105009\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:175:\"https://wptavern.com/gutenberg-hub-launches-landing-page-templates-directory?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-hub-launches-landing-page-templates-directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7657:\"\n\n\n\n

Munir Kamal has created copy-and-paste blocks. He has built sections or “patterns” from those blocks. He has created a plugin that allows users to completely customize the two features via block options. Yesterday, he released an initial offering of 22 landing page templates that build upon his earlier work.

\n\n\n\n

Gutenberg Hub can almost be called his magnum opus, at least at this stage of his career. It is a continually growing library of free tools for WordPress’s block editor.

\n\n\n\n

Like previous projects, Gutenberg Hub’s landing templates require the EditorPlus plugin. This plugin is essentially a suite of design controls for the core WordPress blocks. The templates make use of these options by default. Given the limitations of the block editor’s current design controls, the use of such a plugin is necessary. Otherwise, there would be few other ways to realistically create a template system like this.

\n\n\n\n

Currently, users must copy the block code — via a convenient “copy” button — from the Gutenberg Hub website and then paste it in the editor. It is not an ideal situation, and I have been asking Kamal whether he would consider building a template inserter for months now.

\n\n\n\n

This time around, he preemptively said, “And, by the way, I am already working on adding a Template Inserter in my EditorPlus plugin. That will allow users to browse and insert these templates directly from Gutenberg without leaving the website.”

\n\n\n\n

He knew the question was coming. No need for me to ask again. He was unable to share a current screenshot of what the inserter looks like, but he is asking for feedback on what people expect of the user experience and interface.

\n\n\n\n

“Earlier, I created a template inserter similar to other blocks plugins, but later I changed my mind and thought that I should integrate with the Gutenberg Patterns API and load the templates into the ‘patterns’ panel in the block inserter,” he said. “But, I am having a few issues and thinking about going back to the original idea to have a Templates button on the top toolbar that opens a popup window to browse and filter templates that users can insert on a click.”

\n\n\n\n

For now, it is still early. However, at least it is on the long-term roadmap and being worked on.

\n\n\n\n

The Landing Page Templates

\n\n\n\nTesting the photography template (with minor adjustments).\n\n\n\n

At the moment, Gutenberg Hub offers 22 landing page templates. The “page” terminology may not mean “full page.” It simply depends on the active theme. Some themes have an open-canvas type of template that allows users to create the entire page via the editor. However, that is not a common feature, so these page templates will be confined to the post content area in most cases.

\n\n\n\n

The templates also work better with themes that have at least a full-width or no-sidebar option. End-users will want a lot of breathing room to use the templates and tinker with their designs.

\n\n\n\n

Kamal has built templates that stretch across a variety of industries. From restaurants to gyms to education to fashion, there is a lot to choose from right now. He promises more are on the way and at least a 23rd template in the next few days.

\n\n\n\n

“For the niches, I did some research from the top WordPress and HTML marketplaces and found the following most common or popular niches,” he said. “I think I will stick with these niches unless I get some more recommendations.”

\n\n\n\n

In comparison, Redux Templates offers access to over 1,000 sections and templates. Of course, there are trade-offs, such as some of those being commercial and the plugin typically requiring other third-party plugins. While quantity is not the only thing to look at, it proves there are miles of landscape that Gutenberg Hub’s templates have not yet explored. But, it is merely the beginning.

\n\n\n\n

Gutenberg Hub’s full-page templates are not quite as plug-and-play as its blocks and section templates. This is not so much a fault from the developer’s end. It is an issue of the platform, which is constantly being updated, and the range of support from current themes. End-users will start seeing some of the current limitations of the system when a layout does not quite look right with one theme but does with another. Or, if their theme has not been updated to support a new feature, such as the Social Links block, the typical horizontal menu design will likely be a normal vertical list of links instead.

\n\n\n\n

These are not insurmountable issues. Gutenberg and themes need more time to mature before projects like Gutenberg Hub’s landing templates are perfect or at least as close to perfect as can be expected.

\n\n\n\n

There are some things that Gutenberg Hub could improve with its templates. With several that I tested, I needed to switch specific blocks to be full width. This should be set up as the default with templates that are clearly meant to be full width in the example screenshots available on the site. It is a minor issue, but correcting this in the editor fixed several layout issues I was having when using the templates.

\n\n\n\n

Monetization Plans

\n\n\n\n

The second question that Kamal has not been prepared to answer fully over the past several months is how he will monetize Gutenberg Hub. Eventually, developers need some return on their investment when building tons of free tools. Many would do it all for free as long as their bills somehow got paid, but the reality is that there will come a tipping point where their projects need funding for long-haul maintenance.

\n\n\n\n

Kamal said he has laid the groundwork for funding but has not finalized anything yet. Currently, he is working on three ideas:

\n\n\n\n
  • Creating a pro version of his EditorPlus plugin.
  • Offering premium templates and blocks but is looking for a talented designer to work with.
  • Using ads specific to Gutenberg users, but he is not a fan of going this route or ads in general.
\n\n\n\n

He is open to feedback on how to best monetize the website and its projects. However, he said he is unwilling to compromise on giving away current and future free templates and tools.

\n\n\n\n

Future Gutenberg Projects

\n\n\n\n

Kamal said he does not have any new Gutenberg-related projects in the pipeline. The current plan is to work on what he has already created, which is a large ecosystem of Gutenberg tools that somehow work together.

\n\n\n\n

Outside of blocks, templates, and plugins, he is beginning to write more free tutorials on the Gutenberg Hub blog and focusing on creating videos around the project, including a new tutorial series for beginners.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 22 Sep 2020 21:05:19 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:48;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"WPTavern: WordPress Mobile Engineers Propose Dual Licensing Gutenberg under GPL v2.0 and MPL v2.0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105025\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:239:\"https://wptavern.com/wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6556:\"

During a Q&A session at WordCamp Europe 2020 online, Matt Mullenweg mentioned that Gutenberg contributors were considering dual licensing for embedding Gutenberg in mobile apps, along with the requirement that they would need to get an agreement from all contributors. WordPress mobile engineer Maxime Biais has just published a proposal for discussion, recommending dual licensing the editor under GPL v2.0 and MPL v2.0.

\n\n\n\n

“The GPL v2.0 license is a blocker for distributing the Gutenberg library in proprietary mobile apps,” Biais said in the corresponding GitHub issue. “Currently the only known users of Gutenberg on mobile are the WordPress mobile apps which are under GPL v2.0 (WordPress for AndroidWordPress for iOS). Mobile apps under the GPL v2.0 are not common and this limits Gutenberg usage in many apps.

\n\n\n\n

“Rich text editor libraries in the mobile space are lacking. There is no well known open source rich text editor for Android or iOS. We believe that Gutenberg could be a key library for many mobile apps, but that will never happen with the GPL v2.”

\n\n\n\n

Mobile app developers are limited by the GPL, because it requires the entire app to be distributed under the same license. The team is proposing dual licensing under MPL v2.0, a weaker copyleft license that is often considered to be more “business-friendly.” It allows users to combine the software with proprietary code. MPL v2.0 requires the source code for any changes to be available under the MPL, ensuring improvements are shared back to the community. The rest of the app can be distributed under any terms with the MPL v2.0 code included as part of a “larger work.”

\n\n\n\n

“The idea here is to keep some of the WordPress-specific modules under the GPL v2.0 only; some of them are not needed and not relevant for using Gutenberg in another software. Ideally, there would be a different way of bundling the project for being used in WordPress or in a non-GPL software,” Biais said.

\n\n\n\n

The GitHub ticket has several comments from developers who hope to be able to use the editor in their own projects. Radek Pietruszewski, tech lead for a collaborative todo app called Nozbe Teams, has been requesting a relicensing of Gutenberg since October 2019.

\n\n\n\n

“Our tech stack is essentially React on web and React Native on iOS and Android,” Pietruszewski said. “We’re a tiny company, and so we share >80% of app’s codebase between these 3 platforms.

\n\n\n\n

“Our app sorely lacks a WYSIWYG editor. We had a working implementation on web, but we decided to scrap it, because there was no way to port it on iOS and Android. There are pretty much no viable rich text editors for iOS or Android, yet alone both. But even then, shipping three completely separate, but somehow compatible editors would be a vast amount of work.”

\n\n\n\n

When Peitruszewski originally made his case to the mobile team, he identified Gutenberg/Aztec as a basic infrastructure that has the potential to enable many different apps:

\n\n\n\n

And that infrastructure is sorely lacking. There are very few rich text editor libraries on both iOS and Android — and most of them suck. And if you want an editor that has a shared API for both platforms… you’re stuck. There are no options – Gutenberg is the only game in town (and it’s really good).

And it’s very hard to create this infrastructure. WYSIWYG editors are very hard, and it takes entire teams years to develop them (and they still usually suck). Almost no-one has the resources to develop it just for themselves, and if they do, they’re unwilling to open-source it.

\n\n\n\n

Automattic’s mobile app engineers have struggled to get regular contributions to the apps, despite them being open source. Dual licensing Gutenberg could open up a new world of contributors with the editor being used more widely across the industry.

\n\n\n\n

“While we might not be big enough to be able to tackle a challenge of developing a rich text editor from scratch, we’re big enough to contribute features and bug fixes to open source projects,” Pietruszewski said.

\n\n\n\n

Matt Mullenweg was the first comment on Biais’ post in favor of the change:

\n\n\n\n

I think Gutenberg has a chance to become a cross-CMS standard, giving users a familiar interface any place they currently have a rich text box. There are hundreds and hundreds of engineers at other companies solving similar problems in a proprietary way, it would be amazing to get them working together but a huge barrier now is supporting Gutenberg in mobile apps, which every modern web service or CMS has. (Hypothetically, think of Mailchimp as a possible consumer and collaborator here, but it could be any company, SaaS, or other open source CMS.)

\n\n\n\n

Unless any major blockers come up in further discussion, this dual licensing change appears to be on track to move forward. Biais noted that a similar license change has already happened on Aztec-Android and Aztec-iOS. The last hurdle is gaining the approval of all the original code contributors or rewriting the code for those who decline to give approval.

\n\n\n\n

Once Gutenberg can be used under the MPL v2.0, the editor will gain a broader reach, with people already on deck wanting to use it. Other companies and projects that are normally outside WordPress’ open source orbit will also have the opportunity to enrich Gutenberg’s ecosystem with contributions back to the project. At the same time, the MPL 2.0 protects Gutenberg from companies that would try to re-release the code as a closed-source project.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 22:59:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:49;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:124:\"WPTavern: GitHub to Use ‘Main’ Instead of ‘Master’ as the Default Branch on All New Repositories Starting Next Month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105014\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:269:\"https://wptavern.com/github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month?utm_source=rss&utm_medium=rss&utm_campaign=github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4844:\"

In August, GitHub announced that it would change the “master” branch name for all new repositories created on the platform to “main” starting October 1. The date is less than two weeks away, and WordPress developers need to be prepared for the change if they use the service for version control or project management.

\n\n\n\n

The larger tech and web development community began conversations through various venues in June, a time in which the Black Lives Matter was gaining more traction in the U.S. and worldwide. The discussion centered on removing any terminology that could be discriminatory or oppressive to specific groups of people. This ongoing discussion has shown that there is a deep division over whether such changes are necessary or even helpful.

\n\n\n\n

The WordPress community is dealing with this division itself. Aaron Jorbin proposed a change at the same time to rename the default branch name on WordPress-owned repositories. Through discussion on his post and elsewhere, the community landed on “trunk,” which keeps WordPress projects in line with its SVN roots.

\n\n\n\n

“To close the circle on this, a decision was made in June and earlier today (August 19),” wrote Helen Hou-Sandí, a lead WordPress developer, in the comments of the original proposal. “I updated the default branch name for new GitHub repositories under the WordPress organization to be trunk after GitHub enabled early access to that feature.”

\n\n\n\n

As evidenced by the comments on the Tavern’s coverage of the proposal and those on the original post, the WordPress development community as a whole did not support this decision.

\n\n\n\n

Jorbin has updated several of WordPress’s repositories and switched them to use trunk instead of master. However, there are still some lingering projects yet to be updated, including the primary WordPress and WordPress Develop repositories. He left a comment with an updated list in June. There is no public word on whether the existing, leftover projects will be changed.

\n\n\n\n

WordPress Developer Preparations

\n\n\n\nCustomizing the default branch for a user’s GitHub repositories.\n\n\n\n

GitHub is merely changing the default branch name for new repositories starting on October 1. This change does not affect existing repositories. Individual users, organization owners, and enterprise administrators can customize the default branch via their account settings now before the switch is made. Owners can also change the default branch name for individual repositories.

\n\n\n\n

The biggest thing that developers need to watch out for is their tooling or other integrations that might still require the master branch. There may be cases where an alternative default branch name will break workflows. If planning to use a different branch name, the best thing to do right now is to spin up the tools you use on a test repository. If something breaks, check to see whether the particular tool you are using will be getting an update. In most cases, this should not be a problem because customized default branch names will be an industry standard.

\n\n\n\n

The great thing about how GitHub is rolling out this feature is that it offers a choice. Those who believe that “master” is oppressive can change the branch name to something they feel is more inclusive. For those who believe otherwise, they can keep their master branch. But, everyone can use the branch name they prefer.

\n\n\n\n

For existing repositories, GitHub is asking that developers be patient for now. The company is investing in tools to make this a seamless experience later this year. There are a few technical hurdles to clear first.

\n\n\n\n

Developers should read the full GitHub guide on setting the default branch for more information.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 20:39:55 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:8:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:31 GMT\";s:12:\"content-type\";s:8:\"text/xml\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:13:\"last-modified\";s:29:\"Thu, 22 Oct 2020 16:15:08 GMT\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 2\";s:16:\"content-encoding\";s:4:\"gzip\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(133,'_transient_timeout_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(134,'_transient_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603384291','no'),(135,'_transient_timeout_dash_v2_88ae138922fe95674369b1cb3d215a2b','1603427491','no'),(136,'_transient_dash_v2_88ae138922fe95674369b1cb3d215a2b','','no'),(139,'theme_mods_twentytwenty','a:1:{s:18:\"custom_css_post_id\";i:-1;}','yes'),(140,'recently_activated','a:0:{}','yes'); +INSERT INTO `wp55_options` VALUES (1,'siteurl','http://localhost','yes'),(2,'home','http://localhost','yes'),(3,'blogname','Datadog Test Application','yes'),(4,'blogdescription','Just another WordPress site','yes'),(5,'users_can_register','1','yes'),(6,'admin_email','test@gmail.com','yes'),(7,'start_of_week','1','yes'),(8,'use_balanceTags','0','yes'),(9,'use_smilies','1','yes'),(10,'require_name_email','1','yes'),(11,'comments_notify','1','yes'),(12,'posts_per_rss','10','yes'),(13,'rss_use_excerpt','0','yes'),(14,'mailserver_url','mail.example.com','yes'),(15,'mailserver_login','login@example.com','yes'),(16,'mailserver_pass','password','yes'),(17,'mailserver_port','110','yes'),(18,'default_category','1','yes'),(19,'default_comment_status','open','yes'),(20,'default_ping_status','open','yes'),(21,'default_pingback_flag','0','yes'),(22,'posts_per_page','10','yes'),(23,'date_format','F j, Y','yes'),(24,'time_format','g:i a','yes'),(25,'links_updated_date_format','F j, Y g:i a','yes'),(26,'comment_moderation','0','yes'),(27,'moderation_notify','1','yes'),(28,'permalink_structure','/%postname%','yes'),(29,'rewrite_rules','a:93:{s:11:\"^wp-json/?$\";s:22:\"index.php?rest_route=/\";s:14:\"^wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:21:\"^index.php/wp-json/?$\";s:22:\"index.php?rest_route=/\";s:24:\"^index.php/wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:17:\"^wp-sitemap\\.xml$\";s:23:\"index.php?sitemap=index\";s:17:\"^wp-sitemap\\.xsl$\";s:36:\"index.php?sitemap-stylesheet=sitemap\";s:23:\"^wp-sitemap-index\\.xsl$\";s:34:\"index.php?sitemap-stylesheet=index\";s:48:\"^wp-sitemap-([a-z]+?)-([a-z\\d_-]+?)-(\\d+?)\\.xml$\";s:75:\"index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]\";s:34:\"^wp-sitemap-([a-z]+?)-(\\d+?)\\.xml$\";s:47:\"index.php?sitemap=$matches[1]&paged=$matches[2]\";s:47:\"category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:42:\"category/(.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:23:\"category/(.+?)/embed/?$\";s:46:\"index.php?category_name=$matches[1]&embed=true\";s:35:\"category/(.+?)/page/?([0-9]{1,})/?$\";s:53:\"index.php?category_name=$matches[1]&paged=$matches[2]\";s:17:\"category/(.+?)/?$\";s:35:\"index.php?category_name=$matches[1]\";s:44:\"tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:39:\"tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:20:\"tag/([^/]+)/embed/?$\";s:36:\"index.php?tag=$matches[1]&embed=true\";s:32:\"tag/([^/]+)/page/?([0-9]{1,})/?$\";s:43:\"index.php?tag=$matches[1]&paged=$matches[2]\";s:14:\"tag/([^/]+)/?$\";s:25:\"index.php?tag=$matches[1]\";s:45:\"type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:40:\"type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:21:\"type/([^/]+)/embed/?$\";s:44:\"index.php?post_format=$matches[1]&embed=true\";s:33:\"type/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?post_format=$matches[1]&paged=$matches[2]\";s:15:\"type/([^/]+)/?$\";s:33:\"index.php?post_format=$matches[1]\";s:12:\"robots\\.txt$\";s:18:\"index.php?robots=1\";s:13:\"favicon\\.ico$\";s:19:\"index.php?favicon=1\";s:48:\".*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\\.php$\";s:18:\"index.php?feed=old\";s:20:\".*wp-app\\.php(/.*)?$\";s:19:\"index.php?error=403\";s:18:\".*wp-register.php$\";s:23:\"index.php?register=true\";s:32:\"feed/(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:27:\"(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:8:\"embed/?$\";s:21:\"index.php?&embed=true\";s:20:\"page/?([0-9]{1,})/?$\";s:28:\"index.php?&paged=$matches[1]\";s:41:\"comments/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:36:\"comments/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:17:\"comments/embed/?$\";s:21:\"index.php?&embed=true\";s:44:\"search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:39:\"search/(.+)/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:20:\"search/(.+)/embed/?$\";s:34:\"index.php?s=$matches[1]&embed=true\";s:32:\"search/(.+)/page/?([0-9]{1,})/?$\";s:41:\"index.php?s=$matches[1]&paged=$matches[2]\";s:14:\"search/(.+)/?$\";s:23:\"index.php?s=$matches[1]\";s:47:\"author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:42:\"author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:23:\"author/([^/]+)/embed/?$\";s:44:\"index.php?author_name=$matches[1]&embed=true\";s:35:\"author/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?author_name=$matches[1]&paged=$matches[2]\";s:17:\"author/([^/]+)/?$\";s:33:\"index.php?author_name=$matches[1]\";s:69:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:64:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:45:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$\";s:74:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true\";s:57:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:81:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]\";s:39:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$\";s:63:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]\";s:56:\"([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:51:\"([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:32:\"([0-9]{4})/([0-9]{1,2})/embed/?$\";s:58:\"index.php?year=$matches[1]&monthnum=$matches[2]&embed=true\";s:44:\"([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:65:\"index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]\";s:26:\"([0-9]{4})/([0-9]{1,2})/?$\";s:47:\"index.php?year=$matches[1]&monthnum=$matches[2]\";s:43:\"([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:38:\"([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:19:\"([0-9]{4})/embed/?$\";s:37:\"index.php?year=$matches[1]&embed=true\";s:31:\"([0-9]{4})/page/?([0-9]{1,})/?$\";s:44:\"index.php?year=$matches[1]&paged=$matches[2]\";s:13:\"([0-9]{4})/?$\";s:26:\"index.php?year=$matches[1]\";s:27:\".?.+?/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\".?.+?/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\".?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\".?.+?/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"(.?.+?)/embed/?$\";s:41:\"index.php?pagename=$matches[1]&embed=true\";s:20:\"(.?.+?)/trackback/?$\";s:35:\"index.php?pagename=$matches[1]&tb=1\";s:40:\"(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:35:\"(.?.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:28:\"(.?.+?)/page/?([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&paged=$matches[2]\";s:35:\"(.?.+?)/comment-page-([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&cpage=$matches[2]\";s:24:\"(.?.+?)(?:/([0-9]+))?/?$\";s:47:\"index.php?pagename=$matches[1]&page=$matches[2]\";s:27:\"[^/]+/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\"[^/]+/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\"[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\"[^/]+/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"([^/]+)/embed/?$\";s:37:\"index.php?name=$matches[1]&embed=true\";s:20:\"([^/]+)/trackback/?$\";s:31:\"index.php?name=$matches[1]&tb=1\";s:40:\"([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:35:\"([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:28:\"([^/]+)/page/?([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&paged=$matches[2]\";s:35:\"([^/]+)/comment-page-([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&cpage=$matches[2]\";s:24:\"([^/]+)(?:/([0-9]+))?/?$\";s:43:\"index.php?name=$matches[1]&page=$matches[2]\";s:16:\"[^/]+/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:26:\"[^/]+/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:46:\"[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:22:\"[^/]+/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";}','yes'),(30,'hack_file','0','yes'),(31,'blog_charset','UTF-8','yes'),(32,'moderation_keys','','no'),(33,'active_plugins','a:1:{i:0;s:19:\"datadog/datadog.php\";}','yes'),(34,'category_base','','yes'),(35,'ping_sites','http://rpc.pingomatic.com/','yes'),(36,'comment_max_links','2','yes'),(37,'gmt_offset','0','yes'),(38,'default_email_category','1','yes'),(39,'recently_edited','','no'),(40,'template','twentytwenty','yes'),(41,'stylesheet','twentytwenty','yes'),(42,'comment_registration','0','yes'),(43,'html_type','text/html','yes'),(44,'use_trackback','0','yes'),(45,'default_role','subscriber','yes'),(46,'db_version','48748','yes'),(47,'uploads_use_yearmonth_folders','1','yes'),(48,'upload_path','','yes'),(49,'blog_public','0','yes'),(50,'default_link_category','2','yes'),(51,'show_on_front','posts','yes'),(52,'tag_base','','yes'),(53,'show_avatars','1','yes'),(54,'avatar_rating','G','yes'),(55,'upload_url_path','','yes'),(56,'thumbnail_size_w','150','yes'),(57,'thumbnail_size_h','150','yes'),(58,'thumbnail_crop','1','yes'),(59,'medium_size_w','300','yes'),(60,'medium_size_h','300','yes'),(61,'avatar_default','mystery','yes'),(62,'large_size_w','1024','yes'),(63,'large_size_h','1024','yes'),(64,'image_default_link_type','none','yes'),(65,'image_default_size','','yes'),(66,'image_default_align','','yes'),(67,'close_comments_for_old_posts','0','yes'),(68,'close_comments_days_old','14','yes'),(69,'thread_comments','1','yes'),(70,'thread_comments_depth','5','yes'),(71,'page_comments','0','yes'),(72,'comments_per_page','50','yes'),(73,'default_comments_page','newest','yes'),(74,'comment_order','asc','yes'),(75,'sticky_posts','a:0:{}','yes'),(76,'widget_categories','a:2:{i:2;a:4:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:12:\"hierarchical\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(77,'widget_text','a:0:{}','yes'),(78,'widget_rss','a:0:{}','yes'),(79,'uninstall_plugins','a:0:{}','no'),(80,'timezone_string','','yes'),(81,'page_for_posts','0','yes'),(82,'page_on_front','0','yes'),(83,'default_post_format','0','yes'),(84,'link_manager_enabled','0','yes'),(85,'finished_splitting_shared_terms','1','yes'),(86,'site_icon','0','yes'),(87,'medium_large_size_w','768','yes'),(88,'medium_large_size_h','0','yes'),(89,'wp_page_for_privacy_policy','3','yes'),(90,'show_comments_cookies_opt_in','1','yes'),(91,'admin_email_lifespan','1618936275','yes'),(92,'disallowed_keys','','no'),(93,'comment_previously_approved','1','yes'),(94,'auto_plugin_theme_update_emails','a:0:{}','no'),(95,'initial_db_version','48748','yes'),(96,'wp55_user_roles','a:5:{s:13:\"administrator\";a:2:{s:4:\"name\";s:13:\"Administrator\";s:12:\"capabilities\";a:61:{s:13:\"switch_themes\";b:1;s:11:\"edit_themes\";b:1;s:16:\"activate_plugins\";b:1;s:12:\"edit_plugins\";b:1;s:10:\"edit_users\";b:1;s:10:\"edit_files\";b:1;s:14:\"manage_options\";b:1;s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:6:\"import\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:8:\"level_10\";b:1;s:7:\"level_9\";b:1;s:7:\"level_8\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;s:12:\"delete_users\";b:1;s:12:\"create_users\";b:1;s:17:\"unfiltered_upload\";b:1;s:14:\"edit_dashboard\";b:1;s:14:\"update_plugins\";b:1;s:14:\"delete_plugins\";b:1;s:15:\"install_plugins\";b:1;s:13:\"update_themes\";b:1;s:14:\"install_themes\";b:1;s:11:\"update_core\";b:1;s:10:\"list_users\";b:1;s:12:\"remove_users\";b:1;s:13:\"promote_users\";b:1;s:18:\"edit_theme_options\";b:1;s:13:\"delete_themes\";b:1;s:6:\"export\";b:1;}}s:6:\"editor\";a:2:{s:4:\"name\";s:6:\"Editor\";s:12:\"capabilities\";a:34:{s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;}}s:6:\"author\";a:2:{s:4:\"name\";s:6:\"Author\";s:12:\"capabilities\";a:10:{s:12:\"upload_files\";b:1;s:10:\"edit_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;s:22:\"delete_published_posts\";b:1;}}s:11:\"contributor\";a:2:{s:4:\"name\";s:11:\"Contributor\";s:12:\"capabilities\";a:5:{s:10:\"edit_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;}}s:10:\"subscriber\";a:2:{s:4:\"name\";s:10:\"Subscriber\";s:12:\"capabilities\";a:2:{s:4:\"read\";b:1;s:7:\"level_0\";b:1;}}}','yes'),(97,'fresh_site','0','yes'),(98,'widget_search','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(99,'widget_recent-posts','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(100,'widget_recent-comments','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(101,'widget_archives','a:2:{i:2;a:3:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(102,'widget_meta','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(103,'sidebars_widgets','a:4:{s:19:\"wp_inactive_widgets\";a:0:{}s:9:\"sidebar-1\";a:3:{i:0;s:8:\"search-2\";i:1;s:14:\"recent-posts-2\";i:2;s:17:\"recent-comments-2\";}s:9:\"sidebar-2\";a:3:{i:0;s:10:\"archives-2\";i:1;s:12:\"categories-2\";i:2;s:6:\"meta-2\";}s:13:\"array_version\";i:3;}','yes'),(104,'cron','a:7:{i:1674663182;a:1:{s:34:\"wp_privacy_delete_old_export_files\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"hourly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:3600;}}}i:1674695582;a:4:{s:18:\"wp_https_detection\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_version_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:17:\"wp_update_plugins\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_update_themes\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674695658;a:1:{s:21:\"wp_update_user_counts\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674738782;a:2:{s:30:\"wp_site_health_scheduled_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"weekly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:604800;}}s:32:\"recovery_mode_clean_expired_keys\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738858;a:2:{s:19:\"wp_scheduled_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:25:\"delete_expired_transients\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738859;a:1:{s:30:\"wp_scheduled_auto_draft_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}s:7:\"version\";i:2;}','yes'),(105,'widget_pages','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(106,'widget_calendar','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(107,'widget_media_audio','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(108,'widget_media_image','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(109,'widget_media_gallery','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(110,'widget_media_video','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(111,'widget_tag_cloud','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(112,'widget_nav_menu','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(113,'widget_custom_html','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(114,'_transient_doing_cron','1603384688.0394918918609619140625','yes'),(115,'_site_transient_update_core','O:8:\"stdClass\":4:{s:7:\"updates\";a:1:{i:0;O:8:\"stdClass\":10:{s:8:\"response\";s:6:\"latest\";s:8:\"download\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:6:\"locale\";s:5:\"en_US\";s:8:\"packages\";O:8:\"stdClass\":5:{s:4:\"full\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:10:\"no_content\";s:70:\"https://downloads.wordpress.org/release/wordpress-5.5.1-no-content.zip\";s:11:\"new_bundled\";s:71:\"https://downloads.wordpress.org/release/wordpress-5.5.1-new-bundled.zip\";s:7:\"partial\";s:0:\"\";s:8:\"rollback\";s:0:\"\";}s:7:\"current\";s:5:\"5.5.1\";s:7:\"version\";s:5:\"5.5.1\";s:11:\"php_version\";s:6:\"5.6.20\";s:13:\"mysql_version\";s:3:\"5.0\";s:11:\"new_bundled\";s:3:\"5.3\";s:15:\"partial_version\";s:0:\"\";}}s:12:\"last_checked\";i:1603384286;s:15:\"version_checked\";s:5:\"5.5.1\";s:12:\"translations\";a:0:{}}','no'),(116,'_site_transient_update_plugins','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384412;s:7:\"checked\";a:3:{s:19:\"akismet/akismet.php\";s:5:\"4.1.6\";s:19:\"datadog/datadog.php\";s:5:\"0.0.0\";s:9:\"hello.php\";s:5:\"1.7.2\";}s:8:\"response\";a:0:{}s:12:\"translations\";a:0:{}s:9:\"no_update\";a:2:{s:19:\"akismet/akismet.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:21:\"w.org/plugins/akismet\";s:4:\"slug\";s:7:\"akismet\";s:6:\"plugin\";s:19:\"akismet/akismet.php\";s:11:\"new_version\";s:5:\"4.1.6\";s:3:\"url\";s:38:\"https://wordpress.org/plugins/akismet/\";s:7:\"package\";s:56:\"https://downloads.wordpress.org/plugin/akismet.4.1.6.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:59:\"https://ps.w.org/akismet/assets/icon-256x256.png?rev=969272\";s:2:\"1x\";s:59:\"https://ps.w.org/akismet/assets/icon-128x128.png?rev=969272\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:61:\"https://ps.w.org/akismet/assets/banner-772x250.jpg?rev=479904\";}s:11:\"banners_rtl\";a:0:{}}s:9:\"hello.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:25:\"w.org/plugins/hello-dolly\";s:4:\"slug\";s:11:\"hello-dolly\";s:6:\"plugin\";s:9:\"hello.php\";s:11:\"new_version\";s:5:\"1.7.2\";s:3:\"url\";s:42:\"https://wordpress.org/plugins/hello-dolly/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/plugin/hello-dolly.1.7.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-256x256.jpg?rev=2052855\";s:2:\"1x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-128x128.jpg?rev=2052855\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:66:\"https://ps.w.org/hello-dolly/assets/banner-772x250.jpg?rev=2052855\";}s:11:\"banners_rtl\";a:0:{}}}}','no'),(117,'_site_transient_timeout_theme_roots','1603386087','no'),(118,'_site_transient_theme_roots','a:3:{s:14:\"twentynineteen\";s:7:\"/themes\";s:15:\"twentyseventeen\";s:7:\"/themes\";s:12:\"twentytwenty\";s:7:\"/themes\";}','no'),(119,'_site_transient_update_themes','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384287;s:7:\"checked\";a:3:{s:14:\"twentynineteen\";s:3:\"1.7\";s:15:\"twentyseventeen\";s:3:\"2.4\";s:12:\"twentytwenty\";s:3:\"1.5\";}s:8:\"response\";a:0:{}s:9:\"no_update\";a:3:{s:14:\"twentynineteen\";a:6:{s:5:\"theme\";s:14:\"twentynineteen\";s:11:\"new_version\";s:3:\"1.7\";s:3:\"url\";s:44:\"https://wordpress.org/themes/twentynineteen/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/theme/twentynineteen.1.7.zip\";s:8:\"requires\";s:5:\"4.9.6\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:15:\"twentyseventeen\";a:6:{s:5:\"theme\";s:15:\"twentyseventeen\";s:11:\"new_version\";s:3:\"2.4\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentyseventeen/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentyseventeen.2.4.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:12:\"twentytwenty\";a:6:{s:5:\"theme\";s:12:\"twentytwenty\";s:11:\"new_version\";s:3:\"1.5\";s:3:\"url\";s:42:\"https://wordpress.org/themes/twentytwenty/\";s:7:\"package\";s:58:\"https://downloads.wordpress.org/theme/twentytwenty.1.5.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}}s:12:\"translations\";a:0:{}}','no'),(120,'_site_transient_timeout_browser_6daa110c3e56e442b403473c9591e946','1603989088','no'),(121,'_site_transient_browser_6daa110c3e56e442b403473c9591e946','a:10:{s:4:\"name\";s:6:\"Chrome\";s:7:\"version\";s:12:\"86.0.4240.80\";s:8:\"platform\";s:9:\"Macintosh\";s:10:\"update_url\";s:29:\"https://www.google.com/chrome\";s:7:\"img_src\";s:43:\"http://s.w.org/images/browsers/chrome.png?1\";s:11:\"img_src_ssl\";s:44:\"https://s.w.org/images/browsers/chrome.png?1\";s:15:\"current_version\";s:2:\"18\";s:7:\"upgrade\";b:0;s:8:\"insecure\";b:0;s:6:\"mobile\";b:0;}','no'),(122,'_site_transient_timeout_php_check_56babb1797dd31750a342dc4c8a11025','1603989088','no'),(123,'_site_transient_php_check_56babb1797dd31750a342dc4c8a11025','a:5:{s:19:\"recommended_version\";s:3:\"7.4\";s:15:\"minimum_version\";s:6:\"5.6.20\";s:12:\"is_supported\";b:1;s:9:\"is_secure\";b:1;s:13:\"is_acceptable\";b:1;}','no'),(124,'_site_transient_timeout_community-events-e0e4f94be3c2d577e126ec3b012627f2','1603427490','no'),(125,'_site_transient_community-events-e0e4f94be3c2d577e126ec3b012627f2','a:4:{s:9:\"sandboxed\";b:0;s:5:\"error\";N;s:8:\"location\";a:1:{s:2:\"ip\";s:12:\"192.168.16.0\";}s:6:\"events\";a:2:{i:0;a:10:{s:4:\"type\";s:6:\"meetup\";s:5:\"title\";s:58:\"Discussion Group: WordPress Troubleshooting Basics: Part 1\";s:3:\"url\";s:68:\"https://www.meetup.com/learn-wordpress-discussions/events/273993927/\";s:6:\"meetup\";s:27:\"Learn WordPress Discussions\";s:10:\"meetup_url\";s:51:\"https://www.meetup.com/learn-wordpress-discussions/\";s:4:\"date\";s:19:\"2020-10-23 06:00:00\";s:8:\"end_date\";s:19:\"2020-10-23 07:00:00\";s:20:\"start_unix_timestamp\";i:1603458000;s:18:\"end_unix_timestamp\";i:1603461600;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"US\";s:8:\"latitude\";d:37.779998779297;s:9:\"longitude\";d:-122.41999816895;}}i:1;a:10:{s:4:\"type\";s:8:\"wordcamp\";s:5:\"title\";s:17:\"WordCamp Bulgaria\";s:3:\"url\";s:35:\"https://bulgaria.wordcamp.org/2020/\";s:6:\"meetup\";N;s:10:\"meetup_url\";N;s:4:\"date\";s:19:\"2020-10-24 10:00:00\";s:8:\"end_date\";s:19:\"2020-10-24 10:00:00\";s:20:\"start_unix_timestamp\";i:1603522800;s:18:\"end_unix_timestamp\";i:1603522800;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"BG\";s:8:\"latitude\";d:42.733883;s:9:\"longitude\";d:25.48583;}}}}','no'),(126,'can_compress_scripts','0','no'),(127,'_transient_timeout_feed_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(128,'_transient_feed_9bbd59226dc36b9b26cd43f15694c5c3','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:49:\"\n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:27:\"News – – WordPress.org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:13:\"lastBuildDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 20:10:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"en-US\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"generator\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"https://wordpress.org/?v=5.6-beta1-49274\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:10:{i:0;a:6:{s:4:\"data\";s:60:\"\n \n\n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WordPress 5.6 Beta 1 is now available for testing!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8236:\"\n

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:38:\"The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:363:\"This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September.  WordPress 5.5.1 Launch On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8713:\"\n

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"WordPress 5.5.1 Maintenance Release\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"https://wordpress.org/news/2020/09/wordpress-5-5-1-maintenance-release/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 19:13:53 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8979\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:460:\"WordPress 5.5.1 is now available! This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade. You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process. […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9020:\"\n

WordPress 5.5.1 is now available!

\n\n\n\n

This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade.

\n\n\n\n

You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process.

\n\n\n\n

WordPress 5.5.1 is a short-cycle maintenance release. The next major release will be version 5.6.

\n\n\n\n

To see a full list of changes, you can browse the list on Trac, read the 5.5.1 RC1 and 5.5.1 RC2 posts, or visit the 5.5.1 documentation page.

\n\n\n\n

Thanks and props!

\n\n\n\n

The 5.5.1 release was led by @audrasjb, @azhiyadev, @davidbaumwald, @desrosj, @johnbillion, @planningwrite, @sergeybiryukov and @whyisjake.

\n\n\n\n

Thank you to everyone who helped make WordPress 5.5.1 happen:

\n\n\n\nAmit Dudhat, Andrea Fercia, Andrey “Rarst” Savchenko, Andy Fragen, Angel Hess, avixansa, bobbingwide, Brian Hogg, chunkysteveo, Clayton Collie, David Baumwald, David Herrera, dd32, demetris, Dominik Schilling, dushakov, Earle Davies, Enrique Sánchez, Frankie Jarrett, fullofcaffeine, Garrett Hyder, Gary Jones, gchtr, Hauwa, Herre Groen, Howdy_McGee, Ipstenu (Mika Epstein), Jb Audras, Jeremy Felt, Jeroen Rotty, Joen A., Johanna de Vos, John Blackbourn, John James Jacoby, Jonathan Bossenger, Jonathan Desrosiers, Jonathan Stegall, Joost de Valk, Jorge Costa, Justin Ahinon, Kalpesh Akabari, Kevin Hagerty, Knut Sparhell, Kyle B. Johnson, landau, Laxman Prajapati, Lester Chan, mailnew2ster, Marius L. J., Mark Jaquith, Mark Uraine, Matt Gibson, Michael Beckwith, Mikey Arce, Mohammad Jangda, Mukesh Panchal, Nabil Moqbel, net, oakesjosh, O André, Omar Reiss, Ov3rfly, Paddy, Pascal Casier, Paul Biron, Peter Wilson, rajeshsingh520, Rami Yushuvaev, rebasaurus, riaanlom, Riad Benguella, Rodrigo Arias, rtagliento, salvoaranzulla, Sanjeev Aryal, sarahricker, Sergey Biryukov, Stephen Bernhardt, Steven Stern (sterndata), Thomas M, Timothy Jacobs, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), TwentyZeroTwo, Winstina, wittich, and Yoav Farhi.\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8979\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"The Month in WordPress: August 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wordpress.org/news/2020/09/the-month-in-wordpress-august-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 09:32:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8983\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:362:\"August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world. WordPress 5.5 Launch […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9605:\"\n

August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Launch

\n\n\n\n

The team launched WordPress 5.5 on August 11. The major release comes with a host of features like automatic updates for plugins and themes, enabling updates over uploaded ZIP files, a block directory, XML sitemaps, block patterns, inline image editing, and lazy-loading images, to name a few. WordPress 5.5 is now available in 50 languages too! You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. Subsequent to the 5.5 release, the 5.5.1 release candidate came out on August 28, which will be followed by its official launch of the minor release on September 1.

\n\n\n\n

A record 805 people contributed to WordPress 5.5, hailing from 58 different countries. @audrasjb has compiled many more stats like that and they’re well worth a read!

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.7 and 8.8

\n\n\n\n

The core team launched Gutenberg 8.7 and 8.8. Version 8.7 saw many improvements to the Post Block suite, along with other changes like adding a block example to the Buttons block, consistently autosaving edits, and updating the group block description. Version 8.8 offers updates to Global Styles, the Post Block suite, and Template management. The release significantly improves the back-compatibility of the new Widget Screen, and also includes other important accessibility and mobile improvements to user interfaces like the Toolbar, navigation menus, and Popovers. For full details on the latest versions of these Gutenberg releases, visit these posts about 8.7 and 8.8.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Check out the brand new Learn WordPress platform!

\n\n\n\n

Learn WordPress is a brand new cross-team initiative led by the WordPress Community team, with support from the training team, the TV team, and the meta team. This platform is a learning repository on learn.wordpress.org, where WordPress learning content will be made available. Video workshops published on the site will be followed up by supplementary discussion groups based on workshop content. The first of these discussion groups have been scheduled, and you can join an upcoming discussion on the dedicated meetup group. The community team invites members to contribute to the project. You can apply to present a workshop, assist with reviewing submitted workshops, and add ideas for workshops that you would like to see on the site. You can also apply to be a discussion group leader to organize discussions directly through the learn.wordpress.org platform. We are also creating a dedicated Learn WordPress working group and have posted a call for volunteers. Meetup organizers can use Learn WordPress content for their meetup events (without applying as a discussion group leader). Simply ask your meetup group to watch one of the workshops in the weeks leading up to your scheduled event, and then host a discussion group for that content as your event.

\n\n\n\n

Want to get involved with the Community team? Follow the Community blog, or join them in the #community-events channel in the Making WordPress Slack group. To organize a local WordPress community event, visit the handbook page

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8983\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n\n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"WordPress 5.5 “Eckstine”\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"https://wordpress.org/news/2020/08/eckstine/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 11 Aug 2020 19:03:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8799\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:354:\"Version 5.5 \"Eckstine\" of WordPress is available for download or update in your WordPress dashboard. With this release, your site gets new power in three major areas: \nspeed (lazy-loading images), search (sitemaps included by default), and security (auto-updates for plugins and themes), along with many new features and improvements to the block editor.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:3:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:48:\"https://s.w.org/images/core/5.5/auto-updates.mp4\";s:6:\"length\";s:6:\"238264\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:50:\"https://s.w.org/images/core/5.5/block-patterns.mp4\";s:6:\"length\";s:7:\"3518792\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:56:\"https://s.w.org/images/core/5.5/inline-image-editing.mp4\";s:6:\"length\";s:7:\"3145140\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Matt Mullenweg\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:71062:\"\n

Here it is! Named “Eckstine” in honor of Billy Eckstine, this latest and greatest version of WordPress is available for download or update in your dashboard.

\n\n\n\n
\"\"
\n\n\n\n
\n

Welcome to WordPress 5.5.

\n\n\n\n

In WordPress 5.5, your site gets new power in three major areas:
speed, search, and security.

\n
\n\n\n\n
\n
\n\n\n\n
\n

Speed

\n\n\n\n

Posts and pages feel faster, thanks to lazy-loaded images.

\n\n\n\n

Images give your story a lot of impact, but they can sometimes make your site seem slow.

\n\n\n\n

In WordPress 5.5, images wait to load until they’re just about to scroll into view. The technical term is ‘lazy loading.’

\n\n\n\n

On mobile, lazy loading can also keep browsers from loading files meant for other devices. That can save your readers money on data — and help preserve battery life.

\n\n\n\n

Search

\n\n\n\n

Say hello to your new sitemap.

\n\n\n\n

WordPress sites work well with search engines.

\n\n\n\n

Now, by default, WordPress 5.5 includes an XML sitemap that helps search engines discover your most important pages from the very minute you go live.

\n\n\n\n

So more people will find your site sooner, giving you more time to engage, retain and convert them to subscribers, customers or whatever fits your definition of success.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Security

\n\n\n\n
Now you can choose to update plugins and themes automatically–or pick just a few–from the screens you’ve always used.
\n\n\n\n

Auto-updates for Plugins and Themes

\n\n\n\n

Now you can set plugins and themes to update automatically — or not! — in the WordPress admin. So you always know your site is running the latest code available.

\n\n\n\n

You can also turn auto-updates on or off for each plugin or theme you have installed — all on the same screens you’ve always used.

\n\n\n\n

Update by uploading ZIP files

\n\n\n\n

If updating plugins and themes manually is your thing, now that’s easier too — just upload a ZIP file.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Highlights from the block editor

\n\n\n\n

Once again, the latest WordPress release packs a long list of exciting new features for the block editor. For example:

\n\n\n\n
\n\n\n\n
\n
\n

Block patterns

\n\n\n\n

New block patterns make it simple and fun to create complex, beautiful layouts, using combinations of text and media that you can mix and match to fit your story.

\n\n\n\n

You will also find block patterns in a wide variety of plugins and themes, with more added all the time. Pick any of them from a single place — just click and go!

\n
\n\n\n\n
\n

The new block directory

\n\n\n\n

Now it’s easier than ever to find the block you need. The new block directory is built right into the block editor, so you can install new block types to your site without ever leaving the editor.

\n\n\n\n

Inline image editing

\n\n\n\n

Crop, rotate, and zoom your photos right from the image block. If you spend a lot of time on images, this could save you hours!

\n
\n
\n\n\n\n
\n\n\n\n

And so much more.

\n\n\n\n

The highlights above are a tiny fraction of the new block editor features you’ve just installed. Open the block editor and enjoy!

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Accessibility

\n\n\n\n

Every release adds improvements to the accessible publishing experience, and that remains true for WordPress 5.5.

\n\n\n\n

Now you can copy links in media screens and modal dialogs with a button, instead of trying to highlight a line of text.

\n\n\n\n

You can also move meta boxes with the keyboard, and edit images in WordPress with your assistive device, as it can read you the instructions in the image editor.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

For developers

\n\n\n\n

5.5 also brings a big box of changes just for developers.

\n\n\n\n
\n
\n

Server-side registered blocks in the REST API

\n\n\n\n

The addition of block types endpoints means that JavaScript apps (like the block editor) can retrieve definitions for any blocks registered on the server.

\n\n\n\n

Defining environments

\n\n\n\n

WordPress now has a standardized way to define a site’s environment type (staging, production, etc). Retrieve that type with wp_get_environment_type() and execute only the appropriate code.

\n\n\n\n

Dashicons

\n\n\n\n

The Dashicons library has received its final update in 5.5. It adds 39 block editor icons along with 26 others.

\n\n\n\n

Passing data to template files

\n\n\n\n

The template loading functions (get_header()get_template_part(), etc.) have a new $args argument. So now you can pass an entire array’s worth of data to those templates.

\n
\n\n\n\n
\n

More changes for developers

\n\n\n\n
  • The PHPMailer library just got a major update, going from version 5.2.27 to 6.1.6.
  • Now get more fine-grained control of redirect_guess_404_permalink().
  • Sites that use PHP’s OPcache will see more reliable cache invalidation, thanks to the new wp_opcache_invalidate() function during updates (including to plugins and themes).
  • Custom post types associated with the category taxonomy can now opt-in to supporting the default term.
  • Default terms can now be specified for custom taxonomies in register_taxonomy().
  • The REST API now officially supports specifying default metadata values through register_meta().
  • You will find updated versions of these bundled libraries: SimplePie, Twemoji, Masonry, imagesLoaded, getID3, Moment.js, and clipboard.js.
\n
\n
\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

The Squad

\n\n\n\n

Leading this release were Matt MullenwegJake Spurlock, and David Baumwald. Supporting them was this highly enthusiastic release squad:

\n\n\n\n\n\n\n\n

Joining the squad throughout the release cycle were 805 generous volunteer contributors who collectively worked on over 523 tickets on Trac and over 1660 pull requests on GitHub.

\n\n\n\n

Put on a Billy Eckstine playlist, click that update button (or download it directly), and check the profiles of the fine folks that helped:

\n\n\nA2 Hosting, a4jp . com, a6software, Aaron D. Campbell, Aaron Jorbin, abderrahman, Abha Thakor, Achal Jain, achbed, Achyuth Ajoy, acosmin, acsnaterse, Adam Silverstein, Addie, addyosmani, adnan.limdi, adrian, airamerica, Ajay Ghaghretiya, Ajit Bohra, akbarhusen, akbarhusen429, Akhilesh Sabharwal, Akira Tachibana, Alain Schlesser, Albert Juhé Lluveras, Alex Concha, Alex Kirk, Alex Lende, Alex Shiels, Ali Shan, ali11007, Allen Snook, amaschas, Amit Dudhat, anbumz, andfinally, Andrea Fercia, Andrea Middleton, Andrea Tarantini, Andrei Draganescu, Andrew Duthie, Andrew Nacin, Andrew Nevins, Andrew Ozz, Andrey \"Rarst\" Savchenko, Andrés Maneiro, Andy Fragen, Andy Meerwaldt, Andy Peatling, Angel Hess, Angela Jin, Angelika Reisiger, Anh Tran, Ankit Gade, Ankit K Gupta, Ankit Panchal, Anne McCarthy, Anthony Burchell, Anthony Hortin, Anton Timmermans, Antonis Lilis, apedog, archon810, argentite, Arpit G Shah, Arslan Ahmed, asalce, ashiagr, ashour, Atharva Dhekne, Aurélien Joahny, aussi, automaton, avixansa, Ayesh Karunaratne, BackuPs, Barry, Barry Ceelen, Bart Czyz, bartekcholewa, bartkalisz, Bastien Ho, Bastien Martinent, bcworkz, bdbch, bdcstr, Ben Dunkle, Bence Szalai, bencroskery, Benjamin Gosset, Benoit Chantre, Bernhard Reiter, BettyJJ, bgermann, bigcloudmedia, bigdawggi, Bill Erickson, Birgir Erlendsson (birgire), Birgit Pauli-Haack, BjornW, bobbingwide, bonger, Boone Gorges, Boris Brdarić, Boy Witthaya, Brandon Kraft, Brandon Payton, Brent Swisher, Brian Hogg, Brian Krogsgard, bruandet, Bunty, Burhan Nasir, caiocrcosta, Cameron Voell, cameronamcintyre, Carike, Carl Wuensche, Carlos Galarza, Carolina Nymark, Caroline Moore, Carrigan, ceyhun, Chad, Chad Butler, Charles Fulton, Chetan Prajapati, Chintan hingrajiya, Chip Snyder, Chloé Bringmann, Chouby, Chris Van Patten, chriscct7, Christian Chung, Christian Jongeneel, Christian Sabo, Christian Wach, Christoph Herr, Christopher Churchill, chunkysteveo, cklee, clayray, Clayton Collie, Clifford Paulick, codeforest, Commeuneimage, Copons, Corey McKrill, cpasqualini, Cristovao Verstraeten, Csaba (LittleBigThings), Curtis Belt, Cyrus Collier, D.PERONNE, d6, Daniel Bachhuber, Daniel Hüsken, Daniel James, Daniel Llewellyn, Daniel Richards, Daniel Roch, Daniele Scasciafratte, Danny, Darko G., Darren Ethier (nerrad), Dave McHale, Dave Whitley, David A. Kennedy, David Aguilera, David Anderson, David Artiss, David Baumwald, David Brumbaugh, David E. Smith, David Herrera, David Ryan, David Shanske, David Smith, david.binda, davidvee, dchymko, Debabrata Karfa, Deepak Lalwani, dekervit, Delowar Hossain, demetris, Denis Yanchevskiy, derekakelly, Derrick Hammer, Derrick Tennant, Diane Co, Dilip Bheda, Dimitris Mitsis, dingo-d, Dion Hulse, Dixita Dusara, djennez, dmenard, dmethvin, doc987, Dominik Schilling, donmhico, Dono12, Doobeedoo, Dossy Shiobara, dpacks, dratwas, Drew Jaynes, DrLightman, DrProtocols, dsifford, dudo, dushakov, Dustin Bolton, dvershinin, Dylan Kuhn, Earle Davies, ecotechie, Eddie Moya, Eddy, Edi Amin, ehtis, Eileen Violini, Ekaterina, Ella van Durpe, elmastudio, Emanuel Blagonic, Emilie LEBRUN, Emmanuel Hesry, Enej Bajgoric, Enrico Sorcinelli, Enrique Piqueras, Enrique Sánchez, Eric, Eric Andrew Lewis, Eric Binnion, Erik Betshammar, Erin \'Folletto\' Casali, esemlabel, esoj, espiat, Estela Rueda, etoledom, etruel, Ev3rywh3re, Evan Mullins, Fabian Kägy, Fabian Todt, Faisal Ahmed, Felix Arntz, Felix Edelmann, ferdiesletering, finomeno, Florian Brinkmann, Florian TIAR, Florian Truchot, florianatwhodunit, FolioVision, Francesca Marano, Francois Thibaud, Frank Goossens, Frank Klein, Frank.Prendergast, Frankie Jarrett, Franz Armas, fullofcaffeine, Gabriel Koen, Gabriel Maldonado, Gabriel Mays, gadgetroid, Gal Baras, Garavani, garethgillman, Garrett Hyder, Gary Cao, Gary Jones, Gary Pendergast, gchtr, Geert De Deckere, Gemini Labs, Gennady Kovshenin, geriux, Giorgio25b, gisselfeldt, glendaviesnz, goldsounds, Goto Hayato, Govind Kumar, Grégory Viguier, gradina, Greg Ziółkowski, gregmulhauser, grierson, Grzegorz.Janoszka, gsmumbo, Guido Scialfa, guidobras, Gunther Pilz, gwwar, H-var, hakre, Halacious, hankthetank, Hapiuc Robert, Hareesh, haukep, Hauwa, Haz, Hector Farahani, Helen Hou-Sandi, Henry Wright, Herre Groen, hlanggo, hommealone, Hoover, Howdy_McGee, Hronak Nahar, huntlyc, Ian Belanger, Ian Dunn, Ian Stewart, ianjvr, ifrins, infinum, Ipstenu (Mika Epstein), Isabel Brison, ishitaka, J.D. Grimes, jackfungi, jacklinkers, Jadon N, jadpm, jagirbahesh, Jake Spurlock, Jake Whiteley, James Koster, James Nylen, Jan Koch, Jan Reilink, Jan Thiel, Janvo Aldred, Jarret, Jason Adams, Jason Coleman, Jason Cosper, Jason Crouse, Jason LeMahieu (MadtownLems), Jason Rouet, JasWSInc, Javier Casares, Jayson Basanes, jbinda, jbouganim, Jean-Baptiste Audras, Jean-David Daviet, Jeff Chandler, Jeff Farthing, Jeff Ong, Jeff Paul, Jen, Jenil Kanani, Jeremy Felt, Jeremy Herve, Jeremy Yip, Jeroen Rotty, jeryj, Jesin A, Jignesh Nakrani, Jim_Panse, Jip Moors, jivanpal, Joe Dolson, Joe Hoyle, Joe McGill, Joen Asmussen, Johanna de Vos, John Blackbourn, John Dorner, John James Jacoby, John P. Green, John Richards II, John Watkins, johnnyb, Jon Quach, Jon Surrell, Jonathan Bossenger, Jonathan Champ, Jonathan Christopher, Jonathan Desrosiers, Jonathan Stegall, jonkolbert, Jonny Harris, jonnybot, Jono Alderson, Joost de Valk, Jorge Bernal, Jorge Costa, Joseph Dickson, Josepha Haden, Josh Smith, JoshuaWold, Joy, Juanfra Aldasoro, juanlopez4691, Jules Colle, julianm, Juliette Reinders Folmer, Julio Potier, Julka Grodel, Justin Ahinon, Justin de Vesine, Justin Tadlock, justlevine, justnorris, K. Adam White, kaggdesign, Kailey (trepmal), Kaira, Kaitlin Bolling, Kalpesh Akabari, KamataRyo, Kantari Samy, Kaspars, Kavya Gokul, keesiemeijer, Kelly Dwan, kennethroberson5556, Kevin Hagerty, Kharis Sulistiyono, Khokan Sardar, kinjaldalwadi, Kiril Zhelyazkov, Kirsty Burgoine, Kishan Jasani, kitchin, Kite, Kjell Reigstad, Knut Sparhell, Konstantin Obenland, Konstantinos Xenos, ksoares, KT Cheung, Kukhyeon Heo, Kyle B. Johnson, lalitpendhare, landau, Laterna Studio, laurelfulford, Laurens Offereins, Laxman Prajapati, Lester Chan, Levdbas, Lew Ayotte, Lex Robinson, linyows, lipathor, Lisa Schuyler, liuhaibin, ljharb, logig, lucasbustamante, luiswill, Luke Cavanagh, Luke Walczak, lukestramasonder, M Asif Rahman, M.K. Safi, Maarten de Boer, Mahfoudh Arous, mailnew2ster, manojlovic, Manuel Schmalstieg, maraki, Marcin Pietrzak, Marcio Zebedeu, Marco Pereirinha, MarcoZ, Marcus, Marcus Kazmierczak, Marek Dědič, Marek Hrabe, Mario Valney, Marius Jensen, Mark Chouinard, Mark Jaquith, Mark Parnell, Mark Uraine, markdubois, markgoho, Marko Andrijasevic, Marko Heijnen, MarkRH, markshep, markusthiel, Martijn van der Kooij, martychc23, Mary Baum, Matheus Martins, Mathieu Viet, Matias Ventura, matjack1, Matt Cromwell, Matt Gibson, Matt Mullenweg, Matt Radford, Matt van Andel, mattchowning, Matthew Boynes, Matthew Eppelsheimer, Matthew Gerring, Matthias Kittsteiner, Matthias Pfefferle, Matthieu Mota, mattyrob, Maxime Culea, Maxime Pertici, maxme, Mayank Majeji, mcshane, Mel Choyce-Dwan, Menaka S., mensmaximus, metalandcoffee, Michael, Michael Arestad, Michael Arestad, Michael Beckwith, Michael Fields, Michael Nelson, Michele Butcher-Jones, Michelle, Miguel Fonseca, mihdan, Miina Sikk, Mikael Korpela, mikaumoto, Mike Crantea, Mike Glendinning, Mike Haydon, Mike Schinkel [WPLib Box project lead], Mike Schroder, Mikey Arce, Milana Cap, Milind More, mimi, mislavjuric, Mohammad Jangda, Mohammad Rockeybul Alam, Mohsin Rasool, Monika Rao, Morgan Kay, Morten Rand-Hendriksen, Morteza Geransayeh, moto hachi ( mt8.biz ), mrgrt, mrmist, mrTall, msaggiorato, Muhammad Usama Masood, Mukesh Panchal, munyagu, Nabil Moqbel, Nadir Seghir, Nahid Ferdous Mohit, Nalini Thakor, Naoko Takano, narwen, Nate Gay, Nathan Rice, Navid, neonkowy, net, netpassprodsr, Nextendweb, Ngan Tengyuen, Nick Daugherty, Nicky Lim, nicolad, Nicolas Juen, NicolasKulka, Nidhi Jain, Niels de Blaauw, Niels Lange, nigro.simone, Nik Tsekouras, Nikhil Bhansi, Nikolay Bachiyski, Nilo Velez, Niresh, nmenescardi, Noah Allen, NumidWasNotAvailable, oakesjosh, obliviousharmony, ockham, Olga Gleckler, Omar Alshaker, Omar Reiss, onokazu, Optimizing Matters, Ov3rfly, ovann86, overclokk, p_enrique, Paal Joachim Romdahl, Pablo Honey, Paddy, palmiak, Paresh Shinde, Parvand, Pascal Birchler, Pascal Casier, Paul Bearne, Paul Biron, Paul Fernhout, Paul Gibbs, Paul Ryan, Paul Schreiber, Paul Stonier, Paul Von Schrottky, pavelevap, Pedro Mendonça, pentatonicfunk, pepe, Peter \"Pessoft\" Kolínek, Peter Westwood, Peter Wilson, Phil Derksen, Phil Johnston, Philip Jackson, Pierre Gordon, pigdog234, pikamander2, pingram, Pionect, Piyush Patel, pkarjala, pkvillanueva, Prashant Baldha, pratik028, Pravin Parmar, Presskopp, Presslabs, Priyank Patel, Priyo Mukul, ProGrafika, programmin, Puneet Sahalot, pvogel2, r-a-y, Raaj Trambadia, Rachel Peter, raine, rajeshsingh520, Ramanan, Rami Yushuvaev, RavanH, Ravat Parmar, ravenswd, rawrly, rebasaurus, Red Sand Media Group, Remy Perona, Remzi Cavdar, Renatho, renggo888, retlehs, retrofox, riaanlom, Riad Benguella, Rian Rietveld, riasat, Rich Tabor, Ringisha, ritterml, Rnaby, Rob Cutmore, Rob Migchels, rob006, Robert Anderson, Robert Chapin, Robert Peake, Robert Windisch, Rodrigo Arias, Ronald Huereca, Rostislav Wolný, Roy Tanck, rtagliento, ruxandra, Ryan Boren, Ryan Fredlund, Ryan Kienstra, Ryan McCue, Ryan Welcher, Ryota Sakamoto, ryotsun, Sören Wrede, Søren Brønsted, Sachit Tandukar, Sagar Jadhav, Sajjad Hossain Sagor, Sal Ferrarello, Salvatore Formisano, salvoaranzulla, Sam Fullalove, Sam Webster, Samir Shah, Samuel Wood (Otto), samueljseay, Sander van Dragt, Sanjeev Aryal, Sanket Mehta, sarahricker, Sathiyamoorthy V, Sayed Taqui, scarolan, scholdstrom, Scott Kingsley Clark, Scott Reilly, Scott Smith, Scott Taylor, scribu, scruffian, Sean Hayes, seanpaulrasmussen, seayou, senatorman, Sergey Biryukov, Sergey Predvoditelev, Sergio de Falco, sergiomdgomes, Shannon Smith, Shantanu Desai, shaunandrews, Shawn Hooper, shawnz, Shital Marakana, shulard, siliconforks, Simon Wheatley, simonjanin, sinatrateam, sjmur, skarabeq, skorasaurus, skoskie, slushman, snapfractalpop, SpearsMarketing, sphakka, squarecandy, sreedoap, Stanimir Stoyanov, Stefano Minoia, Stefanos Togoulidis, Steph Wells, Stephen Bernhardt, Stephen Cronin, Stephen Edgar, Steve Dufresne, stevegibson12, Steven Stern (sterndata), Steven Word, stevenkussmaul, stevenlinx, Stiofan, Subrata Sarkar, SUM1, Sunny, Sunny Ratilal, Sushyant Zavarzadeh, suzylah, Sybre Waaijer, Synchro, Sérgio Estêvão, Takayuki Miyauchi, Tammie Lister, Tang Rufus, TeBenachi, Tessa Watkins LLC, Tetsuaki Hamano, theMikeD, theolg, Thierry Muller, Thimal Wickremage, Thomas M, Thorsten Frommen, Thrijith Thankachan, Tiago Hillebrandt, Till Krüss, Timothy Jacobs, Tkama, tmdesigned, tmoore41, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), Tofandel, tomdude, Tommy Ferry, Tony G, Toro_Unit (Hiroshi Urabe), torres126, Torsten Landsiedel, Toru Miki, Travis Northcutt, treecutter, truongwp, tsimmons, Tung Du, Udit Desai, Ulrich, Vagios Vlachos, valchovski, Valentin Bora, Vayu Robins, veromary, Viktor Szépe, vinkla, virginienacci, Vladimir, Vladislav Abrashev, vortfu, voyager131, vtieu, webaware, Weston Ruter, William Earnhardt, williampatton, Winstina, wittich, wpdesk, WPDO, WPMarmite, wppinar, Yahil Madakiya, yashrs, yoancutillas, Yoav Farhi, yohannp, yuhin, Yui, Yuri Salame, Yvette Sonneveld, Zack Tollman, zaheerahmad, zakkath, Zebulan Stanphill, zieladam, and Česlav Przywara.\n\n\n\n

 

\n\n\n\n

Many thanks to all of the community volunteers who contribute in the support forums. They answer questions from people across the world, whether they are using WordPress for the first time or since the first release. These releases are more successful for their efforts!

\n\n\n\n

Finally, thanks to all the community translators who worked on WordPress 5.5. Their efforts bring WordPress fully translated to 46 languages at release time, with more on the way.

\n\n\n\n

If you want to learn more about volunteering with WordPress, check out Make WordPress or the core development blog.

\n
\n\n\n\n
\n
\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8799\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"WordPress 5.5 Release Candidate 2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"https://wordpress.org/news/2020/08/wordpress-5-5-release-candidate-2/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 04 Aug 2020 19:12:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8764\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:420:\"The second release candidate for WordPress 5.5 is here! WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time! You can test the WordPress 5.5 release candidate in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding edge nightlies” option) Or download the release […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2503:\"\n

The second release candidate for WordPress 5.5 is here!

\n\n\n\n

WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

For a more detailed breakdown of the changes included in WordPress 5.5, check out the WordPress 5.5 beta 1 post. The WordPress 5.5 Field Guide is also out! It’s your source for details on all the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8764\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n\n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"The Month in WordPress: July 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"https://wordpress.org/news/2020/08/the-month-in-wordpress-july-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 03 Aug 2020 13:54:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8755\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:340:\"July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:11539:\"\n

July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Updates

\n\n\n\n

July was full of WordPress 5.5 updates! The WordPress 5.5 Beta 1 came out on July 7, followed by Beta 2 on July 14, Beta 3 on July 21, and Beta 4 on July 27. Subsequently, the team also published the first release candidate of WordPress 5.5 on July 28. 

\n\n\n\n

WordPress 5.5, which is slated for release on August 11, 2020, is a major update with features like automatic updates for plugins and themes, a block directory, XML sitemaps, block patterns, and lazy-loading images, among others. To learn more about the release, check out its field guide post.

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.5 and 8.6

\n\n\n\n

The core team launched Gutenberg 8.5 and 8.6. Version 8.5 – the last plugin release will be included entirely (without experimental features) in WordPress 5.5, introduced improvements to block drag-and-drop and accessibility, easier updates for external images, and support for the block directory. Version 8.6 comes with features like Cover block video position controls and block pattern updates. For full details on the latest versions on these Gutenberg releases, visit these posts about 8.5 and 8.6.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Reimagining Online WordPress Events

\n\n\n\n

The Community team made the difficult decision to suspend in-person WordPress events for the rest of 2020 in light of the COVID-19 pandemic. The team has also started working on reimagining online events. Based on feedback from the community members, the team decided to make changes to the current online WordCamp format. Key changes include wrapping up financial support for A/V vendors, ending event swag support for newer online WordCamps, and suspending the Global Community Sponsorship program for 2020. The team encourages upcoming online WordCamps to experiment with their events to facilitate an effective learning experience for attendees while avoiding online event fatigue. The team is currently working on a proposal to organize community-supported recorded workshops and synchronous discussion groups to help community members learn WordPress.

Want to get involved with the Community team? Follow the Community blog here, or join them in the #community-events channel in the Making WordPress Slack group. To organize a Meetup or WordCamp, visit the handbook page

\n\n\n\n

WordCamp US 2020 is canceled

\n\n\n\n

The organizers of WordCamp US 2020 have canceled the event in light of the continued pandemic and online event fatigue. The flagship event, which was originally scheduled for October 27-29 as an in-person event, had already planned to transition to an online event. Several WCUS Organizers will be working with the WordPress Community team to focus on other formats and ideas for online events, including a 24-hour contributor day, and contributing to the workshops initiative currently being discussed. Matt Mullenweg’s State of the Word (which typically accompanies WordCamp US) is likely to take place in a different format later in 2020.

\n\n\n\n

Plugin and theme updates are now available over zip files

\n\n\n\n

After eleven years, WordPress now allows users to update plugins and themes by uploading a ZIP file, in WordPress 5.5.  The feature, which was merged on July 7, has been one of the most requested features in WordPress. Now, when a user tries to upload a plugin or theme zip file from the WordPress dashboard by clicking the “Install Now” button, WordPress will direct users to a new screen that compares the currently-installed extension with the uploaded versions. Users can then choose between continuing with the installation or canceling. WordPress 5.5 will also offer automatic plugin and theme updates

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n
  • The Block directory is coming to WordPress with the 5.5 release. Plugin authors can now submit their Block plugins to the directory.
  • The Core team has opened up the call for features in the WordPress 5.6 release. You can comment on the post with features that you’d like to be included, current UX pain points, or maintenance tickets that need to be addressed. August 20 is the deadline for feature requests. 
  • Editor features such as the new Navigation block, the navigation screen, and the widget screen that were originally planned to be merged with WordPress 5.5 have been pushed for the next release
  • The Theme team is inviting proposals on whether to allow themes to place an additional top-level menu link in the admin.
  • BuddyPress 6.2 beta is out in the wild, and the team will soon release the stable version. The update includes changes that will make BuddyPress fully compatible with WordPress 5.5.
  • WordCamp EU 2021, which was being planned as an in-person event in Porto, Portugal, is moving online. The team is considering an in-person WordCamp EU in 2022. 
  • The Polyglots team has prepared and finalized a Translation Editor & Locale Manager Vetting Criteria to provide more clarity on how global mentors assign PTE/GTE/Locale Managers and to help locale teams set their own guidelines. The document, which was finalized after a lot of discussion, is now available in the Polyglots handbook.
  • Members of the Community team are discussing whether WordCamp volunteers, WordCamp attendees, or Meetup attendees should be awarded a WordPress.org profile badge. The ongoing discussion will be open for comments until August 13.
  • The WP Notify project, which aims to create a better way to manage and deliver notifications to the relevant audience, is on to its next steps. The team has finalized the initial requirements, and is kicking off the project build.
  • The WordPress documentation team is considering a ban on links to commercial websites in a revision to its external linking policy. The policy change does not remove external links to commercial sites from WordPress.org and only applies to documentation sites. The idea is to protect documentation from being abused, and to prevent the WordPress project from being biased. Discussion on this post is still ongoing, and a decision has not yet been made. Feel free to comment on the discussion posts, if you would like to share your thoughts on the topic.
\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8755\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"WordPress 5.5 Release Candidate\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://wordpress.org/news/2020/07/wordpress-5-5-release-candidate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 28 Jul 2020 19:08:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8732\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:370:\"The first release candidate for WordPress 5.5 is now available! This is an important milestone in the community’s progress toward the final release of WordPress 5.5. “Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2970:\"\n

The first release candidate for WordPress 5.5 is now available!

\n\n\n\n

This is an important milestone in the community’s progress toward the final release of WordPress 5.5.

\n\n\n\n

“Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

What’s in WordPress 5.5?

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developer notes tag for updates on those and other changes that could affect your products.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

The WordPress 5.5 Field Guide, due very shortly, will give you a more detailed dive into the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8732\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 4\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-4/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 27 Jul 2020 20:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8719\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:313:\"WordPress 5.5 Beta 4 is now available! This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 4 in two ways: Try the WordPress Beta Tester plugin (choose the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"David Baumwald\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3812:\"\n

WordPress 5.5 Beta 4 is now available!

\n\n\n\n

This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 4 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 3 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 3, 43 bugs have been fixed. Here are a few changes in beta 4:

\n\n\n\n
  • Add \"loading\" as an allowed kses image attribute (see #50731).
  • Add filter for the plugin/theme auto-update message in the Info tab of Site health (see #50663).
  • $_SERVER[\'SERVER_NAME\'] not a reliable when generating email host names (see #25239)
  • Several backported fixes from Gutenberg are included in WordPress 5.5 Beta 4 (See PR #24218)
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8719\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 3\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-3/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 21 Jul 2020 17:51:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8706\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:324:\"WordPress 5.5 Beta 3 is now available! This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 3 in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3876:\"\n

WordPress 5.5 Beta 3 is now available!

\n\n\n\n

This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 3 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 2 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 2, 43 bugs have been fixed. Here are a few changes in beta 3:

\n\n\n\n
  • Plugin and theme versions are now shared in the emails when automatically updated (see #50350).
  • REST API routes without a permission_callback now trigger a _doing_it_wrong() warning (see #50075).
  • Over 23 Gutenberg changes and updates (see #24068 and #50712).
  • A bug with the new import and export database Dashicons has been fixed (see #49913).
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8706\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}s:27:\"http://www.w3.org/2005/Atom\";a:1:{s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:4:\"href\";s:32:\"https://wordpress.org/news/feed/\";s:3:\"rel\";s:4:\"self\";s:4:\"type\";s:19:\"application/rss+xml\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:44:\"http://purl.org/rss/1.0/modules/syndication/\";a:2:{s:12:\"updatePeriod\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"\n hourly \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:15:\"updateFrequency\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"\n 1 \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:4:\"site\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"14607090\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:9:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:30 GMT\";s:12:\"content-type\";s:34:\"application/rss+xml; charset=UTF-8\";s:25:\"strict-transport-security\";s:11:\"max-age=360\";s:6:\"x-olaf\";s:3:\"⛄\";s:13:\"last-modified\";s:29:\"Wed, 21 Oct 2020 20:10:31 GMT\";s:4:\"link\";s:63:\"; rel=\"https://api.w.org/\"\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(129,'_transient_timeout_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(130,'_transient_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603384291','no'),(131,'_transient_timeout_feed_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(132,'_transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:16:\"WordPress Planet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"en\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WordPress Planet - http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:50:{i:0;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Loginizer Plugin Gets Forced Security Update for Vulnerabilities Affecting 1 Million Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106557\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users?utm_source=rss&utm_medium=rss&utm_campaign=loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5484:\"

WordPress.org has pushed out a forced security update for the Loginizer plugin, which is active on more than 1 million websites. The plugin offers brute force protection in its free version, along with other security features like two-factor auth, reCAPTCHA, and PasswordLess login in its commercial upgrade.

\n\n\n\n

Last week security researcher Slavco Mihajloski discovered an unauthenticated SQL injection vulnerability, and an XSS vulnerability, that he disclosed to the plugin’s authors. Loginizer version 1.6.4 was released on October 16, 2020, with patches for the two issues, summarized on the plugin’s blog:

\n\n\n\n

1) [Security Fix] : A properly crafted username used to login could lead to SQL injection. This has been fixed by using the prepare function in PHP which prepares the SQL query for safe execution.

2) [Security Fix] : If the IP HTTP header was modified to have a null byte it could lead to stored XSS. This has been fixed by properly sanitizing the IP HTTP header before using the same.

\n\n\n\n

Loginizer did not disclose the vulnerability until today in order to give users the time to upgrade. Given the severity of the vulnerability, the plugins team at WordPress.org pushed out the security update to all sites running Loginizer on WordPress 3.7+.

\n\n\n\n

In July, 2020, Loginizer was acquired by Softaculous, so the company was also able to automatically upgrade any WordPress installations with Loginizer that had been created using Softaculous. This effort, combined with the updates from WordPress.org, covered a large portion of Loginizer’s user base.

\n\n\n\n
\n

Any #WordPress install with @loginizer probably isn\'t using another WAF solution. As you can notice from the graph 600k+500k active installs were updated upside down, so … Preauth SQLi in it, reported by me. Update! Crunching write up :) https://t.co/gkEVWun9wt pic.twitter.com/XWXVMYO1ED

— mslavco (@mslavco) October 19, 2020
\n
\n\n\n\n

The automatic update took some of the plugin’s users by surprise, since they had not initiated it themselves and had not activated automatic updates for plugins. After several users posted on the plugin’s support forum, plugin team member Samuel Wood said that “WordPress.org has the ability to turn on auto-updates for security issues in plugins” and has used this capability many times.

\n\n\n\n

Mihajloski published a more detailed proof-of-concept on his blog earlier today. He also highlighted some concerns regarding the systems WordPress has in place that allowed this kind of of severe vulnerability to slip through the cracks. He claims the issue could have easily been prevented by the plugin review team since the plugin wasn’t using the prepare function for safe execution of SQL queries. Mihajloski also recommended recurring code audits for plugins in the official directory.

\n\n\n\n

“When a plugin gets into the repository, it must be reviewed, but when is it reviewed again?” Mihajloski said. “Everyone starts with 0 active installs, but what happens on 1k, 10k, 50k, 100k, 500k, 1mil+ active installs?”

\n\n\n\n

Mihajloski was at puzzled how such a glaring security issue could remain in the plugin’s code so long, given that it is a security plugin with an active install count that is more than many well known CMS’s. The plugin also recently changed hands when it was acquired by Softaculus and had been audited by multiple security organizations, including WPSec and Dewhurst Security.

\n\n\n\n

Mihajloski also recommends that WordPress improve the transparency around security, as some site owners and closed communities may not be comfortable with having their assets administered by unknown people at WordPress.org.

\n\n\n\n

“WordPress.org in general is transparent, but there isn’t any statement or document about who, how and when decides about and performs automatic updates,” Mihajloski said. “It is kind of [like] holding all your eggs in one basket.

\n\n\n\n

“I think those are the crucial points that WP.org should focus on and everything will came into place in a short time: complete WordPress tech documentation for security warnings, a guide for disclosure of the bugs (from researchers, but also from a vendor aspect), and recurring code audits for popular plugins.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 22 Oct 2020 03:47:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"Post Status: Joe Casabona on creating quality content and courses\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=80099\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"https://poststatus.com/joe-casabona-on-creating-quality-content-and-courses/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1407:\"

David Bisset interviews Joe Casabona, an independent creator and teacher, and discusses what it\'s like to be a creator as his job, plus some news topics.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Sandhills Development

\n\n\n\n

Sandhills Development crafts ingenuity, developed with care:

\n\n\n\n
  • Easy Digital Downloads – Sell digital products with WordPress
  • AffiliateWP – A full-featured affiliate marketing solution
  • Sugar Calendar – WordPress event management made simple
  • WP Simple Pay – A lightweight Stripe payments plugin
\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:17:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: MakeStories 2.0 Launches Editor for WordPress, Rivaling Google’s Official Web Stories Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106327\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin?utm_source=rss&utm_medium=rss&utm_campaign=makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8860:\"Recipe slide from the MakeStories WordPress plugin.\n\n\n\n

Earlier today, MakeStories launched version 2.0 of its plugin for creating Web Stories with WordPress. In many ways, this is a new plugin launch. The previous version simply allowed users to connect their WordPress installs to the MakeStories site. With the new version, users can build and edit their stories directly from the WordPress admin.

\n\n\n\n

Version 2.0 of the plugin still requires an account and a connection with the MakeStories.io website. However, it is simple to set up. Users can log in without leaving their WordPress admin interface. This API connection means that user-created Stories are stored on the MakeStories servers. If an end-user wanted to jump platforms from WordPress to something else, this would allow them to take their Stories with them.

\n\n\n\n

“One of the things we would like to assure is your content is still yours, and none of the user data is being consumed or analyzed by us,” said Pratik Ghela, the founder and product manager at MakeStories. “We only take enough data to help serve you better.”

\n\n\n\n

The plugin is a competing solution to the official Web Stories plugin by Google. While the two share similarities in the final output (they are built to utilize the same front-end format for creating Stories on the web), they take different paths to get there.

\n\n\n\n

The two share similarities on the backend too. However, MakeStories may be more polished in some areas. For example, it allows users to zoom in on the small canvas area. Having the ability to reorder slides from the grid view also feels more intuitive.

\n\n\n\n

“The main unique selling proposition of our plugin is that it comes with a guarantee of the MakeStories team,” said Ghela. “We as a team have been building this for over two years, and we are proud to be one of the tools that has stood the test of time, and competition and is still growing at a very fast pace.”

\n\n\n\n

The team also wants to make the Story-creating process faster, safer, and rewarding. The goal is to cater to designers, developers, and content creators. Ghela also feels like his team’s support turnaround time of a few hours will be the key to success and is a good reason for users to give this plugin a try before settling on something else.

\n\n\n\n

“We feel that our goal is to see Web Stories flourish,” he said. “And we may have different types of users looking out for various options. So, the official plugin from Google and the one from MakeStories at least opens up the options for users to choose from. And we feel that the folks at Google are also building a great editor, and, at the end of the day, it’s up to the user to select what they feel is the best.

\n\n\n\n

Technically, MakeStories is a SaaS (software as a service) product. Even though it is a free plugin, there will eventually be a commercial component to it. Currently, it is free at least until the first quarter of 2021, which may be extended based on various factors. There is no word on what pricing tiers may be available after that.

\n\n\n\n

“There will always be a free tier, and we have always stood for it that your data belongs to you,” said Ghela. “In case you do not like the pricing, we will personally assist you to port out from using our editor and still keep the data and everything totally intact.”

\n\n\n\n

Diving Into the Plugin

\n\n\n\nStory management screen.\n\n\n\n

MakeStories is a drag-and-drop editor for building Web Stories. It works and feels much like typical design editors like Gimp or Photoshop. It shares similarities with QuarkXPress or InDesign, for those familiar with page layout programs. In some ways, it feels a lot like a light-colored version of Google’s Web Stories plugin with more features and a slightly more intuitive interface.

\n\n\n\n

The end goal is simple: create a Story through designing slides/pages that site visitors will click through as the narrative unfolds.

\n\n\n\n

The plugin provides a plethora of shapes, textures, and animations. These features are easy to find and implement. It also includes free access to images, GIFs, and videos. These are made possible via API integrations with Unsplash, Tenor, and Pexels.

\n\n\n\n

MakeStories includes access to 10 templates at the moment. However, what makes this feature stand out is that end-users can create and save custom templates for reuse down the road.

\n\n\n\nEditing a Story from a predesigned template.\n\n\n\n

One of the more interesting, almost hidden, features is the available text patterns. The plugin allows users to insert these patterns from a couple of dozen choices. This makes it easier to visualize a design without having to build everything from scratch.

\n\n\n\nInserting a text pattern and adjusting its size.\n\n\n\n

While the editing process is a carefully-crafted experience that makes the plugin worth a look, it is the actual publishing aspect of the workflow that is a bit painful. Traditional publishing in WordPress means hitting the “publish” button to make content live. This is not the case with the MakeStories plugin. It takes you through a four-step process of entering various publisher details, setting up metadata and SEO, validating the Story content, and analytics. It is not that these steps are necessarily bad. For example, MakeStories lets you know when images are missing alt text, which is needed information screen readers. The problem is that it feels out of place to go through all of these details when I, as a user, simply want my content published. And, many of these details, such as the publisher (author), should be automatically filled in.

\n\n\n\n

Updating a Story is not as simple as hitting an “update” button either. The system needs to run through some of the same steps too.

\n\n\n\n

Ghela said the publishing process might be a bit tough but will prove fruitful in the end. The plugin takes care of the technical aspects of adding title tags, meta, and other data on the front end after the user fills in the form fields.

\n\n\n\n

“We will definitely work on improving the flow as the community evolves and improve it a lot to be easier, faster, and, most importantly, still very customizable,” he said.

\n\n\n\n

The MakeStories team has no plans of stopping at its current point on the roadmap. Ghela sounded excited about some of the upcoming additions they are planning, including features like teams, branding, easy template customization, polls, and quizzes.

\n\n\n\n

On the Web Stories Format

\n\n\n\nUN report on COVID-19 and poverty published with MakeStories.\n\n\n\n

Many will ultimately hesitate to use any plugin that implements Web Stories given Google’s history of dropping projects. There is also a feeling that the format is a bit of a fad and will not stand the test of time.

\n\n\n\n

“We greatly believe in AMP and Web Stories as a content format,” said Ghela. “We, as an agency, have been involved a lot in AMP and have done a lot of experiments with it, including a totally custom WooCommerce site in fully-native, valid AMP with support for variable products, subscriptions, and other functionalities.”

\n\n\n\n

The company is all-in on the format and feels like it will be around for the long term, particularly if there is a good ecosystem around monetization.

\n\n\n\n

“We think that the initial reactions are because there are not enough proven results and because we never imagined the story format to come to the web,” said Ghela. “There were definitely plugins that did this. Few folks tried to build stories using good ol’ HTML, CSS, and JavaScript. But, the performance and UX were not that great. On the other hand, the engineers at the AMP Team are making sure that everything is just perfect. The UX, load time, WCV Score, just everything.”

\n\n\n\n

He feels that some of the early criticisms are unwarranted and that the web development community should give the format a try and provide feedback.

\n\n\n\n

“The more data we all get, actually gives the AMP team a clear idea of what’s needed, and they can design the roadmap accordingly,” he said. “So, just giving out early reactions won’t help, but constructive criticism and getting back to the AMP team with what you are doing will.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:12:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"WordPress.org blog: WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7956:\"

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"WPTavern: WordPress 5.6 Release Team Pulls the Plug on Block-Based Widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106466\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:193:\"https://wptavern.com/wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8762:\"Current block-based widgets admin screen design.\n\n\n\n

I was wrong. I assured our readers that “the block-based widget system will be ready for prime time when WordPress 5.6 lands” in my previous post on the new feature’s readiness. I also said that was on the condition of not trying to make it work with the customizer — that experience was still broken. However, the 5.6 team pulled the plug on block-based widgets for the second time this year.

\n\n\n\n

One week ago, WordPress 5.6 release lead Josepha Haden seemed to agree that it would be ready. However, things can change quickly in a development cycle, and tough decisions have to be made with beta release deadlines.

\n\n\n\n

This is not the first feature the team has punted to a future release. Two weeks ago, they dropped block-based nav menus from the 5.6 feature list. Both features were originally planned for WordPress 5.5.

\n\n\n\n

A new Widgets admin screen has been under development since January 2019, which was not long after the initial launch of the block editor in WordPress 5.0. For now, the block-based widgets feature has been punted to WordPress 5.7. It has also been given the “early” tag, which means it should go into core WordPress soon after the 5.7 release cycle begins. This will give it more time to mature and more people an opportunity to test it.

\n\n\n\n

Helen Hou-Sandì, the core tech lead for 5.6, provided a historical account of the decision and why it was not ready for inclusion in the new ticket:

\n\n\n\n

My question for features that affect the front-end is “can I try out this new thing without the penalty of messing up my site?” — that is, user trust. At this current moment, given that widget areas are not displayed anything like what you see on your site without themes really putting effort into it and that you have to save your changes live without revisions to get an actual contextual view, widget area blocks do not allow you to try this new feature without penalizing you for experimenting.

\n\n\n\n

She went on to say that the current experience is subpar at the moment. Problems related to the customizer experience, which I covered in detail over a month ago, were also mentioned.

\n\n\n\n

“So, when we come back to this again, let’s keep sight of what it means to keep users feeling secure that they can get their site looking the way they want with WordPress, and not like they are having to work around what we’ve given them,” said Hou-Sandì.

\n\n\n\n

This is a hopeful outlook despite the tough decision. Sometimes, these types of calls need to be made for the good of the project in the long term. Pushing back a feature to a future version for a better user experience can be better than launching early with a subpar experience.

\n\n\n\n

“The good part of this is that now widgets can continue to be ‘re-imagined’ for 5.7, and get even more enhancements,” said lead WordPress developer Andrew Ozz in the ticket. “Not sure how many people have tested this for a bit longer but having blocks in the widgets areas (a.k.a. sidebars) opens up many new possibilities and makes a lot of the old, limited widgets obsolete. The ‘widget areas’ become something like ‘specialized posts with more dynamic content,’ letting users (and designers) do a lot of stuff that was either hard or impossible with the old widgets.”

\n\n\n\n

After the letdown of seeing one of my most anticipated features of 5.6 being dropped, it is encouraging to see the positive outlook from community leaders on the project.

\n\n\n\n

“You know, I was really hopeful for it too, and that last-minute call was one I labored over,” said Haden. “When I last looked, it did seem close to ready, but then more focused testing was done and there were some interactions that are a little rough for users. I’m grateful for that because the time to discover painful user experiences is before launch rather than after!”

\n\n\n\n

Despite dropping its second major feature, WordPress 5.6 still has some big highlights that will be shipping in less than two months. The new Twenty Twenty-One theme looks to be a breath of fresh air and will explore block-related features not seen in previous default themes. Haden also pointed out auto-updates for major releases, application passwords support for the REST API, and accessibility improvements as features to look forward to.

\n\n\n\n

WordPress 5.6 Beta 1 is expected to ship today.

\n\n\n\n

Adding New Features To an Old Project

\n\n\n\n

At times, it feels like the Gutenberg project has bitten off more than it can chew. Many of the big feature plans continually miss projections. Between full-site editing, global styles, widgets, nav menus, and much more, it is tough to get hyper-focused on one feature and have it ready to ship. On the other hand, too much focus one way can be to the detriment to other features in the long run. All of these pieces must eventually come together to create a more cohesive whole.

\n\n\n\n

WordPress is also 17 years old. Any new feature could affect legacy features or code. The goal for block-based widgets is to transition an existing feature to work within a new system without breaking millions of websites in the process. Twenty-one months of work on a single feature shows that it is not an easy problem to solve.

\n\n\n\n

“You are so right about complex engineering problems!” said Haden. “We are now at a point in the history of the project where connecting all of the pieces can have us facing unforeseen complications.”

\n\n\n\n

The project also needs to think about how it can address some of the issues it has faced with not quite getting major features to completion. Is the team stretched too thin to focus on all the parts? Are there areas we can improve to push features forward?

\n\n\n\n

“There will be a retrospective where we can identify what parts of our process can be improved in the future, but I also feel like setting stretch goals is good for any software project,” said Haden. “Many contributors have a sense of urgency around bringing the power of blocks to more spaces in WordPress, which I share, but when it’s time to ship, we have to balance that with our deep commitment to usability.”

\n\n\n\n

One problem that has become increasingly obvious is that front-end editing has become tougher over the years. Currently, widgets and nav menus can be edited in two places in WordPress with wildly different interfaces. Full-site editing stands to add an entirely new interface to the mix.

\n\n\n\n

“I think one of the problems that we’re trying to solve with Gutenberg has always been a more consistent experience for editing elements across the WordPress interface,” said Haden. “No user should have to learn five different workflows to make sure their page looks the way they imagined it when it’s published.”

\n\n\n\n

In the meantime, which may be numbered in years, end-users will likely have these multiple interfaces to deal with — overlap while new features are being developed. This may simply be a necessary growing pain of an aging project, one that is trying to lead the pack of hungry competitors in the CMS space.

\n\n\n\n

“There’s a lot of interest in reducing the number of workflows, and I’m hopeful that we can consolidate down to just one beautiful, intuitive interface,” said Haden.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 21:16:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce Tests New Instagram Shopping Checkout Feature, Now in Closed Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106398\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2878:\"

Instagram’s checkout feature, which allows users to purchase products without leaving the app, has become an even more important part of Facebook’s long-term investment in e-commerce now that the pandemic has so heavily skewed consumer behavior towards online shopping. When Instagram introduced checkout in 2019, it reported that 130 million users were tapping to reveal product tags in shopping posts every month.

\n\n\n\nimage credit: Instagram\n\n\n\n

Business owners who operate an existing store can extend their audience to Instagram by funneling orders from the social network into their own stores, without shoppers having to leave Instagram. Checkout supports integration with several e-commerce platform partners, including Shopify and BigCommerce, and will soon be available for WooCommerce merchants.

\n\n\n\n

WooCommerce is testing a new Instagram Shopping Checkout feature for its Facebook for WooCommerce plugin. The free extension is used on more than 900,000 websites and will provide the bridge for store owners who want to tap into Instagram’s market. The checkout capabilities are currently in closed beta. Anyone interested to test the feature can sign up for consideration. Businesses registered in the USA that meet certain other requirements may be selected to participate, and the beta is also expanding to other regions soon.

\n\n\n\n

WooCommerce currently supports shoppable posts, which are essentially products sourced from a product catalog created on Facebook that are then linked to the live store through an Instagram business account. Instagram’s checkout takes it one step further to provide a native checkout experience inside the app. Merchants pay no selling fees until December 31, 2020. After that time, the fee is 5% per shipment or a flat fee of $0.40 for shipments of $8.00 or less. 

\n\n\n\n

On the customer side, shoppers only have to enter their information once and thereafter it is stored for future Instagram purchases. Instagram also pushes shipment and delivery notifications inside the app. Store owners will need to weigh whether the convenience of the in-app checkout experience is worth forking over 5% to Facebook, or if they prefer funneling users over to the live store instead.

\n\n\n\n

Instagram Shopping Checkout is coming to WooCommerce in the near future but the company has not yet announced a launch date, as the feature is just now entering closed beta.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 04:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Past Twenty* WordPress Themes To Get New Block Patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106396\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/past-twenty-wordpress-themes-to-get-new-block-patterns?utm_source=rss&utm_medium=rss&utm_campaign=past-twenty-wordpress-themes-to-get-new-block-patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6608:\"

Mel Choyce-Dwan, the Default Theme Design Lead for WordPress 5.6, kick-started 10 tickets around two months ago that would bring new features to the old default WordPress themes. The proposal is to add unique block patterns, a feature added to WordPress 5.5, to all of the previous 10 Twenty* themes. It is a lofty goal that could breathe some new life into old work from the previous decade.

\n\n\n\n

Currently, only the last four themes are marked for an update by the time WordPress 5.6 lands. Previous themes are on the list to receive their block patterns in a future release. For developers and designers interested in getting involved, the following is a list of the Trac tickets for each theme:

\n\n\n\n\n\n\n\n

If you are wondering where Twenty Eighteen is in that list, that theme does not actually exist. It is the one missing year the WordPress community has had since the one-default-theme-per-year era began with Twenty Ten. It is easy to forget that we did not get a new theme for the 2017-2018 season. With all that has happened in the world this year, we should count ourselves fortunate to see a new default theme land for WordPress this December. WordPress updates and its upcoming default theme are at least one consistency that we have had in an otherwise chaotic time.

\n\n\n\n

More than anything, it is nice to see some work going toward older themes — not just in terms of bug fixes but feature updates. The older defaults are still a part of the face of WordPress. Twenty Twenty and Twenty Seventeen each have over one million active installs. Twenty Nineteen has over half a million. The other default themes also have significant user bases in the hundreds of thousands — still some of the most-used themes in the directory. We owe it to those themes’ users to keep them fresh, at least as long as they maintain such levels of popularity.

\n\n\n\n

This is where the massive theme development community could pitch in. Do some testing of the existing patches. Write some code for missing patterns or introduce new ideas. This is the sort of low-hanging fruit that almost anyone could take some time to help with.

\n\n\n\n

First Look at the New Patterns

\n\n\n\n

None of the proposed patterns have landed in trunk, the development version of WordPress, yet. However, several people have created mockups or added patches that could be committed soon.

\n\n\n\n

One of my favorite patterns to emerge thus far is from Beatriz Fialho for the Twenty Nineteen theme. Fialho has created many of the pattern designs proposed thus far, but this one, in particular, stands out the most. It is a simple two-column, two-row pattern with a circular image, heading, and paragraph for each section. Its simplicity fits in well with the more elegant, business-friendly look of the Twenty Nineteen theme.

\n\n\n\nServices pattern for Twenty Nineteen.\n\n\n\n

It is also fitting that Twenty Nineteen get a nice refresh with new patterns because it was the default theme to launch with the block editor. Ideally, it would continually be updated to showcase block-related features.

\n\n\n\n

While many people will focus on some of the more recent default themes, perhaps the most interesting one is a bit more dated. Twenty Thirteen was meant to showcase the new post formats feature in WordPress 3.6. According to Joen Asmussen, the theme’s primary designer, the original idea was for users to compose a ribbon of alternating colors as each post varied its colors.

\n\n\n\n

“The alternating ribbon of colors did not really come to pass because post formats were simply not used enough to create an interesting ribbon,” he wrote in the Twenty Thirteen ticket. “However, perhaps for block patterns, we have an opportunity to revisit those alternating ribbons of colors. In other words, I’d love to see those warm bold colors used in big swathes that take up the whole pattern background.”

\n\n\n\n
Patterns designed to match post formats.\n\n\n\n

This could be a fun update for end-users who are still using that feature that shall not be named post formats.

\n\n\n\n

There is a lot to like about many of the pattern mockups so far. I look forward to seeing what lands along with WordPress 5.6 and in future updates.

\n\n\n\n

Establishing Pattern Category Standard

\n\n\n\n

With the more recent Twenty Twenty-One theme’s block patterns and the new patterns being added to some of the older default themes, it looks like a specific pattern category naming scheme is starting to become a standard. Of the patches thus far, each is creating a new pattern category named after the theme itself.

\n\n\n\n

This makes sense. Allowing users to find all of their theme’s patterns in one location means that they can differentiate between them and those from core or other plugins. Third-party theme authors should follow suit and stick with this convention for the same reason.

\n\n\n\n

Developers can also define multiple categories for a single pattern. This allows theme authors to create a category that houses all of their patterns in one location. However, they can also split them into more appropriate context-specific categories for discoverability.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 19 Oct 2020 21:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"BuddyPress: BuddyPress 7.0.0-beta1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://buddypress.org/?p=315150\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://buddypress.org/2020/10/buddypress-7-0-0-beta1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4332:\"

BuddyPress 7.0.0-beta1 is now available for testing!

\n\n\n\n

Please note the plugin is still in development, so we recommend running this beta release on a testing site.

\n\n\n\n

You can test BuddyPress 7.0.0-beta1 in 4 ways :

\n\n\n\n\n\n\n\n

The 7.0.0 stable release is slated to the beginning of December, and we’d love you to give us a hand to get there!

\n\n\n\n

Please note BuddyPress 7.0.0 will require at least WordPress 4.9.

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute. Here are some of the big changes and features to pay close attention to while testing (Check out this report on Trac for the full list).

\n\n\n\n
\n\n\n\n

New Administration screens to manage BuddyPress types

\n\n\n\n

In BuddyPress 7.0.0 site administrators will be able to add, edit or delete Member & Group types using their WordPress Administration Screens just like they would do for Post tags.

\n\n\n\n

Read this development note to learn more about it.

\n\n\n\n
\n\n\n\n

Let’s welcome 3 new BP Blocks into our Block Editor

\n\n\n\n
  • The Activity Embed block let authors embed an activity into their post or page.
  • Use the BP Members block to select community users you want to feature into a post or a page.
  • Enjoy the BP Groups block to pick the groups you want to highlight into a post or a page.
\n\n\n\n

Get to know these new blocks reading this development note.

\n\n\n\n
\n\n\n\n

Improved support for WP CLI

\n\n\n\n

WP-CLI is the command-line interface for WordPress. You can update plugins, configure multisite installs, and much more, without using a web browser. In 7.0.0, you will be able to Enjoy new BuddyPress CLI commands to manage BuddyPress Group Meta, BuddyPress Activity Meta, activate or deactivate the BuddyPress signup feature and create BuddyPress specific testing code for plugins.

\n\n\n\n

Discover more about it from this development note.

\n\n\n\n
\n\n\n\n

And so much more such as improvements to the BP REST API, our Template pack, images and iframes lazy loading support…

\n\n\n\n
\n\n\n\n

How You Can Help

\n\n\n\n

Do you speak a language other than English? Help us translate BuddyPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, file one on BuddyPress Trac.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 22:30:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:12:\"Mathieu Viet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:89:\"WPTavern: Using the Web Stories for WordPress Plugin? You Better Play By Google’s Rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105848\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:215:\"https://wptavern.com/using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules?utm_source=rss&utm_medium=rss&utm_campaign=using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4080:\"Web Stories dashboard screen in WordPress.\n\n\n\n

What comes as a surprise to few, Google has updated its content guidelines for its Web Stories format. For users of its recently-released Web Stories for WordPress plugin, they will want to follow the extended rules for their Stories to appear in the “richer experiences” across Google’s services. This includes the grid view on Search, Google Images, and Google Discover’s carousel.

\n\n\n\n

Google released its Web Stories plugin in late September to the WordPress community. It is a drag-and-drop editor that allows end-users to create custom Stories from a custom screen in their WordPress admin.

\n\n\n\n
Visual Stories on Search.
\n\n\n\n

The plugin does not directly link to Google’s content guidelines anywhere. For users who do not do a little digging, they may be caught unaware if their stories are not surfaced in various Google services.

\n\n\n\n

On top of the Discover and Webmaster guidelines, Web Stories have six additional restrictions related to the following:

\n\n\n\n
  • Copyrighted content
  • Text-heavy Web Stories
  • Low-quality assets
  • Lack of narrative
  • Incomplete stories
  • Overly commercial
\n\n\n\n

While not using copyrighted content is one of those reasonably-obvious guidelines, the others could trip up some users. Because Stories are meant to represent bite-sized bits of information on each page, they may become ineligible if most pages have more than 180 words of text. Videos should also be limited to fewer than 60 seconds on each page.

\n\n\n\n

Low-quality media could be a flag for Stories too. Google’s guidelines point toward “stretched out or pixelated” media that negatively impacts the reader’s experience. They do not offer any specific resolution guidelines, but this should mostly be a non-issue today. The opposite issue is far more likely — users uploading media that is too large and not optimized for viewing on the web.

\n\n\n\n

The “lack of narrative” guideline is perhaps the vaguest, and it is unclear how Google will monitor or police narrative. However, the Stories format is about storytelling.

\n\n\n\n

“Stories are the key here imo,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Google specifically states that Stories need a “binding theme or narrative structure” from one page to the next. Essentially, the company is telling users to use the format for the purpose it was created for. They also do not want users to create incomplete stories where readers must click a link to finish the Story or get information.

\n\n\n\nCNN’s Web Story on Remembering John Lennon.\n\n\n\n

Overly commercial Stories are frowned upon too. While Google will allow affiliate marketing links, they should be restricted to a minor part of the experience.

\n\n\n\n

Closing his Twitter thread, Marsland seemed to hit the point. “I’ve seen some initial Google Web Stories where the platform is being used as a replacement for a brochure or website,” he wrote. “In my view that’s a huge missed opportunity. If I was advising brands I would say ‘Tell Stories’ this is a platform for Story Telling.”

\n\n\n\n

If users of the plugin follow this advice, their Stories should surface on Google’s rich search experiences.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 20:51:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:45:\"WPTavern: Stripe Acquires Paystack for $200M+\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106269\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:131:\"https://wptavern.com/stripe-acquires-paystack-for-200m?utm_source=rss&utm_medium=rss&utm_campaign=stripe-acquires-paystack-for-200m\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3196:\"

The big news in the world of e-commerce today is Stripe’s acquisition of Paystack, a Nigeria-based payments system that is widely used throughout African markets. The company, which became informally known as “the Stripe of Africa” picked up $8 million in Series A funding in 2018, led by Stripe, Y Combinator, and Tencent. Paystack has grown to power more than 60,000 businesses, including FedEx, UPS, MTN, the Lagos Internal Revenue Service, and AXA Mansar.

\n\n\n\n

Stripe’s acquisition of the company is rumored to be more than $200M, a small price to pay for a foothold in emerging African markets. In the company’s announcement, Stripe noted that African online commerce is growing 21% year-over-year, 75% faster than the global average. Paystack dominates among payment systems, accounting for more than half of all online transactions in Nigeria.

\n\n\n\n

“In just five years, Paystack has done what many companies could not achieve in decades,” Stripe EMEA business lead Matt Henderson said. “Their tech-first approach, values, and ambition greatly align with our own. This acquisition will give Paystack resources to develop new products, support more businesses and consolidate the hyper-fragmented African payments market.”

\n\n\n\n

Long term, Stripe plans to embed Paystack’s capabilities in its Global Payments and Treasury Network (GPTN), the company’s programmable infrastructure for global money movement.

\n\n\n\n

“Paystack merchants and partners can look forward to more payment channels, more tools, accelerated geographic expansion, and deeper integrations with global platforms,” Paystack CEO and co-founder Shola Akinlade said. He also assured customers that there’s no need to make any changes to their technical integrations, as Paystack will continue expanding and operating independently in Africa.

\n\n\n\n

Paystack is used as a payment gateway for thousands of WordPress-powered stores through plugins for WooCommerce, Easy Digital Downloads, Paid Membership Pro, Give, Contact Form 7, and an assortment of booking plugins. The company has an official WordPress plugin, Payment Forms for Paystack, which is active on more than 6,000 sites, but most users come through the Paystack WooCommerce Payment Gateway (20,000+ active installations).

\n\n\n\n

Stripe’s acquisition was a bit of positive news during what is currently a turbulent time in Nigeria, as citizens are actively engaged in peaceful protests to end police brutality. Paystack’s journey is an encouraging example of the flourishing Nigerian tech ecosystem and the possibilities available for smaller e-commerce companies that are solving problems and removing barriers for businesses in emerging markets.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 22:26:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:10;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WPTavern: Diving Into the Book Review Block Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106273\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:145:\"https://wptavern.com/diving-into-the-book-review-block-plugin?utm_source=rss&utm_medium=rss&utm_campaign=diving-into-the-book-review-block-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6791:\"

Created by Donna Peplinskie, a Product Wrangler at Automattic, the Book Review Block plugin is nearly three years old. However, it only came to my attention during a recent excursion to find interesting block plugins.

\n\n\n\n

The plugin does pretty much what it says on the cover. It is designed to review books. It generally has all the fields users might need to add to their reviews, such as a title, author, image, rating, and more. The interesting thing is that it can automatically fill in those details with a simple ISBN value. Plus, it supports Schema markup, which may help with SEO.

\n\n\n\n

Rain or shine, sick or well, I read every day. I am currently a month and a half shy of a two-year reading streak. When the mood strikes, I even venture to write a book review. As much as I want to share interesting WordPress projects with the community, I sometimes have personal motives for testing and writing about plugins like Book Review Block. Anything that might help me or other avid readers share our thoughts on the world of literature with others is of interest.

\n\n\n\n

Admittedly, I was excited as I plugged in the ISBN for Rhthym of War, the upcoming fourth book of my favorite fantasy series of all time, The Stormlight Archive. I merely needed to click the “Get Book Details” button.

\n\n\n\n

Success! The plugin worked its magic and pulled in the necessary information. It had my favorite author’s name, the publisher, the upcoming release date, and the page count. It even had a long description, which I could trim down in the editor.

\n\n\n\nDefault output of the Book Review block.\n\n\n\n

There was a little work to make this happen before the success. To automatically pull in the book details, end-users must have an API Key from Google. It took me around a minute to set that up and enter it into the field available in the block options sidebar. The great thing about the plugin is that it saves this key so that users do not have to enter each time they want to review a book.

\n\n\n\n

Book Review Block a good starting point. It is straightforward and simple to use. It is not yet at a point where I would call it a great plugin. However, it could be.

\n\n\n\n

Falling Short

\n\n\n\n

The plugin’s Book Review block should be taking its cues from the core Media & Text block. When you get right down to it, the two are essentially doing the same thing visually. Both are blocks with an image and some content sitting next to each other.

\n\n\n\n

The following is a list of items where it should be following core’s lead:

\n\n\n\n
  • No way to edit alt text (book title is automatically used).
  • The image is always aligned left and the content to the right with no way to flip them.
  • The media and content are not stackable on mobile views.
  • Cannot adjust the size of the image or content columns.
  • While inline rich-text controls are supported, users cannot add Heading, List, or Paragraph blocks to the content area and use their associated block options.
\n\n\n\n

That is the shortlist that could offer some quick improvements to the user experience. Ultimately, the problems with the plugin essentially come down to not offering a way to customize the output.

\n\n\n\n

One of the other consistent problems is that the book image the plugin loads is always a bit small. This seems to be more of an issue from the Google Books API than the plugin. Each time I tested a book, I opted to add a larger image — the plugin does allow you to replace the default.

\n\n\n\n

The color settings are limited. The block only offers a background color option with no way to adjust the text color. A better option for plugin users is to wrap it in a Group block and adjust the background and text colors there.

\n\n\n\nBook Review block wrapped inside a Group block.\n\n\n\n

It would also be nice to have wide and full-alignment options, which is an often-overlooked featured from many block plugin authors.

\n\n\n\n

Using the Media & Text Block to Recreate the Book Review Block

\n\n\n\n

The Book Review Block plugin has a lot of potential, and I want to see it evolve by providing more flexibility to end-users. Because the Media & Text block is the closest core block to what the plugin offers, I decided to recreate a more visually-appealing design with it.

\n\n\n\nBook review section created with the Media & Text block.\n\n\n\n

I made some adjustments on the content side of things. I used the Heading block for the book title, a List block for the book metadata, and a Paragraph block for the description.

\n\n\n\n

The Media & Text block also provided me the freedom to adjust the alignment, stack the image and content on mobile views, and tinker with the size of the image. Plus, it has that all-important field for customizing the image alt attribute.

\n\n\n\n

The Media & Text block gave me much more design mileage.

\n\n\n\n

However, there are limitations to the core block. It does not fully capture some of the features available via the Book Review block. The most obvious are the automatic book details via an ISBN and the Schema markup. Less obvious, there is no easy way to recreate the star rating — I used emoji stars — and long description text does not wrap under the image. To recreate that, you would have to opt to use a left-aligned image followed by content.

\n\n\n\n

Overall, the Media & Text block gives me the ability to better style the output, which is what I am more interested in as a user. I want to put my unique spin on things. That is where the Book Review Plugin misfires. It is also the sort of thing that the plugin author can iterate on, offering more flexibility in the future.

\n\n\n\n

This is where many block plugins go wrong, particularly when there is more than one or two bits of data users should enter. Blocks represent freedom in many ways. However, when plugin developers stick to a rigid structure, users can sometimes lose that sense of freedom that they would otherwise have with building their pages.

\n\n\n\n

One of the best blocks, hands down, that preserves that freedom is from the Recipe Block plugin. It has structured inputs and fields. However, it allows freeform content for end-users to make it their own.

\n\n\n\n

When block authors push beyond this rigidness, users win.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 20:44:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:11;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce 4.6 Makes New Home Screen the Default for New and Existing Stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3018:\"

WooCommerce 4.6 was released today. The minor release dropped during WooSesh, a global, virtual conference dedicated to WooCommerce and e-commerce topics. It features the new home screen as the default for all stores. Previously, the screen was only the default on new stores. Existing store owners had to turn the feature on in the settings.

\n\n\n\n
\n\n\n\n

The updated home screen, originally introduced in version 4.3, helps store admins see activity across the site at a glance and includes an inbox, quick access to store management links, and an overview of stats on sales, orders, and visitors. This redesigned virtual command center arrives not a moment too soon, as anything that makes order management more efficient is a welcome improvement, due to the sheer volume of sales increases that store owners have seen over the past eight months.

\n\n\n\n

In stark contrast to industries like hospitality and entertainment that have proven to be more vulnerable during the pandemic, e-commerce has seen explosive growth. During the State of the Woo address at WooSesh 2020, the WooCommerce team shared that e-commerce is currently estimated to be a $4 trillion market that will grow to $4.5 trillion by 2021. WooCommerce accounts for a sizable chunk of that market with an estimated total payment volume for 2020 projected to reach $20.6 billion, a 74% increase compared to 2019.

\n\n\n\n

The WooCommerce community is on the forefront of that growth and is deeply invested in the products that are driving stores’ success. The WooCommerce team shared that 75% of people who build extensions also build and maintain stores for merchants, and 70% of those who build stores for merchants also build and maintain extensions or plugins. In 2021, they plan to invest heavily in unlocking more features in more countries and will make WooCommerce Payments the native payment method for the global platform.

\n\n\n\n

A new report from eMarketer shows that US e-commerce growth has jumped 32.4%, accelerating the online shopping shift by nearly two years. Experts also predict the top 10 e-commerce players will swallow up more of US retail spending to account for 63.2% of all online sales this year, up from 57.9% in 2019.

\n\n\n\n

The increase in e-commerce spending may not be entirely tied to the pandemic, as some experts believe this historic time will mark permanent changes in consumer spending habits. This is where independent stores, powered by WooCommerce and other technologies, have the opportunity to establish a strong reputation for themselves by providing quality products and reliable service, as well as by being more nimble in the face of pandemic-driven increases in volume.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 03:48:32 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:12;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:101:\"WPTavern: The Future of Starter Content: WordPress Themes Need a Modern Onboarding and Importing Tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106177\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool?utm_source=rss&utm_medium=rss&utm_campaign=the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7385:\"Image credit: picjumbo.com on Pexels.\n\n\n\n

Starter content. It was a grand idea, one of those big dreams of WordPress. It was the new kid on the block in late 2016. Like the introduction of post formats in 2011, the developer community was all in for at least that particular release version. Then, it was on to the next new thing, with the feature dropping off the radar for all but the most ardent evangelists.

\n\n\n\n

Some of us were burned over the years, living and dying by the progress of features that we wanted most.

\n\n\n\n

Released in WordPress 4.7, starter content has since seemed to be going the way of post formats. After four years, only 141 themes in the WordPress theme directory support the feature. There has been no movement to push it beyond its initial implementation. And, it never really covered the things that theme authors wanted in the first place. It was a start. But, themers were ultimately left to their own devices, rolling custom solutions for something that never panned out — fully-featured demo and imported content. Four years is an eternity in the web development world, and there is no sense in waiting around to see if WordPress would push forward.

\n\n\n\n

Until Helen Hou-Sandí published Revisiting Starter Content last week, most would have likely assumed the feature would be relegated to legacy code used by old-school fans of the feature and those theme authors who consider themselves completionists.

\n\n\n\n

“Starter content in 4.7 was always meant to be a step one, not the end goal or even the resting point it’s become,” wrote Hou-Sandí. “There are still two major things that need to be done: themes should have a unified way of showing users how best to put that theme to use in both the individual site and broader preview contexts, and sites with existing content should also be able to take advantage of these sort of ‘ideal content’ definitions.”

\n\n\n\n

Step two should have been this four-year-old accompanying ticket to allow users to import starter content into existing, non-fresh sites.

\n\n\n\n

Since the initial feature dropped, the theme landscape has changed. Let’s face it. WordPress might simply not be able to compete with theme companies that are pushing the limits, creating experiences that users want at much swifter speeds.

\n\n\n\n

Look at where the Brainstorm Force’s Starter Templates plugin for its Astra theme is now. Users can click a button and import a full suite of content-filled pages or even individual templates. And, the Astra theme is not alone in this. It has become an increasingly-common standard to offer some sort of onboarding to users. GoDaddy’s managed WordPress service fills a similar need on the hosting end.

\n\n\n\nAstra’s starter templates and content.\n\n\n\n

As WordPress use becomes more widespread, the more it needs a way to onboard users.

\n\n\n\n

This essentially boils down to the question: how can I make it look like the demo?

\n\n\n\n

Ah, the age-old question that theme authors have been trying to solve. Whether it has been limitations in the software or, perhaps, antiquated theme review guidelines related to demo and imported content, this has been a hurdle that has been tough to jump. But, some have sailed over it and moved on. While WordPress has seemingly been twiddling its thumbs for years, Brainstorm Force and other theme companies have solved this and continued to innovate.

\n\n\n\n

This is not necessarily a bad thing. There are plenty of ideas to steal copy and pull into the core platform.

\n\n\n\n

One of the other problems facing the WordPress starter content feature is that it is tied to the customizer. With the direction of the block system, it is easy to ask what the future holds. The customizer — originally named the theme customizer — was essentially a project to allow users to make front-end adjustments and watch those customizations happen in real time. However, new features like global styles and full-site editing are happening on their own admin screens. Most theme options will ultimately be relegated to global styles, custom templates, block styles, and block patterns. There may not be much left for the customizer to do.

\n\n\n\n

Right now, there are too many places in WordPress to edit the front-end bits of a WordPress site. My hope is that all of these things are ultimately merged into one less-confusing interface. But, I digress…

\n\n\n\n

Starter content should be rethought. Whoever takes the reins on this needs a fresh take that adopts modern methods from leading theme companies.

\n\n\n\n

The ultimate goal should be to allow theme authors to create multiple sets of templates/content that end-users can preview and import. It should not be tied to whether it is a new site. Any site owner should be able to import content and have it automagically go live. It should also be extendable to allow themes to support page builders like Elementor, Beaver Builder, and many others.

\n\n\n\n

This seems to be in line with Hou-Sandí’s thoughts. “For a future release, we should start exploring what it might look like to opt into importing starter content into existing sites, whether wholesale or piecewise,” she wrote. “Many of us who work in the WordPress development/consulting space tend not to ever deal in switching between public themes on our sites, but let’s not forget that’s a significant portion of our user audience and we want to continue to enable them to not just publish but also publish in a way that matches their vision.”

\n\n\n\n

Let’s do it right this go-round, keep a broad vision, and provide an avenue for theme authors to adopt a standardized core WordPress method instead of having everyone build in-house solutions.

\n\n\n\n

I haven’t even touched on the recent call to use starter content for WordPress.org theme previews. It will take more than ideas to excite many theme authors about the possibility. That ticket has sat for seven years with no progress, and most have had it on their wish list for much longer. It is an interesting proposal, one that has been tossed around in various team meetings for years.

\n\n\n\n

Like so many other things, theme authors have either given up hope or moved onto doing their own thing. They need to be brought into the fold, not only as third parties who are building with core WordPress tools but as developers who are contributing to those features.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 14 Oct 2020 20:07:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:13;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:116:\"WPTavern: Google Podcasts Manager Adds More Data from Search: Impressions, Top-Discovered Episodes, and Search Terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:271:\"https://wptavern.com/google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms?utm_source=rss&utm_medium=rss&utm_campaign=google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2568:\"

Google announced an expansion of listener engagement metrics today for those using its Podcast Manager. Previously, audience insights included data about the types of devices listeners are using, where listeners tune in and drop off during a given episode, total number of listens, and listening duration, but the service lacked analytics regarding how visitors were discovering the podcast.

\n\n\n\n

Google is remedying that today by expanding the dashboard to show impressions, clicks, top-discovered episodes, and search terms that brought listeners to the podcast. This information can help podcasters understand how their content is getting discovered so they can better tailor their episodes to attract more new listeners.

\n\n\n\n

The podcasting industry has seen remarkable growth over the past five years, which previously led experts to project that marketers will spend over $1 billion in advertising by 2021. After the pandemic hit, podcast listening took a downturn in the U.S. but at the same time, podcast creators have found more time to create new shows and episodes. Businesses are turning to the medium to supplement traditional marketing methods that no longer have the same impact now that consumer spending habits heavily favor online products.

\n\n\n\n

Along with the new metrics available inside Google Podcasts Manager, the company also published a guide to optimizing podcasts for Google Search. It highlights four important items for making sure a podcast can be found:

\n\n\n\n
  • Detailed show and episode metadata
  • Ensure the podcast’s webpage and RSS data match
  • Include cover art
  • Ensure Googlebot can access your audio files
\n\n\n\n

A detailed breakdown of your audience’s listening habits isn’t worth much if you’re having trouble getting your podcast discovered. Any podcasting plugin for WordPress should handle these basic optimization recommendations, but if you are still having trouble being found via Google, you can dig deeper into the podcast setup guide for more detailed recommendations.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 22:57:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:14;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Are Block-Based Widgets Ready To Land in WordPress 5.6?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/are-block-based-widgets-ready-to-land-in-wordpress-5-6?utm_source=rss&utm_medium=rss&utm_campaign=are-block-based-widgets-ready-to-land-in-wordpress-5-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8214:\"

Two weeks ago, the Gutenberg team put out an open call for block-based widgets feedback. I had already written a lengthy review of the new system earlier in September but was asked by a member of the team to share my thoughts on the most recent iteration. With the upcoming freeze for WordPress 5.6 Beta 1 just a week away, I figured it would not hurt to do another deep dive.

\n\n\n\n

For reference, my latest testing is against version 9.2.0-alpha-172f589 of the Gutenberg plugin, which was a build from earlier today. Gutenberg development moves fast, but everything should be accurate to that point.

\n\n\n\n

Ultimately, many of the problems I pointed out over a month ago still exist. However, the team has cleaned most of the minor issues, such as pointing the open/close arrows for sidebars (block areas) in the correct direction and making it more consistent with the post-editing screen. The UI is much more polished.

\n\n\n\n

Before I dive into all the problems, I want to answer the question I am proposing. Yes, the block-based widget system will be ready for prime time when WordPress 5.6 lands. It is not there yet, but it is at a point where there is a clear finish line that is reachable in the next two months.

\n\n\n\n

I will ignore the failure of block-based widgets in the customizer, which landed in Gutenberg 8.9 and was removed in 9.1. I will also look past the recent proposal to reconstruct the widgets screen to use the Customize API, at least for now. There is a boatload of problems that block-based widgets present for the customizer, and those problems are insurmountable for WordPress 5.6. Long term, WordPress needs to have a single place for editing widget/block areas. Users will likely have to live with some inconsistencies for a while.

\n\n\n\n

Assuming the team does not try to throw a last-minute Hail Mary and implement full editing of blocks in the customizer this round, it is safe to say that block-based widgets are well on their way toward a successful WordPress 5.6 debut.

\n\n\n\n

The User Experience

\n\n\n\nBlock-based widgets screen.\n\n\n\n

As a user, I genuinely enjoy using the new Widgets admin screen. The open-ended, free-form block areas create untold possibilities for designing my WordPress sites. Traditional widgets were limited in scope. Users were buckled down to a handful of core widgets, possibly some plugin widgets, and whatever their theme author offered up. However, with blocks, the pool of choices expands to at least triple the out-of-the-box options (I am not counting embed-type blocks individually). Plus, blocks provide a far more extensive set of design options than a traditional widget.

\n\n\n\n

In comparison, traditional widgets are outdated. Blocks are superior in almost every way. However, there are still problems with this new system.

\n\n\n\n

The biggest issue right now is that end-users can exit the Widgets screen without saving their changes. There is no warning to let them know that all their work is about to be lost in the ether. This is one of those OMGBBQ-level items that need to happen before WordPress 5.6 drops.

\n\n\n\n

One nice-to-have-but-not-necessary feature would be the ability to drag blocks from one block area to another. In the old widgets system, users could move widgets from sidebar to sidebar. The current alternative is to copy a widget, paste it in a new block area, and remove the original.

\n\n\n\n

I am also not a fan of not having an option for the top toolbar, which is available on the post-editing screen. One of the reasons for using this toolbar is because I dislike the default popup toolbar on individual blocks. It is distracting and often gets in the way of my work.

\n\n\n\n

Legacy widgets seem to still be a work in progress. The Legacy Widget block did not work at all for me at times. Then, it magically began to work. However, Gutenberg does now automatically add registered third-party widgets to the block inserter just as if they were blocks.

\n\n\n\nGetting a plugin’s widget to work.\n\n\n\n

This presented its own problems. The only way I managed to make third-party plugin widgets work was to insert the widget, save, and refresh the widgets screen. At that point, the widgets appeared and became editable.

\n\n\n\n

The Theme Author Experience

\n\n\n\n

One of my biggest concerns for theme authors right now is that there does not seem to be any documentation in the block editor handbook. There is plenty of time to make that happen, but there are things theme authors need to be aware of. Having a centralized location, even while the feature is under development, would help them gear up for the 5.6 release.

\n\n\n\n

Some of these questions, which may be answered in various Make blog posts, should exist on a dedicated documentation page:

\n\n\n\n
  • How can a theme opt out of block-based widgets?
  • What are the hooks to add custom styles for the Widgets screen?
  • Can themes target specific sidebar styles on the Widgets screen?
  • Is it possible to consistently style sections like traditional widgets on the front end?
  • Can themes opt into wide and full-alignment within block areas, which could essentially be used similarly to the post content area?
\n\n\n\n

These are some of the questions I would want to be answered as a former theme author. I am no longer in the thick of the theme design game and presume that those who are would have a larger list of questions.

\n\n\n\n

One less-obvious piece of documentation should center on how to handle fallbacks or default widgets. Traditionally, themes that needed to show a default set of widgets would check if the sidebar has widgets and fall back to using the_widget() to output one or more defaults. While theme authors can still do that, we should start to transition them across the board to the block system.

\n\n\n\n

Should theme authors copy/paste block HTML as a fallback? Would the starter content system be better for this, and can starter widget content handle blocks? What is the recommended method for widget fallbacks in WordPress 5.6?

\n\n\n\n

There is still the ongoing issue of how theme authors should handle the traditional widget and widget title wrapper HTML in the new block paradigm. One patch added since the Gutenberg 9.1 release wraps every top-level block with the widget wrapper. If this lands in the 9.2 release, it will likely make the issue worse.

\n\n\n\n

In the traditional system, both the widget title and content are wrapped within a container together. However, if a user adds a Heading block (widget title) and another block (widget content), each block is wrapped separately with the theme’s widget wrappers. The only way to rectify the situation as it stands is for end-users to add a Group block for each “widget” they want, which would require an extensive amount of re-education for WordPress users. It is not an ideal scenario.

\n\n\n\nEach block is wrapped as an individual section.\n\n\n\n

Instead of attempting to directly “fix” this issue, WordPress should instead do nothing to the output. Blocks and traditional widgets are fundamentally different.

\n\n\n\n

Let theme authors take the reins on this one and explore possibilities. However, give them the tools to do so, such as supporting block patterns.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 21:35:39 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:15;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: WordCamp Austin 2020 Finds Success with VR Experience for Sessions and Networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106119\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking?utm_source=rss&utm_medium=rss&utm_campaign=wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7246:\"

WordCamp Austin 2020 attendees are raving about their experiences attending the virtual event last Friday. It was no secret that the camp’s organizers planned to use Hubs Virtual Rooms by Mozilla to create a unique environment, but few could imagine how much more interactive and personalized the experience would be than a purely Zoom-based WordCamp.

\n\n\n\n

After selecting a custom avatar, attendees entered the venue using a VR headset or the browser to check out sessions or network in the hallway track.

\n\n\n\n
\n

Here’s a small taste of the experience at @WordCampATX today. #WordPress logos and no sponsor banners on any elevator doors. #WCATX pic.twitter.com/Nv2p2VchXf

— David Bisset (@dimensionmedia) October 9, 2020
\n
\n\n\n\n

Speaker and Q&A sessions were broadcast through Zoom but organizers can also embed YouTube videos and streams within the standalone VR environment.

\n\n\n\n

“The VR experience was the most life-like WordCamp experience I’ve had since the start of global lockdowns,” attendee and speaker David Vogelpohl said. “You could attend sessions in one of two virtual presentation halls depending on what track you wanted to see at that time. The speaker presented on a virtual stage and you could see the other attendees watching the presentation.”

\n\n\n\n

Vogelpohl said he enjoyed his experience getting to know others in the Slack and VR venue. Organizers preserved the general vibe of the “hallway track” to recreate what is arguably one of the most valuable aspects of in-person WordCamps.

\n\n\n\n
\n

So cool – checking out the Virtual Space of WordCamp Austin – love the background noise of people talking, ran into @ChrisWiegman and @Josh412 #WCATX pic.twitter.com/68EdgDN2Om

— Birgit Pauli-Haack (@bph) October 9, 2020
\n
\n\n\n\n

“In the hallway track between the virtual presentation halls was a large foyer where you could meet new people, spot a friend speaking with someone else, and virtually step aside from a group conversation to have a private conversation,” Vogelpohl said.

\n\n\n\n

“It was great to see folks like Josepha circling around speaking with attendees, Josh Pollock nerding out in a corner with a group of advanced WP developers, and having random friends drop into a conversation I was having with a group of others. While VR WordCamp doesn’t wholly replace the value of attending a WordCamp live, a lot of the best parts of meeting and collaborating with others was captured in the VR context.”

\n\n\n\n

The live music interludes, which showcased talents from around the community, also provided a way for virtual attendees to stay connected while waiting for the next session.

\n\n\n\n

Behind the Scenes with Anthony Burchell: Creative Director for WordCamp Austin’s Virtual World

\n\n\n\n

WordPress core contributor Anthony Burchell, who started a company dedicated to creating interactive XR sound and art experiences, was the creative director behind the WordCamp Austin’s VR backdrop.

\n\n\n\n

“For WordCamp Austin we wanted to give folks something to be excited about outside of the typical webcam and chat networking,” Burchell said. “I feel that virtual events are not utilizing the networking layer nearly enough to make folks feel like they are really at an event. I’ve seen many compelling formats for virtual events utilizing webcams and chat rooms, but in the end, it feels like there’s been a missing element of presence; something video games and virtual reality excel at.”

\n\n\n\n
\n

Virtual mission control for #WCATX pic.twitter.com/WyrFkIsW2Q

— Anthony Burchell (@antpb) October 9, 2020
\n
\n\n\n\n

Setting up the virtual world involves spinning up a self-hosted instance of Hubs Cloud, which Burchell said is very similar to the complexity of making a WordPress site.

\n\n\n\n

“The most time consuming part of creating a 3D world for an event is making the 3D assets for the space,” Burchell said. “In total I streamed 11 hours of video leading up to the event to give a glimpse into the process.”

\n\n\n\n

Burchell’s YouTube playlist documents the incredible amount of work that went into creating the WordCamp’s virtual venue for attendees to enjoy.

\n\n\n\n

“While it took quite a bit of time to prepare, the code and assets are completely reusable for another event,” Burchell said. “A lot of the time was spent trying to make the space purpose built for the goals of the camp. Much like a real WordCamp, I found the majority of folks packing into the theater rooms for presentations and dipping out a little early to network with friends in the hallway area. That was very much by design!”

\n\n\n\n

Burchell and the other organizers were careful to ensure that the Hubs space was not the primary viewing experience of the camp but rather an extension of the networking activities that attendees could drop in on. The event had nearly identical numbers of attendees joining the virtual space as it did for those joining the video channels. At the end of the afterparty, Burchell turned on flying for all attendees to conclude the successful event:

\n\n\n\n
\n\n
\n\n\n\n

“With Hubs we were able to give attendees the ability to express themselves within a venue vs within a camera and chat box,” Burchell said. “It was incredible to see characteristics of folks in the community shine through a virtual avatar! Just the simple act of seeing your WordCamp friends in the hallway joking and chatting just as they would at a real life event was enough to make me feel like I was transported to a real WordCamp.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 22:31:02 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:16;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Privacy-Conscious WordPress Plugin Caches and Serves Gravatar Images Locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105825\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally?utm_source=rss&utm_medium=rss&utm_campaign=privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5285:\"

Ari Stathopoulos released his new Local Gravatars plugin last week. The goal of the plugin is to allow site owners to take advantage of the benefits of a global avatar system while mitigating privacy concerns by hosting the images locally.

\n\n\n\n

In essence, it is a caching system that stores the images on the site owner’s server. It is an idea that Peter Shaw proposed in the comments on an earlier Tavern article covering local avatar upload. It is a middle ground that may satisfy some users’ issues with how avatars currently work in WordPress.

\n\n\n\n

“I am one of the people that blocks analytics, uses private sessions when visiting social sites, I use DuckDuckGo instead of Google, and I don’t like the ‘implied’ consents,” said Stathopoulos. “I built the plugin for my own use because I don’t know what Gravatar does, I don’t understand the privacy policies, and I am too lazy to spend two hours analyzing them. It’s faster for me to build something that is safe and doesn’t leave any room for misunderstandings.”

\n\n\n\n

He is referring to Automattic’s extensive Privacy Policy. He said it looks benign. However, he does not like the idea of any company being able to track what sites he visits without explicit consent.

\n\n\n\n

“And when I visit a site that uses Gravatar, some information is exposed to the site that serves them — including my IP,” said Stathopoulos. “Even if it’s just for analytics purposes, I don’t think the company should know that page A on site B got 1,000 visitors today with these IPs from these countries. There is absolutely no reason why any company not related to the page I’m actually visiting should have any kind of information about my visit.”

\n\n\n\n

The Local Gravatars plugin must still connect to the Gravatar service. However, the connection is made on the server rather than the client. Stathopoulos explained that the only information exposed in this case is the server’s IP and nothing from the client, which eliminates any potential privacy concerns.

\n\n\n\n

The Latest Plugin Update

\n\n\n\n

Stathopoulos updated the plugin earlier today to address some performance concerns for pages that have hundreds or more Gravatar images. In the version 1.0.1 update, he added a maximum processing time of five seconds and changed the cache cleanup process from daily to weekly. Both of these are filterable via code.

\n\n\n\n

“Now, if there are Gravatars missing in a page request, it will get as many as it can, and, after five seconds, it will stop,” said Stathopoulos. “So if there are 100 Gravatars missing and it gets the first 20, the rest will be blank (can be filtered to use a fallback URL, or even fall back to the remote URL, though that would defeat the privacy improvement). The next page request will get the next 20, and so on. At some point, all will be there, and there will be no more delays.”

\n\n\n\n

He did point out that performance could temporarily suffer when installing it on a site that has individual posts with 1,000s of comments and a lot of traffic. However, nothing would crash on the site, and the plugin should eventually lead to a performance boost in this scenario. For such large sites, owners could use the existing filter hooks to tweak the settings.

\n\n\n\n

Right now, the plugin is primarily an itch he wanted to scratch for his own purposes. However, if given enough usage and feedback, he may include a settings screen to allow users to control some of the currently-filterable defaults, such as the cleanup timeframe and the maximum process time allowed.

\n\n\n\n

The Growing List of Alternatives

\n\n\n\n

With growing concerns around privacy in the modern world, Local Gravatars is another tool that end-users can employ if they have any concerns around the Gravatar service. For those who are OK with an auto-generated avatar, Pixel Avatars may be a solution.

\n\n\n\n

“I’ve seen some of them, and they are wonderful!” Stathopoulos said of alternatives for serving avatars. “However, this plugin is slightly different in that the avatars the user already has on Gravatar.com are actually used. They can see the image they have uploaded. The user doesn’t need to upload a separate avatar, and an automatic one is not used by default.”

\n\n\n\n

He would not mind using an auto-generated avatar when commenting on blogs or news sites at times. However, Stathopoulos prefers Gravatar for community-oriented sites.

\n\n\n\n

“My Gravatar is part of my online identity, and when I see, for example, a comment from someone on WordPress.org, I know who they are by their Gravatar,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 21:06:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:17;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: WordPress 5.6 to Introduce Application Passwords for REST API Authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105997\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2604:\"

In 2015, WordPress 4.4 introduced a REST API, but one thing that has severely limited its broader use is the lack of authentication capabilities for third-party applications. After considering the benefits and drawbacks of many different types of authentication systems, George Stephanis published a proposal for integrating Application Passwords, into core.

\n\n\n\n

Stephanis highlighted a few of the major benefit that were important factors in the decision to use Application Passwords: the ease of making API requests, ease of revoking credentials, and the ease of requesting API credentials. The project is available as a standalone feature plugin, but Stephanis and his collaborators recommended WordPress merge a pull request that is based off the feature plugin’s codebase.

\n\n\n\n

After WordPress 5.6 core tech lead Helen Hou-Sandi gave the green light for Application Passwords to be merged into core, the developer community responded enthusiastically to the news.

\n\n\n\n

“I am/we are 100% in favor of this,” Joost deValk commented on the proposal. “Opening this up is like opening the dawn of a new era of WordPress based web applications. Suddenly authentication is not something you need to fix when working with the API and you can just build awesome stuff.”

\n\n\n\n

Stephanis’ proposal also mentioned how beneficial a REST API authentication system would be for the Mobile teams‘ contributors who are relying on awkward workarounds while integrating Gutenberg support.

\n\n\n\n

“This would be a first step to replace the use of XMLRPC in the mobile apps and it would allow us to add more features for self hosted users,” Automattic mobile engineer Maxime Biais said.

\n\n\n\n

After the REST API was added to WordPress five years ago, many had the expectation that WordPress-based web applications would start popping up everywhere. Without a reliable authentication system, it wasn’t easy for developers to just get inspired and build something quickly. Application Passwords in WordPress 5.6 will open up a lot of possibilities for those who were previously deterred by the lack of core methods for authenticating third-party access.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 23:01:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:18;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"WPTavern: WP Agency Summit Begins Its Second Annual Virtual Event October 12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105160\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:197:\"https://wptavern.com/wp-agency-summit-begins-its-second-annual-virtual-event-october-12?utm_source=rss&utm_medium=rss&utm_campaign=wp-agency-summit-begins-its-second-annual-virtual-event-october-12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6357:\"

Jan Koch, the founder and host of WP Agency Summit, is kicking off his second annual event on October 12. The five-day event will feature 37 speakers from a wide range of backgrounds across the WordPress industry. It is a free virtual event that anyone can attend.

\n\n\n\n

“The focus for the 2020 WP Agency Summit is showing attendees how to bring back the fun into scaling their agencies,” said Koch. “It is all about reducing the daily hustle by teaching how to successfully build and manage teams, how to work with enterprises (allowing for fewer customers but bigger projects), how to build sustainable recurring revenue, and how to position your agency to dominate your niche.”

\n\n\n\n

This year’s event includes three major changes to make the content more accessible to a larger group of people. Each session will be available between October 12 – 16 instead of the previous 48-hour window that attendees had to find time for in 2019.

\n\n\n\n

After the event has concluded, access to the content will be behind a paywall. Koch reduced the price to $77 for lifetime access for those who purchase pre-launch, which will increase to $127 during the event. Last year’s prices ballooned to $497, which meant that it was simply not affordable for many who found it too late.

\n\n\n\n

Some of the proceeds this year are going toward transcribing all the videos so that hearing-impaired users can enjoy the content.

\n\n\n\n

This year’s event will also focus on a virtual networking lounge for attendees. “I’ve seen how well it worked at the WP FeedBack Summit — we even had BobWP record a podcast episode on the fly in that lounge!” said Koch. “I’ve seen many new friendships develop, people connecting with new suppliers or getting themselves booked on podcasts, and sharing experiences about their businesses.”

\n\n\n\n

The lounge will be open during the entirety of the summit, which will allow attendees to jump into the conversation on their own time.

\n\n\n\n

A More Diverse Speaker Lineup

\n\n\n\n

Koch received some backlash for the lack of gender diversity last year. The 2019 event had over 20 speakers from a diverse male lineup. However, only four women from our industry led sessions.

\n\n\n\n

When asked about this issue in 2019, Koch responded, “I recognize this as a problem with my event. The reason I have so much more male than female speakers is quite simple, the current speaker line-up is purely based on connections I had when I started planning for the event. It was a relatively short amount of time for me, so I wasn’t able to build relationships with more female WP experts beforehand.”

\n\n\n\n

The host said he paid attention to the feedback he received. While not hitting the 50/50 split goal he had for 2020’s event, 16 of the 37 speakers are women.

\n\n\n\n

Koch said he strived to get speakers from a wider range of backgrounds. He wanted to bring in both freelancers and multi-million dollar agency owners. He also focused on getting people from multiple countries to represent WordPress agencies.

\n\n\n\n

“I did reach out to around 130 people four months before the event to make new connections,” he said. “The community around the Big Orange Heart (a non-profit for mental well-being) also helped a lot with introducing me to new members of the WP community.”

\n\n\n\n

Koch said he learned two valuable lessons when branching out beyond his existing connections for this year’s event:

\n\n\n\n

Firstly, don’t hesitate to reach out to people you think will never talk to you because they’re running such big companies. For example, I immediately got confirmations from Mario Peshev from Devrix, Brad Touesnard from Delicious Brains, or Marieke van de Rakt from Yoast. When first messaging them, I had little hope they’d set aside time to jump on an interview with me – but they were super supportive and accommodating! The WordPress community really is a welcoming environment if you approach people in a humble way.

Secondly, build connections with sincerity. Do not just focus on what you can get from that connection but how you can help the other person. I know this sounds cheesy and you’ve heard this quite often — but it is true. Once I got the first response from new contacts and explained my goal of connecting fellow WordPress community members virtually, most immediately agreed because they also benefit from new connections and being positioned as a thought-leader in this event.

\n\n\n\n

WP Agency Summit? WP FeedBack Summit?

\n\n\n\n

For readers who recall the Tavern’s coverage of the WP FeedBack Summit earlier this year, the article specifically stated that the WP FeedBack Summit was a continuation of 2019’s WP Agency Summit. The official word at the time from WP FeedBack’s public relations team was the following:

\n\n\n\n

Last year’s event, the WP Agency Summit has been rebranded under the umbrella of WP FeedBack’s brand when Jan Koch the host of last’s year WP Agency Summit joined WP FeedBack as CTO.

\n\n\n\n

Koch said that it was a standalone event and not directly connected to WP Agency Summit but had the same target audience. However, the WP FeedBack Summit did use the previous WP Agency Summit’s stats and data to promote the event.

\n\n\n\n

“The WP FeedBack Summit was hosted under the WP FeedBack brand because I joined their team as CTO in March this year,” he said. “Vito [Peleg] and I had the idea to host a virtual conference around WordPress because of WordCamp Asia being canceled — we wanted to help connect the community online through our summit.

\n\n\n\n

Koch left WP FeedBack soon after the summit ended and is currently back on his own and has a goal of making WP Agency Summit a yearly event.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 17:01:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:19;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: Navigation Screen Sidelined for WordPress 5.6, Full-Site Editing Edges Closer to Public Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105839\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:247:\"https://wptavern.com/navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta?utm_source=rss&utm_medium=rss&utm_campaign=navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4676:\"

The new block-based navigation screen is once again delayed after it was originally slated for WordPress 5.5 and then put on deck for 5.6. Contributors have confirmed that it will not be landing in WordPress core until 2021 at the earliest.

\n\n\n\n

“The Navigation screen is still in experimental state in the Gutenberg plugin, so it hasn’t had any significant real-world use and testing yet,” Editor Tech Lead Isabel Brison said. She made the call to remove it from the 5.6 lineup after the feature missed the deadline for bringing it out of the experimental state. It still requires a substantial amount of development work and accessibility feedback before moving forward.

\n\n\n\n

Contributors will focus instead on making sure the Widgets screen gets out the door for 5.6 and plan to pick up again on Navigation towards the end of November.

\n\n\n\n

WordPress 5.6 lead Josepha Haden gave an update this week on the progress of all the anticipated features, including the planned public beta for full-site editing (FSE).

\n\n\n\n

“I don’t expect FSE to be feature complete by the time WP5.6 is released,” Haden said. “What I expect is that FSE will be functional for simple, routine user flows, which we can start testing and iterating on. That feedback will also help us more confidently design and build our complex user flows.”

\n\n\n\n

Frank Klein, an engineer at Human Made, asked in the comments of another update why full-site editing is being tied to 5.6 progress in the first place, since it will still only be available in the plugin at the time of release.

\n\n\n\n

“The main value is that it provides a good checkpoint along the path of FSE’s development,” Kjell Reigstad said. “Full-site editing is very much in progress. It is still experimental, but the general approach is coming into view, and becoming clearer with every plugin release.”

\n\n\n\n

Reigstad posted an update on what developers can expect regarding block-based theming and the upcoming release, since the topic is closely tied to full-site editing. He emphasized that the infrastructure is already in place and that, despite it still being experimental, future block-based themes should work in a similar way to how they are working now.

\n\n\n\n

“The focus is now shifting towards polishing the user experience: using the site editor to create templates, using the query block, iterating on the post and site blocks, and implementing the Global Styles UI,” Reigstad said.

\n\n\n\n

“The main takeaway is that when 5.6 is released, the full-site editing feature set will look similar to where it is today, with added polish to the UI, and additional features in the Query block.”

\n\n\n\n

Theme authors are entering a new time of uncertainty and transition, but Reigstad reassured the community that themes as we know them today are not on track to be phased out in the immediate future.

\n\n\n\n

“There is currently no plan to deprecate the way themes are built today,” Reigstad said. “Your existing themes will continue to work as they always have for the foreseeable future.” He also encouraged contributors to get involved in an initiative to help theme authors transition to block-based themes. (This project is not targeted for the 5.6 release.)

\n\n\n\n

Developers can follow important FSE project milestones on GitHub, and subscribe to the weekly Gutenberg + Themes updates to track progress on block-based theming. A block-based version of the Twenty Twenty-One theme is in the works and should pick up steam after 5.6 beta 1, expected on October 20.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 22:57:37 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:20;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"WPTavern: EditorPlus 1.9 Adds Animation Builder for the Block Editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105678\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:181:\"https://wptavern.com/editorplus-1-9-adds-animation-builder-for-the-block-editor?utm_source=rss&utm_medium=rss&utm_campaign=editorplus-1-9-adds-animation-builder-for-the-block-editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4535:\"

Munir Kamal shows no signs of slowing down. He continues to push forward with new features for his EditorPlus plugin, which allows end-users to customize the look of the blocks in their posts and pages. He calls it the “no-code style editor for WordPress.”

\n\n\n\n

The latest addition to his plugin? Animation styles for every core block.

\n\n\n\n

My first thought was that this would bloat the plugin with large amounts of unnecessary CSS and JavaScript for what is essentially a few bells and whistles. However, Kamal pulled it off with minimal custom CSS.

\n\n\n\n

Inspired by features from various website builders, he wanted to bring more and more of those things to the core block editor. The animations feature is just another ticked box on a seemingly never-ending checklist of features. And, so far, it’s all still free.

\n\n\n\n

Since we last covered EditorPlus in June, Kamal has added the ability to insert icons via any rich-text area (e.g., paragraphs, lists, etc.). He has also added shape divider, typography, style copying, and responsive editing options for the core WordPress blocks.

\n\n\n\n

How Do Animations Work?

\n\n\n\n

In the version 1.9 release of EditorPlus, Kamal added “entrance” animations. These types of animations happen when a visitor sees the block for the first time on the screen. For example, users could set the Image block to fade into visibility as a reader views the block.

\n\n\n\n

Currently, the plugin adds seven animations:

\n\n\n\n
  • Fade
  • Slide
  • Bounce
  • Zoom
  • Flip
  • Fold
  • Roll
\n\n\n\nAdding a Slide animation for the Cover block text.\n\n\n\n

Each animation has its own subset of options to control how it behaves on the page. The bounce animation, for example, allows users to select the bounce direction. Other options include duration, delay, speed curve, delay, and repeat. There are enough choices to spend an inordinate amount of time tinkering with the output.

\n\n\n\n

One of the best features of this new feature is that Kamal has included an Animation Player under the block options. By clicking the play button, users can view the animation in action without previewing the post.

\n\n\n\n

Watch a quick video of the Animations feature:

\n\n\n\n
\n\n
\n\n\n\n

After testing and using each animation, everything seemed to work well. The one downside — and this is not limited to animations — is that applying styles on the block level sometimes does not make sense. In many cases, it would help users to have options to style or animate the items within the block, such as the images in the Gallery block. When I broached the subject with Kamal, he was open to the idea of finding a solution to this in the future.

\n\n\n\n

What Is Next for EditorPlus?

\n\n\n\n

At a certain point, too many block options can almost feel like overkill and become unwieldy. EditorPlus does allow users to disable specific features from its settings screen, which can help get rid of some unwanted options. Kamal said he would like to continue making it more modular so that users can use only the features they need.

\n\n\n\n

“What I plan is to have micro-level feature control for this extension so that a user can switch off individual styling panels like, Typography, Background, etc.,” he said. “Even further, I plan to bring these controls based on the user role as well. So an admin can disable these features for the editor, author, etc.”

\n\n\n\n

That may be a bit down the road though. For now, he wants to focus on adding new features that he already has planned.

\n\n\n\n

“I do plan to add more animation features,” said Kamal. “I got too many ideas, such as scroll-controlled animation, hover animation, text animation, Lottie animation, background animation, animated shape dividers, and more. But, having said that, I will be careful adding only those features that don’t affect page performance much.”

\n\n\n\n

Outside of extra styles and animations for existing blocks, he plans to jump on the block-building train in future releases. EditorPlus users could see accordion, toggle, slider, star rating, and other blocks in an upcoming release.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:53:40 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:21;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"Donncha: Hide featured image if it’s in the post\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://odd.blog/?p=89503242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://odd.blog/2020/10/08/hide-featured-image-if-its-in-the-post/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3885:\"

I’ve been running a photoblog at inphotos.org since 2005 on WordPress. (And thanks to writing this I noticed it’s 15 years old today!)

\n\n\n\n
\n\n\n\n

In that time WordPress has changed dramatically. At first I used Flickr to host my images, but after a short time I hosted the images myself. (Good thing too since Flickr limited free user accounts to 1000 images, so I wrote a script to download the Flickr images I used in posts.)

\n\n\n\n
\n\n\n\n

For quite a long time I used the featured image instead of inserting the image into the post content, but then about two years ago I went back to inserting the photo into the post. Unfortunately that meant the photo was shown twice, once as a featured image, and once in the post content.

\n\n\n\n

The last theme I used supported custom post types, one of which was a photo type that displayed the featured image but hid the post content. It was an ok compromise, but not perfect.

\n\n\n\n
\n\n\n\n

Recently I started using Twenty Twenty, but after 15 years I had a mixture of posts with:

\n\n\n\n
  • Featured image with no image in the post.
  • Featured image with the same image in the post.
\n\n\n\n

I knew I needed something more flexible. I wanted to hide the featured image if it also appeared in the post content. I procrastinated and never got around to it until this evening when I discovered it was actually quite easy.

\n\n\n\n\n\n\n\n

Copy the following code into the function.php of your child theme and you’ll be all set! It relies on you having unique filenames for your images. If you don’t then remove the call to basename(), and that may help.

\n\n\n
\nfunction maybe_remove_featured_image( $html ) {\n        if ( $html == \'\' ) {\n                return \'\';\n        }\n        $post = get_post();\n        $post_thumbnail_id = get_post_thumbnail_id( $post );\n        if ( ! $post_thumbnail_id ) {\n                return $html;\n        }\n\n        $image_url = wp_get_attachment_image_src( $post_thumbnail_id );\n        if ( ! $image_url ) {\n                return $html;\n        }\n\n        $image_filename = basename( parse_url( $image_url[0], PHP_URL_PATH ) );\n        if ( strpos( $post->post_content, $image_filename ) ) {\n                return \'\';\n        } else {\n                return $html;\n        }\n}\nadd_filter( \'post_thumbnail_html\', \'maybe_remove_featured_image\' );\n
\n\n\n

The post_thumbnail_html filter acts on the html generated to display the featured image. My code above gets the filename of the featured image, checks if it’s in the current post and if it is returns a blank string. Feedback welcome if you have a better way of doing this!

\n\n\n\n
\n\n\n\n

\n\n

Related Posts

\n

Source

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:43:35 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Donncha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:22;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches Automatic Platform Optimization for WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105641\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-automatic-platform-optimization-for-wordpress?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-automatic-platform-optimization-for-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6128:\"

Just a day after launching its new privacy-first web analytics product last week, Cloudflare announced Automatic Platform Optimization (APO) for WordPress. The new service boasts staggering performance improvements for sites that might otherwise be slowed down by shared hosting, slow database lookups, or sluggish plugins:

\n\n\n\n

Our testing… showed a 72% reduction in Time to First Byte (TTFB), 23% reduction to First Contentful Paint, and 13% reduction in Speed Index for desktop users at the 90th percentile, by serving nearly all of your website’s content from Cloudflare’s network. 

\n\n\n\n

APO uses Cloudflare Workers to cache dynamic content and serve the website from its edge network. In most cases this eliminates origin requests and origin processing time. That means visitors requesting your website will get near instant load times. Cloudflare reports that its testing shows APO delivers consistent load times of under 400ms for HTML Time to First Byte (TTFB).

\n\n\n\n

The effects of using APO are similar to hosting static files on a CDN, but without the need to manage a complicated tech stack. Content creators retain their ability to create dynamic websites without any changes to their workflow for the sake of performance.

\n\n\n\n

Version 3.8 of Cloudflare’s official WordPress plugin was recently updated to include support for APO. It detects when users make changes to their content and purges the content stored on Cloudflare’s edge.

\n\n\n\n

The new service is available to Cloudflare users with a single click of a button. APO is included at no cost for existing Cloudflare customers on the Professional, Business, and Enterprise plans. Users on the Free plan can add it to their sites for $5/month. The service is a flat fee and is not metered.

\n\n\n\n

Cloudflare’s announcement has so far been well-received by WordPress professionals and hosting companies and many have already begun testing it.

\n\n\n\n
\n

So the week after @Cloudflare Birthday Week I try and play with as many of the new products as possible. Today was the WordPress APO on my simple demo site. You can see TTFB dropped from ~350ms to ~75ms! https://t.co/zg976EjrZI pic.twitter.com/KuaHqtHLom

— Matt Bullock (@mibullock) October 6, 2020
\n
\n\n\n\n

WordPress lead developer Mark Jaquith called APO “incredible news for the WordPress world.”

\n\n\n\n

“On sites I manage this is going to lower hosting complexity and easily save hundreds of dollars a month in hosting costs,” Jaquith said.

\n\n\n\n

After running several speed tests from six different locations around the world, early testers at Kinsta got remarkable results using APO:

\n\n\n\n

“By caching static HTML on Cloudflare’s edge network, we saw a 70-300% performance increase. As expected, the testing locations furthest away from Tokyo saw the biggest reduction in load time.

“If your WordPress site uses a traditional CDN that only caches CSS, JS, and images, upgrading to Cloudflare’s WordPress APO is a no-brainer and will help you stay competitive with modern Jamstack and static sites that live on the edge by default.”

\n\n\n\n

George Liu, a “self-confessed page speed addict” and Cloudflare Community MVP, performed a series of detailed tests on the new APO product with his blog. After many comparisons, he found that Cloudoflare’s WordPress plugin with APO turned on delivers results similar to his heavily optimized WordPress blog that uses a custom Cloudflare Worker caching configuration.

\n\n\n\n

“You’ll find that Cloudflare WordPress plugin’s one click Automatic Platform Optimization button does wonders for page speed for the average WordPress user not well versed in page speed optimizations,” Liu said.

\n\n\n\n

“Cloudflare’s WordPress plugin Automatic Platform Optimization will in theory beat all other WordPress caching solutions other than you rolling out your own Cloudflare Worker based caching like I did. So you get a good bang for your buck at US$5/month for Cloudflare’s WordPress plugin APO.”

\n\n\n\n

Liu also warned of some speed bumps with the initial rollout, as Cloudflare’s APO supports a limited set of WordPress cookies for bypassing the Cloudflare CDN cache, leaving certain use cases unsupported. APO does not seem to work on subdomains and users are also reporting that it’s not compatible with other caching plugins. It also disables real visitor IP address detection.

\n\n\n\n

Cloudflare is aware of many of these issues, which have been raised in the comments of the announcement, and is in the process of adding more cookies to the list to bypass caching. Due to some plugin conflicts, APO may not be as plug-and-play as it sounds for some users right now, but the product is very promising and should improve over time with more feedback.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 04:18:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:23;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Kick off Block-Based WordPress Theme Development With the Theme.json Creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105832\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator?utm_source=rss&utm_medium=rss&utm_campaign=kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4674:\"

Gutenberg 9.1 made a backward-incompatible change to its theme.json file (experimental-theme.json while full-site editing is under the experimental flag). This is the configuration file that theme developers will need to create as part of their block-based themes. Staying up to date with such changes can be a challenge for theme authors, but Ari Stathopoulos, a Themes Team representative, wrote a full guide for developers.

\n\n\n\n

Jon Quach, a Principal Designer at Automattic, has also been busy creating a tool to help theme authors transition to block-based themes. He recently built a UI-based project called Theme.json Creator that builds out the JSON code for theme authors. Plus, it is up to date with the most recent changes in the Gutenberg plugin.

\n\n\n\n

Tools like these will be what the development community needs as it gets over the inevitable hump of moving away from the traditional theme development paradigm and into a new era where themes are made almost entirely of blocks and a config file.

\n\n\n\n

While plugin development is becoming more complex with the addition of JavaScript, theme development is taking a sharp turn toward its roots of HTML and CSS. We are barreling toward a future in which far more people will be able to create WordPress themes. Even the possibility of sharing pieces of themes (e.g., template parts and patterns) is on the table. This could not only empower theme designers by lowering the barrier to entry, it could also empower some end-users to make the jump into theme building.

\n\n\n\n

However, the theme.json file is one aspect of future theme authorship that is extremely developer-oriented. JSON is a universal format shared between various programming languages. It is meant to be read by machines and is not quite as human-friendly as other formats. As the theme.json file grows to accommodate more configuration options over time, the less friendly it will become to simply typing keys and values in.

\n\n\n\n

It makes sense to build tools to simplify this part of the theme building process.

\n\n\n\n

That is where the Theme.json Creator tool comes in. Theme authors pick and choose the options they want to support and input custom values. Then, the tool spits out everything in properly-formatted JSON.

\n\n\n\nUsing the Theme.json Creator tool.\n\n\n\n

One big thing the tool does not yet cover is custom CSS variables. This feature is a recent addition to the theme.json specification. It allows theme authors to create any custom property that WordPress will automatically output as CSS. In his announcement post, Stathopoulos covered how to create a typographic scale with custom properties and use those variables for editor features, such as line-height and font-size values.

\n\n\n\n

Currently, Theme.json Creator’s primary focus is on global styles. However, Gutenberg allows theme authors to configure default styles on the block level. For example, theme designers can set the color or typography options for the core Heading block to be different from the default global styles. This provides theme authors with fine-tuned control over every block.

\n\n\n\n

Theme.json Creator does not yet support configuration at this level. However, it would be interesting to see if Quach adds it in the future.

\n\n\n\n

The focus on setting up global styles is a good start for now. This is still an experimental feature. The great thing about it is that it can help theme authors begin to see how one piece of the block-based themes puzzle fits in. It is a starting point for an entirely new method of adding theme support for features when most are accustomed to adding multiple add_theme_support() PHP function calls.

\n\n\n\n

With the direction that theme development seems to be heading, it is easy to imagine that it could evolve into a completely UI-based affair at some point down the line. If templates are made up of blocks and patterns, which anyone can already build with the block editor, and if styles will essentially boil down to a config file, there will be little-to-no programming required to build a basic WordPress theme.

\n\n\n\n

If someone is not already at least jotting down notes for a plugin that allows users to create and package a block-based theme, I would be surprised. For now, Theme.json Creator is removing the need to write code for at least one part of the theme design process.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 07 Oct 2020 20:53:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:24;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: Jetpack 9.0 Introduces Loom Block, Twitter Threads Feature, and Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105743\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4033:\"
\n\n\n\n

Jetpack’s highly anticipated 9.0 release has landed, introducing some of the new features the team has previewed over the past week. Users can now publish WordPress posts to Twitter as threads. This new feature is available as part of the Publicize module when you have connected a Twitter account.

\n\n\n\n

Posting Twitter threads is a feature that only works with the block editor, as it takes advantage of how content is naturally split into chunks (blocks).

\n\n\n\n

In the comments on his demo post, Automattic engineer Gary Pendergast gave a more detailed breakdown of the logic Jetpack uses to ensure full sentences aren’t broken up in the tweets.

\n\n\n\n

“With the mental model now being focused on mapping blocks to tweets, it’s much easier to make logical decisions about how to handle each block,” Pendergast said. “So, a paragraph block is the text of a tweet, if the paragraph is too long for a single tweet, it tries to split the paragraph up by sentences. If a sentence is too long, then it resorts to splitting by words. Then, if there’s an embed/image/video/gallery block following that paragraph, we can attach it to the tweet containing that paragraph. There are additional rules for other blocks, but that’s the basic process. It then just iterates over all of the supported blocks in the post.”

\n\n\n\n

Pendergast published his post as thread to demonstrate the new feature in action. The advantage of posting a thread from your WordPress site is that it doesn’t end up getting lost in Twitter’s fast-moving timeline. Most important Twitter threads evaporate from public consciousness almost as soon as they are published. Publishing threads from your website ensures they are better indexed and easier to reference in the future.

\n\n\n\n

Jetpack Adds Loom Block for Embedding Screen Recordings

\n\n\n\n

Loom was added to Jetpack as a new oEmbed provider three weeks ago. The video recording service allows for recording camera, microphone, and desktop simultaneously. The service is especially popular in educational settings. Jetpack 9.0 introduces a new Loom block for embedding recordings.

\n\n\n\n\n\n\n\n

“Loom is growing in popularity as it is being recommended more and more to assist in distance learning efforts,” Jetpack Director of Innovation Jesse Friedman said. “Now more than ever we want to be able to help those working, learning, and teaching from home. The Loom block was a natural addition to join the other Jetpack video blocks which now include YouTube, TikTok, DailyMotion, and Vimeo.”

\n\n\n\n

Loom’s free tier allows users to record up to 25 videos, but the Pro plan is free for educators. Friedman confirmed that Jetpack does not have any kind of partnership with Loom. The team decided to support the product to assist professionals, educators, and students. Having it available as a block also makes it more convenient for those using P2 for communication.

\n\n\n\n

As anticipated, Jetpack 9.0 also provides a seamless transition necessary to ensure Instagram and Facebook embeds will continue working after Facebook drops unauthenticated oEmbed support on October 24. The Jetpack team reports that it “partnered with Facebook” to make sure these embeds continue to work with the WordPress.com REST API.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 23:28:38 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:25;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:51:\"Post Status: Joost de Valk on WordPress marketshare\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=79914\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:62:\"https://poststatus.com/joost-de-valk-on-wordpress-marketshare/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1193:\"

David Bisset makes his podcast debut for Post Status, as he interviews Joost de Valk, Founder and Chief Product Officer of Yoast, and discusses all things WordPress marketshare related.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Jilt

\n\n\n\n

Jilt offers powerful email marketing built for eCommerce. From newsletters to highly segmented automations, Jilt is your one-stop show for eCommerce email. Join thousands of stores that have already earned tens of millions of dollars in extra sales using Jilt. Try Jilt for free

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 22:28:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:26;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"WPTavern: iThemes Buys WPComplete, Complementing Its Recent Restrict Content Pro Acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105631\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition?utm_source=rss&utm_medium=rss&utm_campaign=ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4395:\"

Just one month after publicly announcing its acquisition of Restrict Content Pro (RCP), iThemes purchased WPComplete for an undisclosed amount. The acquisition is for the product, website, and customers only.

\n\n\n\n

Paul Jarvis and Zack Gilbert created the WPComplete plugin in 2016. However, it has outgrown what the duo could maintain and support alone. After the transition period in which the new owners take over, the two will step away from the project.

\n\n\n\n

In essence, WPComplete is a “course completion” plugin. Site owners can create online courses while allowing students/users to mark their work as completed. It also gives students a way to track their progress through courses, which can often boost the potential for them to finish.

\n\n\n\n

“Paul and Jack believe a key to their success has been their ability to keep their team small and manageable,” wrote Matt Danner, the COO at iThemes, in the announcement. “The growth of WPComplete has presented a number of challenges for a team of two people, so the decision was made to start looking towards alternative ownership solutions that could continue to grow WPComplete and provide it with a stable team. iThemes is a perfect fit.”

\n\n\n\n

iThemes customers who have a Plugin Suite or Toolkit membership will get automatic access to the pro version of the WPComplete plugin. For current WPComplete users, Danner said everything should be “business as usual.” However, iThemes has assigned a few of its team members to work on the product and site, so customers should see some new faces.

\n\n\n\n

RCP and WPComplete are obviously complementary products. RCP is a membership plugin that allows site owners to restrict content based on that membership. WPComplete allows site members to mark lessons or coursework as completed. “We’ll be rolling out a new bundle later this month that combines both RCP and WPComplete for course and membership creators to take advantage of these two plugins,” said AJ Morris, the Product Innovation and Marketing Manager at iThemes.

\n\n\n\n

WPComplete is still a young product. The free version of the plugin currently has 2,000+ active installs and a solid 4.7 rating on WordPress.org. If marketed as an extension of the RCP plugin, it automatically puts it in front of the eyes of 1,000s of more potential customers. It should be much easier to grow the plugin as part of a membership bundle.

\n\n\n\n

iThemes is making some bold moves in the membership space. It will be interesting to see if the company makes any other acquisitions that could strengthen its product line and help it become more dominant. There is still a ton of room for growth in the membership segment of the market. There is also the potential for integrations with other major plugins.

\n\n\n\n

“Adding WPComplete to the iThemes product lineup also allows us to move more quickly on some plans we have for Restrict Content Pro,” said Danner in the initial announcement. He also vaguely mentioned a couple of ideas the team had in the works but did not go into detail.

\n\n\n\n

With a little prodding, Morris provided some insight into what they are planning for the immediate future. The biggest first step is tackling integration with the block editor. Currently, WPComplete uses shortcodes. The team’s next step is likely to begin with creating block equivalents for those shortcodes.

\n\n\n\n

“After that, we’ve touched on a few deeper integrations with Restrict Content Pro, like the possibility to restrict courses to memberships,” said Morris.

\n\n\n\n

The iThemes team does not plan to stop with WPComplete as part of its product lineup. One of the goals is to use the plugin for the iThemes website itself.

\n\n\n\n

“We always try to eat our own dogfood when we can,” said Morris. “You’ll see that with RCP and WPComplete early next year as we look to integrate them into our iThemes Training membership.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 20:59:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:27;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: Exploring Full-Site Editing With the Q WordPress Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105676\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/exploring-full-site-editing-with-the-q-wordpress-theme?utm_source=rss&utm_medium=rss&utm_campaign=exploring-full-site-editing-with-the-q-wordpress-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7492:\"

I have been eagerly awaiting the moment when I could install a theme and truly test Gutenberg’s full-site editing feature. By and large, each time I have tested it over the past few months, the experience has felt utterly broken. This is why I have remained skeptical of seeing the feature land in WordPress 5.6 this December.

\n\n\n\n

The Q theme by Ari Stathopoulos is the first theme that seems to be a decent working example. Whether that is a stroke of luck with timing or that this particular theme is simply built correctly is hard to tell — Stathopoulos is a team rep for the Themes Team. Gutenberg 9.1 dropped last week with continued work toward site editing.

\n\n\n\n

Q is as experimental as it gets. The Themes Team put out an open call for experimental, block-based themes as far back as March this year. However, not many have taken the team up on this offer. If approved, Q stands to be the first block-based theme to go live in the official WordPress directory. It still has to work its way through the standard review process, awaiting its turn in the coming weeks.

\n\n\n\n

On the whole, full-site editing remains a frustrating and confusing experience. I still remain skeptical about its readiness, even in beta form, to show off to the world in WordPress 5.6.

\n\n\n\n

However, Q is an interesting theme to explore at this point for both end-users and theme developers. Users can install it and start tinkering with the site editing screen via the Gutenberg plugin. Developers can learn how global styles, templates, and template parts fit together from a working theme.

\n\n\n\n

Using the Site Editor

\n\n\n\nEditing a single post in the site editor.\n\n\n\n

The Q theme requires the Gutenberg plugin and its full-site editing mode to be enabled. Generally, requiring a plugin is not allowed for themes in the directory. However, experimental Gutenberg themes are allowed to bypass this guideline.

\n\n\n\n

Stathopoulos pointed out that the theme is highly experimental and should not be used on a production site. However, he is hopeful that it will get more eyes focused on full-site editing.

\n\n\n\n

He mentioned that several items are broken, such as category archives not showing the correct posts. This is a current limitation of the Query block in Gutenberg. However, one of the best ways to find and recognize these types of issues is to have a theme that stays up with the pace of development.

\n\n\n\n

Currently, the site editor feels like it is biting off more than it can chew. Not only can users edit the layout and design of the page, but they can also directly edit existing post content — don’t try this at home unless you are willing for your post titles to get switched to the hyphenated slug. Should the site editor be handling the double-duty of design and content editing? If so, should design and content editing be handled in separate locations in the long term or be merged into one feature?

\n\n\n\n

It feels raw. It is not geared toward users at this point.

\n\n\n\n

The bright spot with the site editor is the current progress on template parts in the editor. Template parts are essentially “modules” that handle one part of the page. For example, the typical theme will have a header and footer template part. Currently, end-users can insert custom template parts or switch one template part for another. This opens a world of possibilities, such as users choosing between multiple header designs (template parts) for their sites.

\n\n\n\nSwitching the header template part.\n\n\n\n

The downside to the entire template system is that it seems so divorced from the site editor that it is hard to believe the average user would understand what is going on. Templates and template parts reside under the Appearance menu in the admin. The Site Editor is a separate, top-level menu item. Without any preexisting knowledge of how these pieces work together, it can be confusing.

\n\n\n\n

Template parts worked for me in the site editor from the outset. However, they did not work on the front end at first. I continually received the “template part not found” message for hours. Then, at some point — whether through magic or a random save that pulled everything together — the feature began to output the previously-missing header and footer template parts.

\n\n\n\n

Glimpse Into the Future of Theme Development

\n\n\n\n

The Q theme has a scant few style rules, which it loads directly in the <head> section of the site in lieu of adding an extra stylesheet. It relies on the stock Gutenberg block styles on the front end with a few minor overrides. Most other custom styles are handled via the global styles system, which pulls from the theme’s experimental-theme.json config file (will be theme.json in the future).

\n\n\n\n

It begs the question of whether themes will necessarily need much in the way of CSS when full-site editing lands.

\n\n\n\n

If WordPress allows users to configure most styles via block options and global styles overrides, themes may not need much more than their config files. After that, it would come down to registering custom block styles and patterns.

\n\n\n\n

If this is the future that we are headed toward, anyone could essentially create a WordPress theme. And, those pieces, such as template parts and patterns, could all be shared between any site. In that future, themes may simply not matter anymore.

\n\n\n\n

Last year, Mike Schinkel proposed deprecating the theme system altogether and replacing it with web components.

\n\n\n\n

“Rather than look for a theme that has all the features one needs — which I have found always limits the choices to zero — a site owner could look for the components and modules they need and then assemble their site from those modules,” he said. “They could pick a header, a footer, a home-page hero, a set of article cards, a pricing module, and so on.”

\n\n\n\n

The more I tinker with full-site editing, the more it feels like that is the lane that it will ultimately merge into. Imagine a future where end-users could pick and choose the pieces they wanted and simply have it look right on the front end.

\n\n\n\n

It is exciting to think about that possibility. Both Schinkel and I have more of a background in programming than we do in design. It makes sense from that sort of analytical mindset to put everything into neat, reusable boxes because reuse is a cornerstone of smart programming.

\n\n\n\n

However, I worry about the state of design in such a system with so many replaceable parts. Will designers be able to take holistic approaches to theme development, creating truly intricate pieces of art? Will that system essentially create a web of cookie-cutter sites? Or, will designers simply find ways to think outside the box while within the constraints of the block system?

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 21:21:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:28;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Virtual Jamstack Conf to Feature Fireside Chat with Matt Mullenweg and Matt Biilmann, October 6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105680\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6?utm_source=rss&utm_medium=rss&utm_campaign=virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2618:\"
image credit: Jamstack Conf
\n\n\n\n

The greater Jamstack community is coming together on October 6-7, 2020, for a virtual conference. Organizers expect more than 15,000 attendees from around the globe over a two-day span that includes keynotes, sessions, interactive topic tables, workshops, speaker Q&As, and networking opportunities.

\n\n\n\n

Matt Mullenweg will be joining Netlify CEO Matt Biilmann on day 1 at 12PM PDT for a fireside chat moderated by CSS-Tricks Creator Chris Coyier. The chat will go deeper on recent topics of contention, including developer sentiment, complexity, security, and performance. Coyier also plans to discuss how the Jamstack and WordPress communities intersect through headless implementations of the CMS.

\n\n\n\n

A provocative post from TheNewStack at the end of August quoted Mullenweg as saying that “JAMstack is a regression for the vast majority of the people adopting it.” This sparked multiple heated exchanges across blogs and social media. Biilimann, who originally coined the term “Jamstack,” wrote a response to Mullenweg’s remarks, hailing “the end of the WordPress era.”

\n\n\n\n

Live conversations tend to be more cordial than shots fired across the blogosphere. It will be interesting to see if Biilimann cares to join Stackbit CEO Ohad Eder-Pressman in his wager that Jamstack will become the predominant architecture for the web by 2025. The fireside chat should be recorded, in case you cannot catch the live session. Recordings of talks from the previous virtual Jamstack event held in May are available on YouTube.

\n\n\n\n

Today is the last call for registration. Many of the workshops have already sold out, but tickets to the regular sessions on October 6 are still available. Sign up on the event website to get your free ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 20:12:50 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:29;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Gutenberg 9.1 Adds Patterns Category Dropdown and Reverts Block-Based Widgets in the Customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105629\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5615:\"

Gutenberg 9.1 was released to the public on Wednesday. The team announced over 200 commits from 77 contributors in its release post yesterday. One of the biggest changes to the interface was the addition of a new dropdown selector for block pattern categories. The team also reverted the block-based widgets section in the customizer and added an image size control to the Media & Text block.

\n\n\n\n

One of the main focuses of this release was improving the block-based widgets editor. The feature was taken out of the experimental stage in Gutenberg 8.9 and continues to improve. The widgets screen now uses the same inserter UI as the post-editing screen. However, users can currently only insert regular blocks. Patterns and reusable blocks are still not included.

\n\n\n\n

Theme authors can now control aspects of the block editor via a custom theme.json file. This is part of the ongoing Global Styles project, which will allow theme authors to configure features for their users.

\n\n\n\n

The development team has also added an explicit box-sizing style rule to the Cover and Group blocks. This is to avoid any potential issues with the new padding/spacing options. Theme authors who rely on the block editor styles should test their themes to make sure this change does not break anything.

\n\n\n\n

Better Pattern Organization

\n\n\n\nNew block patterns UI in the inserter.\n\n\n\n

I have been calling for the return of the tabbed pattern categories since Gutenberg 8.0, which was a regression from previous versions. For 11 versions, users have had to scroll and scroll and scroll through every block pattern just to find the one they wanted. The development team has sought to address this issue by using a category dropdown selector. When selecting a specific category, its patterns will appear.

\n\n\n\n

At first, I was unsure about this method over the old tabbed method. However, after some use, it feels like the right direction.

\n\n\n\n

As more and more theme and plugin authors add block pattern categories to users’ sites, the dropdown is a more sensible route. Even tabs could become unwieldy over time. The dropdown better organizes the list of categories and makes the UI cleaner. More than anything, I am enjoying the experience and look forward to this eventually landing in WordPress 5.6 later this year.

\n\n\n\n

Customizer Widgets Reverted

\n\n\n\nReverted widgets panel in the customizer.\n\n\n\n

On the subject of WordPress 5.6, one of its flagship features has been hitting some roadblocks. Block-based widgets are expected to land in core with the December release, but the team just reverted part of the feature. They had to remove the widgets block editor from the customizer they added just two major releases ago.

\n\n\n\n

It was for the best. The customizer’s block-based widgets editor was fundamentally broken. It was not ready for primetime and should have remained in the experimental stage until it was somewhat usable.

\n\n\n\n

“I will approve this since the current state of the customizer in the Gutenberg plugin is broken, and there is no clear path forward about how to fix that,” wrote Andrei Draganescu in the reversion ticket. “With this patch, the normal widgets can still be edited in the customizer and the block ones don’t break it anymore. This is NOT to mean that we won’t proceed with fixing the block editor in the customizer, that is still an ongoing discussion.”

\n\n\n\n

The current state of editing widgets via the customizer is at least workable with this change. If end-users add a block via the admin-side widgets editor, it will merely appear as an uneditable, faux widget named “Block” in the customizer. They will need to edit blocks via the normal widgets screen.

\n\n\n\n

There is no way that WordPress can ship the current solution when 5.6 rolls out. However, we are still two months out. This leaves plenty of time for a fix, but Draganescu’s note that “there is no clear path forward” may make some people a bit uneasy at this stage of development.

\n\n\n\n

Control Image Size for Media & Text

\n\n\n\nImage size dropdown selector for the Media & Text block.\n\n\n\n

One of the bright spots in this update is the addition of an image size control to the Media & Text block. Like the normal Image block, end-users can choose from any registered image size created for their uploaded image.

\n\n\n\n

This is a feature I have been looking forward to in particular. Previously, using the full-sized image often made the page weight a bit heftier than necessary. It is also nice to go along with themes that register sizes for both landscape and portrait orientations, giving users more options.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 20:56:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:30;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"WordPress.org blog: The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8711:\"

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:31;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches New Web Analytics Product Focusing on Privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105446\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-new-web-analytics-product-focusing-on-privacy?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-new-web-analytics-product-focusing-on-privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2448:\"

In pursuit of “democratizing web analytics,” Cloudflare announced it is launching privacy-first analytics as a new standalone product. The company is entering a market that has been dominated by Google Analytics for years but with a major differentiating feature – it will not track individual users by a cookie or IP address to show unique visits.

\n\n\n\n

Cloudflare Web Analytics defines a visit as “a successful page view that has an HTTP referer that doesn’t match the hostname of the request.” It’s not the same as Google’s “unique” metric, and Cloudflare says it may differ from other reporting tools. Weeding out bots from the total traffic numbers is a nascent feature that Cloudflare is improving as part of its Bot Management product.

\n\n\n\n
\n\n\n\n

Cloudflare Web Analytics is launching with features that are largely similar to Google Analytics but with some unique ways of zooming into different traffic segments and time ranges to see where traffic is originating from.

\n\n\n\n

“The most popular analytics services available were built to help ad-supported sites sell more ads,” Cloudflare product manager Jon Levine said. “But, a lot of websites don’t have ads. So if you use those services, you’re giving up the privacy of your users in order to understand how what you’ve put online is performing.

\n\n\n\n

“Cloudflare’s business has never been built around tracking users or selling advertising. We don’t want to know what you do on the Internet — it’s not our business.”

\n\n\n\n

Paying customers on the Pro, Biz, and Enterprise plans can access their analytics from their dashboards immediately. Cloudflare is also offering the product for free as JavaScript-based analytics for users who are not currently customers. Those who want access to the free plan can sign up for the waitlist.

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 04:03:01 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:32;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"WPTavern: Virtual WordPress Page Builder Summit Kicks Off October 5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105570\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:179:\"https://wptavern.com/virtual-wordpress-page-builder-summit-kicks-off-october-5?utm_source=rss&utm_medium=rss&utm_campaign=virtual-wordpress-page-builder-summit-kicks-off-october-5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6348:\"

From October 5 through October 9, the first Page Builder Summit will open its virtual doors to all attendees for free. Nathan Wrigley, the podcaster behind WP Builds, and Anchen le Roux, the founder and lead developer of Simply Digital Design, are hosting the five-day online event that focuses on the vast ecosystem of page builders for WordPress.

\n\n\n\n

The summit will include 35 sessions spread out over the event schedule. Each session will last around 30 minutes, so it will be easy to pop in and watch one in your downtime. Sessions will cover a range of builders, including the default WordPress block editor, Elementor, Beaver Builder, Oxygen, Brizy, and Divi.

\n\n\n\n

“It’s an event specifically for users of WordPress page builders, or those curious about what they can do,” said Wrigley. “I feel like a page builder style interface for creating websites is the future for our industry. WordPress itself is moving in this direction with the block editor (a.k.a. Gutenberg). With that in mind, it seemed like a good idea to create a dedicated event to share knowledge about this side of WordPress. We’ve tried to include presentations from as many page builders as we could.”

\n\n\n\n

Wrigley made sure to point out that it is not all geared toward developers, discussing the inner-workings of builders. Some of the sessions focus on marketing, optimization, and conversion, which provides a wider range of topics for potential attendees.

\n\n\n\n

The summit hosts created an online quiz for those who are unsure about which sessions to watch.

\n\n\n\n

There is a small catch. The sessions will be freely available only from the time they begin and the following 24 hours. After that, accessing the videos will come at a premium. Attendees can gain lifetime access to the PowerPack for $47 if they purchase within 15 minutes of signing up. Then, prices will rise to $97 until the event kicks off on October 5. Beyond, the price jumps to $147. The lifetime access includes access to the presentations, transcripts, a workbook, and other bonuses from the speakers.

\n\n\n\n

For those unsure about forking over the cash, they can still watch the sessions during the 24-hour window.

\n\n\n\n

The proceeds from the event will go out to paying affiliate commissions to speakers and partners. Some of it will go into planning and investing in a second summit down the road.

\n\n\n\n

“Both myself and Nathan have specific charities that we want to donate to after the event,” said le Roux. “It was part of our goals to be able to do this, but we didn’t want to make this an official contribution.”

\n\n\n\n

Why a Page Builder Summit?

\n\n\n\n

Both Wrigley and le Roux have their preferred builders. But, the goal of the summit is to offer a wide look at the tools available and help freelancers and agencies better streamline their businesses and create happier clients.

\n\n\n\n

“I’ve been a user of page builders for many years, but only at the point where they truly showed in the editing interface something that almost perfectly reflected what the end-user would see did I get really immersed,” said Wrigley. “Having come from a background in which I built entire websites from a collection of text files (HTML, CSS, PHP, etc.), I was fascinated that we’d reached a point where the learning curve for building a good website was significantly reduced.”

\n\n\n\n

He pointed out that it is not always so simple though. While the same level of coding skills may not be necessary, people must figure out how to navigate their preferred page builder, which can come with its own learning curve.

\n\n\n\n

“You need to learn their way of doing things and how to achieve your design choices,” he said. “It’s always going to work out better if you know the code, but the WordPress mission of democratizing publishing certainly seems to align quite nicely with the adoption of tools, like page builders, which mean that once-difficult tasks are now easier.”

\n\n\n\n

For le Roux, her interest in hosting the Page Builder Summit falls back to her design studio.

\n\n\n\n

“As a developer, my main reason for switching to page builders was around streamlining and creating more efficient but quality websites in the shortest amount of time,” she said. “Especially now that we focus on day rates, creating the best possible website that clients would love fast would not have been possible without page builders.”

\n\n\n\n

The Hosts’ Go-To Builders

\n\n\n\n

“We prefer using Beaver Builder with Themer at Simply Digital Design,” said le Roux. “We use Gutenberg for blog posts or where possible with custom post types or LMS software. However, we’ve also taken on a few Elementor projects where that’s the client’s preferred option.”

\n\n\n\n

Wrigley uses some of the same tools. His main work is on the WP Builds website where he hosts podcasts.

\n\n\n\n

“I have used Beaver Builder’s Themer to create templates for specific layouts, but for content creation within those layouts I’m using the block editor,” said Wrigley. “My content is mainly text and the WordPress editor is utterly remarkable in this situation. I kept the classic editor installed for a few months after WordPress 5.0 came about, but I soon realized that this was folly and that the editing interface of Gutenberg is superior. The ability to insert and move text, buttons, etc. is such a joy to work with, and the iterations that have been made in the last two years make it, in my opinion, the best text editing experience on the web.”

\n\n\n\n

Wrigley sees a future in which the WordPress block editor takes over much of the work that page builders are currently handling. However, that future is “still over the horizon.”

\n\n\n\n

“I’m excited about this future though, and we’ve got a few crystal ball-gazing presentations; trying to work out what that future might look like,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 20:31:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:33;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"WPTavern: Jetpack 9.0 to Introduce New Feature for Publishing WordPress Posts to Twitter as Threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105448\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3318:\"

Jetpack 9.0, coming on October 6, will debut a new feature that allows users to share blog posts as Twitter threads in multiples tweets. A recent version of Jetpack introduced the ability to import and unroll tweetstorms for publishing inside a post. The 9.0 release will run it back the other way so the content originates in WordPress, yet still reaps all the same benefits of circulation on Twitter as a thread.

\n\n\n\n

The new Twitter threads feature is being added as part of Jetpack’s Publicize module under the Twitter settings. After linking up a Twitter account, the Jetpack sidebar options for Publicize allow users to publish to Twitter as a link to the blog or a set of threaded tweets. It’s not just limited to text content – the threads feature will also upload and attach any images and videos included in the post.

\n\n\n\n\n\n\n\n

When first introduced to the idea of publishing a Twitter thread from WordPress, I wondered if threads might lose their trademark pithy punch, since users aren’t forced to keep each segment to the standard length of a tweet. Would each tweet be separated in an odd, unreadable way? The Jetpack team anticipated this, so the thread option adds more information to the block editor to show where the paragraphs will be split into multiple tweets.

\n\n\n\n

“Threads are wildly underused on Twitter,” Gary Pendergast said in a post introducing the feature. “I think a big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.” The tool Pendergast has been working on for Jetpack gives users the best of both worlds.

\n\n\n\n

In response to a comment requesting Automattic “concentrate on tools to get people off social media,” Pendergast said, “If we’re also able to improve the quality of conversations on social media, I think it’d be remiss of us to not do so.” He also credits IndieWeb discussions on Tweetstorms and POSSE (Publish (on your) Own Site, Syndicate Elsewhere) as inspirations for the feature.

\n\n\n\n

For years, blogging advocates have tried to convince those who post lengthy tweetstorms to switch to a publishing medium that is more suitable to the length of their thoughts. The problem is that Twitter users lose so much of the immediate feedback and momentum that their thoughts would have generated when composed as a tweetstorm.

\n\n\n\n

Instead of lecturing people about how they should really be blogging instead of tweetstorming, Jetpack is taking a fresh approach by enabling full content ownership with effortless social syndication. You can test out the experience for yourself by adding the Jetpack Beta Testers plugin and running the 9.0 RC version on your site.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 02:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:34;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: Ask the Bartender: How To WordPress in a Block World?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105491\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:167:\"https://wptavern.com/ask-the-bartender-how-to-wordpress-in-a-block-world?utm_source=rss&utm_medium=rss&utm_campaign=ask-the-bartender-how-to-wordpress-in-a-block-world\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9755:\"

I love your articles. And now, in the middle of the WordPress revolution, I realized I’m constantly searching for an answer regarding WP these days.

So many things are being said, so many previsions of the future, problems, etc., but, right now, I think I, as a designer, just want to understand one thing that seemed answered already but it’s never clear:

Is WordPress a good choice to build a client’s template where he just has to insert the info that will show in the frontend where I want to? And he doesn’t have to worry about formatting blocks? I love blocks, don’t get me wrong, but will normal templating end?

I just think that having a super CMS, HTML, CSS, and being able to play with a database with ACF is so powerful, that I’m wondering if it’s lost. After so much reading, I still don’t understand if this paradigm is going to disappear.

Right now, I don’t know if it’s best to stop making websites as I used to and adopt block patterns instead.

Ricardo
\n\n\n\n

WordPress is definitely changing. Over the past two years, we have seen much of it reshaped into something different from the previous decade and more. However, this is not new. WordPress has always been a constantly-changing platform. It just feels far too different this time around, almost foreign to many. The platform had to make a leap. Otherwise, it would have started falling behind.

\n\n\n\n

And, it is a big ask of the existing community to come along with it, to take that leap together.

\n\n\n\n

It can be scary as a developer whose livelihood has depended on things working a certain way or who has built tools and systems around pre-block WordPress. Many freelancers and agencies had their world turned upside down with the launch of the block editor. It is perfectly OK to feel a bit lost.

\n\n\n\n

Now, it is time for a little tough love. It has been two years. As a professional, you need to have a plan in place already. Whether that is an educational plan for yourself or a transitional plan for your clients, you should already be tackling projects that leverage the block editor. If you are at a point where you have not been building with blocks, you are now behind. However, you can still catch up and continue advancing in your WordPress career.

\n\n\n\n

There are so many changes coming down the pipeline that anyone who plans to develop for WordPress will be in continual education mode for years to come.

\n\n\n\n

When building for clients, the biggest thing to remember is that it is not about you. It is about getting something into the hands of your clients that addresses their specific needs. Freelancers and agencies need to often be the Jacks and Jills of all trades. Sometimes, this even means having a backup CMS or two that you can use that are not named WordPress. It helps to be well-rounded enough to jump around when needed, especially if you are not at a point in your career where you can demand specific work and pass on jobs that would put food on the table.

\n\n\n\n

It is also easy to look at every job as a nail and WordPress as the hammer. Or, even specific plugins as the tool that will always get the job done. I have seen developers in the past rely on tools like ACF, CMB2, or Meta Box but could not code a custom metadata solution when necessary to save their life. Sometimes a bigger toolbox is necessary.

\n\n\n\n

Every WordPress developer needs a solid, foundational understanding of the languages that WordPress uses. Gone are the days of skating by on HTML, CSS, and PHP knowledge. You need to learn JavaScript deeply. Matt Mullenweg, the co-founder of WordPress, was not joking around when he said this back in 2015. It holds true more and more each day. In another five years, it will tough to be a developer in the WordPress world without knowing JavaScript, at least for backend work.

\n\n\n\n

It also depends on what types of sites you are building. If you are primarily handling front-end design, you will likely be able to get by with a lower skill level. You will just need to know the “WordPress way” of building themes.

\n\n\n\n

Within the next year, you should be able to build just about any theme design with decent CSS and HTML knowledge along with an understanding of how the block system works. Full-site editing and block-based themes will change how we build the front end of the web. It is going to be a challenging transition at first, especially for those of us who are steeped in traditional theme development, but client sites will often be far easier to build. I highly recommend the twice-monthly block-based themes meetings if your focus is on the front end.

\n\n\n\n

Block Templates

\n\n\n\n

Based on your question, I am going to make some assumptions. You have a history of essentially building out meta boxes via ACF where the client just pops in their data. Then, you format that data on the front end. You are likely mixing this with custom post types (CPTs). This is a fairly common scenario.

\n\n\n\n

One of the great things about the block system is that you can lock the post editor for individual CPTs. WordPress already has you covered with its block templates feature, which allows you to define just what a post should look like. You can set up which blocks you want to appear and have the client drop their content in. At the moment, this feature is limited to the post type level. However, it should grow more robust over time, particularly when it works alongside the traditional “page templates” system.

\n\n\n\n

Block templates are a powerful tool in the ol’ toolbox that will come in handy when building client sites.

\n\n\n\n

Block Patterns

\n\n\n\n

You do not have to stop making websites as you are accustomed to at the moment. However, you should start leveraging new block features as they become available and make sense for a specific project. I am a fanatic when it comes to block patterns, so my bias will definitely show.

\n\n\n\n

The biggest thing with block patterns and clients is education. For the uninitiated, you will need to spend some time teaching them how to insert a pattern and how it can be used to their advantage. That is the hurdle you must jump.

\n\n\n\n

For many of the users that I have seen introduced to well-designed patterns, they have fallen in love with the feature. Even many who were reluctant to switch to the block editor became far more comfortable working with it after learning how patterns worked. This is not the case for every user or client, but it has been a good introduction point to the block editor for many.

\n\n\n\n

To answer your question regarding patterns: yes, you should absolutely begin to adopt them.

\n\n\n\n

ACF Is Evolving

\n\n\n\n

Because you are accustomed to ACF, you should be aware that the framework is evolving to keep up with the block editor. Version 5.8.0 introduced a PHP framework for creating custom blocks over a year ago. And, it has been improving ever since. There are even projects like ACF Blocks, which will provide even more tools for your arsenal.

\n\n\n\n

It is important to learn from what some of the larger agencies are doing. Read up on how WebDevStudios is tackling block development. The company also has an open-source block library for ACF.

\n\n\n\n

Solving Problems

\n\n\n\n

Your job as a developer is to be a problem solver. Whatever system you are building with is merely a part of your toolset. You need to be able to solve issues regardless of what tool you are using. At the end of the day, it is just code. If you can learn HTML, you can learn CSS. If you can learn those, you can learn PHP. And, if you can manage PHP, you can certainly pick up JavaScript.

\n\n\n\n

A decade or two from now, you will need to learn something else to stay relevant in your career. Web technology changes. You must change with it. Always consider yourself a student and continue your education. Surround yourself and learn from those who are more advanced than you. Emulate, borrow, and steal good ideas. Use what you have learned to make them great.

\n\n\n\n

There is no answer I can give that will be perfect for every scenario. Each client is unique, and you will need to decide the best direction for each.

\n\n\n\n

However, yes, you should already be on the path to building with a block-first mindset if you plan to continue working with WordPress for the long haul. Immerse yourself in the system. Read, study, and build something any chance you get.

\n\n\n\n

This is the first post in the Ask the Bartender series. Have a question of your own? Shoot it over.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 30 Sep 2020 20:35:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:35;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: Supercharge the Default WordPress Theme With Twentig, a Toolbox for Twenty Twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105344\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:225:\"https://wptavern.com/supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty?utm_source=rss&utm_medium=rss&utm_campaign=supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6455:\"Custom page pattern from the Twentig plugin.\n\n\n\n

I am often on the hunt for those hidden gems when it comes to block-related plugins. I like to see the interesting places that plugin authors venture. That is why it came as a surprise when someone recommended I check out the Twentig plugin a few days ago. Somehow, it has flown under my radar for months. And, it has managed to do this while being one of the more interesting plugins for WordPress I have seen in the past year.

\n\n\n\n

Twentig is a plugin that essentially gives superpowers to the default Twenty Twenty theme. Diane and Yann Collet are the sibling co-founders and brains behind the plugin.

\n\n\n\n

While I have been generally a fan of Twenty Twenty since it was first bundled in core, it was almost a bit of a letdown in some ways. It was supposed to be the theme that truly showcased what the block editor could do — and it does a fine job of styling the default blocks — but there was a lot of potential left on the table. The Twentig plugin turns Twenty Twenty into something worthier of a showcase for the block editor. It is that missing piece, that extra mile in which WordPress should be marching its default themes.

\n\n\n\n

While the new Twenty Twenty-One default theme is just around the corner, Twentig is breathing new life into the past year’s theme. The developers behind the plugin are still fixing bugs and bringing new features users.

\n\n\n\n

Of its 34 reviews on WordPress.org, Twentig has earned a solid five-star rating. That is a nice score for a plugin with only 4,000 active installations. As I said, it has flown under the radar a bit, but the users who have found it have obviously discovered something that adds those extra touches to their sites they need.

\n\n\n\n

What Does Twentig Do?

\n\n\n\n

It is a toolbox for Twenty Twenty. The headline feature is its block editor features, such as custom patterns and page layouts. It also offers a slew of customizer options that allow end-users to put their own design spin on the default theme. However, my interest is primarily in how it extends the block editor.

\n\n\n\n

Let’s get this out of the way up front. Twentig’s one downside is that it adds a significant amount of additional CSS on top of the already-heavy Twenty Twenty and block editor styles. I will blame the current lack of a full design system from WordPress on most of this. Styling for the block editor can easily bloat a stylesheet. Adding an extra 100+ kb per page load might be a blocker for some who would like to try the plugin. Users will need to weigh the trade-offs between the additional features and the added page size.

\n\n\n\n

The thing that makes Twentig special is its extensive patterns and pages library, which offers one-click access to hundreds of layouts specifically catered to the Twenty Twenty theme.

\n\n\n\nInserting one of the hero patterns.\n\n\n\n

It took me a few minutes to figure out how to access the patterns — mainly because I did not read the manual. I expected to find them mixed in with the core patterns inserter. However, the plugin adds a new sidebar panel to the editor, which users can access by clicking the “tw” icon. After seeing the list of options, I can understand why they probably would not fit into WordPress’s limited block and patterns inserter UI.

\n\n\n\n

It would be easier to list what the plugin does not have than to go through each of the custom patterns and pages.

\n\n\n\n

The one thing that truly sets this plugin apart from the dozens of other block-library types of plugins is that there are no hiccups with the design. Almost every similar plugin or tool I have tested has had CSS conflicts with themes because they are trying to be a tool for every user. Twentig specifically targets the Twenty Twenty theme, which means it does not have to worry about whether it looks good with the other thousands of themes out there. It has one job, which is to extend its preferred theme, and it does it with well-designed block output.

\n\n\n\n

The other aspect of this is that it does not introduce new blocks. Every pattern and page layout option uses the core WordPress blocks, which includes everything from hero sections to testimonials to pricing tables to event listings. And more.

\n\n\n\n

Twentig does not stop adding features to the block editor with custom patterns. The useful and sometimes fun bits are on the individual block level, and I have yet to explore everything. I continue to discover new settings each time I open my editor.

\n\n\n\n

Whether it is custom pullquote styles, a photo image frame, or an inner border tweak to the Cover block (shown below), the plugin adds little extras that push what users can do with their content.

\n\n\n\nInner border style for the Cover block.\n\n\n\n

Each block also gets some basic top and bottom margin options, which comes in handy when laying out a page. At this point, I am simply looking forward to discovering features I have yet to find.

\n\n\n\n

Areas Themes Should Explore

\n\n\n\n

One of the things I dislike about many of these features being within the Twentig plugin is that I would like to see them within the Twenty Twenty theme instead. Obviously not every feature belongs in the theme — some features firmly land in plugin territory. The default WordPress themes should also leave some room for plugin authors to explore. But, shipping some of the more prominent patterns and styles with Twenty Twenty would make a more robust experience for the average end-user looking to get the most out of blocks.

\n\n\n\n

Block patterns were not a core WordPress feature when Twenty Twenty landed. However, for the upcoming Twenty Twenty-One theme, which is expected to bundle some unique patterns, the design team should explore what the Twentig plugin has brought to the current default. That is the direction that theme development should be heading, and theme developers can learn a lot by stealing borrowing from this plugin.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 22:00:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:36;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Coming in Jetpack 9.0: Shortcode Embeds Module Updated to Handle Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105381\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2938:\"

Facebook and Instagram are dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers in an upcoming release. After evaluating third-party solutions, WordPress VIP is recommending its partners enable Jetpack’s Shortcode Embeds module. Jetpack will be shipping the update in its 9.0 release, which is anticipated to land prior to the October 24th deadline.

\n\n\n\n

The module is being updated to provide a seamless transition for users who might otherwise be negatively impacted by Facebook’s upcoming API change. WordPress contributors have run some simulations but are not yet sure what will happen to the display for previously embedded content.

\n\n\n\n

“It is possible that they change the contents of the JS file to manipulate cached embeds, perhaps to display a warning that the site is using an old method to embed content or that the request is not properly authenticated,” Jonathan Desrosiers commented on the trac ticket for removing the oEmbed providers.

\n\n\n\n

WordPress.com VIP roughly outlined what users can expect if they do not enable a solution to begin authenticating oEmbeds:

\n\n\n\n

By default, WordPress caches oEmbed contents in post metadata. These embeds will continue to display in previously-published content. If you edit older posts in the Block Editor, regardless of whether you update the post by saving changes, the embeds in the post will no longer be cached and will stop displaying. If you view these older posts using the Classic Editor, so long as the post is not re-saved, the embeds will continue to function and display properly. If you update the post content, the embed will cease functioning unless you have a mitigation installed.

\n\n\n\n

Although WordPress VIP recommends using the Jetpack module as the best solution, self-hosted WordPress users may want to investigate other options if they are not already using Jetpack. oEmbed Plus is a free plugin created specifically for solving the problem of WordPress dropping Facebook and Instagram as oEmbed providers but it is more work to set up and configure. It requires users to register as a Facebook developer and create an app to get API credentials.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 21:18:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:37;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:52:\"WPTavern: W3C Selects Craft CMS for Redesign Project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105265\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:149:\"https://wptavern.com/w3c-selects-craft-cms-for-redesign-project?utm_source=rss&utm_medium=rss&utm_campaign=w3c-selects-craft-cms-for-redesign-project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9407:\"

W3C has selected Craft CMS over Statamic for its redesign project, after dropping WordPress from consideration in an earlier round of elimination:

\n\n\n\n

In the end, our decision mostly came down to available resources. Craft had already committed to reach AA compliance in Craft 4 (it is currently on version 3.5, the release of version 4 is planned for April 2021). They had also arranged for an external agency to provide them with accessibility issues to tackle weekly. In the end, they decided instead to hire an in-house accessibility specialist to perform assessments and assist the development team in adopting accessibility patterns in the long run.

W3C CMS Selection Report
\n\n\n\n

Last week we published a post urging W3C to revisit Gutenberg for a fair shake against the proprietary CMS’s or consider adopting another open source option. During the selection process, Studio 24, the agency contracted for the redesign, cited its extensive experience with WordPress as the reason for not performing any accessibility testing on more recent versions of Gutenberg.

\n\n\n\n

When asked if the team contacted anyone from WordPress’ Accessibility Team during the process or put Gutenberg through the same tests as the proprietary CMS’s, Studio 24 founder Simon Jones confirmed they had not.

\n\n\n\n

“No, we only reached out to the two shortlisted CMS’s” Jones said. “I’m afraid we didn’t have time to do more. We did test GB a few months ago based on editing content – though it wasn’t the only factor in our choice. As an agency we do plan to keep reviewing GB in the future.”

\n\n\n\n

In response to our concerns regarding licensing, Jones penned an update titled “On not choosing WordPress,” which further elaborated on the reasons why the agency was not inclined towards using or evaluating the new editor:

\n\n\n\n

From a business perspective I also believe Gutenberg creates a complexity issue that makes it challenging for use by many agencies who create custom websites for clients; where we have a need to create lots of bespoke blocks and page elements for individual client projects.

The use of React complicates front-end build. We have very talented front-end developers, however, they are not React experts – nor should they need to be. I believe front-end should be built as standards-compliant HTML/CSS with JavaScript used to enrich functionality where necessary and appropriate.

As of yet, we have not found a satisfactory (and profitable) way to build custom Gutenberg blocks for commercial projects. 

\n\n\n\n

The CMS selection report also stated that W3C needs the CMS to be “usable by non-sighted users” by the launch date, since some members of the staff who contribute to the website are non-sighted.

\n\n\n\n

Since the most recent version of WordPress was not tested in comparison with the proprietary CMS’s, it’s unclear how much better they handle accessibility. Ultimately, W3C and Studio 24 were more comfortable moving forward with a proprietary vendor that was able to make certain assurances about the future accessibility of its authoring tool, despite having a smaller pool of contributors.

\n\n\n\n

“[I’m] also deeply curious since the cursory notes on accessibility for both of the reviewed CMSes seem to highlight a ton of issues like ‘Buttons and Checkboxes are built using div elements’ or most inputs lacking clear focus styles,” Gutenberg technical lead Matías Ventura said. “An element like the Calendar for choosing a post date seems entirely inoperable with keyboard on Craft, for example, while WordPress’ has had significant effort and rounds of feedback poured into that element alone to make it fully operable.”

\n\n\n\n

WordPress developer Anthony Burchell commented on how using a relatively new proprietary CMS seemed counter to W3C’s stated goal to select an option on the basis of longevity. Craft CMS’s continued success is contingent upon its business model and the company’s ability to remain profitable.

\n\n\n\n

“FOSS have the same opportunity of direct access to developers,” Burchell said. “I recognize there are many accessibility shortcomings in popular software, but I think it’s more constructive to rally behind and contribute, not use a proprietary CMS that boasts beer budget in their guidelines.”

\n\n\n\n

On the other side of the issue, accessibility advocates took the W3C’s decision as a referendum on Gutenberg’s continued struggles to meet WCAG AA standards. WordPress accessibility specialist Amanda Rush said it was “nice to see the W3C flip tables over this.”

\n\n\n\n

“Gutenberg is not mature software,” accessibility consultant and WordPress contributor Joe Dolson said in a post elaborating on his comments at WPCampus 2020 Online. He emphasized the lack of stability in the project that Studio 24 alluded to when documenting the reasons against using WordPress.

\n\n\n\n

“It is still undergoing rapid changes, and has grand goals to add a full-site editing experience for WordPress that almost guarantees that it will continue to undergo rapid changes for the next few years,” Dolson said. “Why would any organization that is investing a large amount into a site that they presumably hope will last another 10 years want to invest in something this uncertain?”

\n\n\n\n

Dolson also said the accessibility improvements he referenced regarding the audit were only a small part of the whole picture.

\n\n\n\n

“They only encompass issues that existed in the spring of 2019,” he said. “Since then, many features have been added and changed, and those features both resolve issues and have created new ones. The accessibility team is constantly playing catch up to try and provide enough support to improve Gutenberg. And even now, while it is more or less accessible, there are critical features that are not yet implemented. There are entirely new interface patterns introduced on a regular basis that break prior accessibility expectations.”

\n\n\n\n

WordPress is also being used by millions of people who are constantly reporting issues to fuel the software’s continued refinement, which increases the backlog of issues. Unfortunately, Studio 24 did not properly evaluate Gutenberg against the proprietary CMS’s in order to determine if these software projects are in any better shape.

\n\n\n\n

Instead, they decided that Craft CMS’s community was more receptive to collaborating on issues without reaching out to WordPress. Given the W3C’s stated preference for open source software, WordPress, as the only CMS under consideration with an OSD-compliant license, should have received the same accessibility evaluation.

\n\n\n\n

“I can’t make any statements that would be meaningful about the other content management systems under consideration; but if WordPress wants to be taken seriously in environments where accessibility is a legal, ethical, and mission imperative, there’s still a lot of work to be done,” Dolson said.

\n\n\n\n

Studio 24’s evaluation may not have been equitable to the only open source CMS under consideration, but the situation serves to highlight a unique quandary: when using open source software becomes the impractical choice for organizations requiring a high level of accessibility in their authoring tools.

\n\n\n\n

“Studio 24 ultimately determined that working with a CMS to make it better was more possible with a smaller, proprietary vendor than with a large open-source project,” accessibility advocate Brian DeConinck said. “Project leadership would be more receptive, and the smaller community means changes can be made more quickly. That should prompt a lot of soul-searching for…well, everyone. What does that say about the future of open source?”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 04:56:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:38;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"Gary: More than 280 characters\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:25:\"https://pento.net/?p=5405\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://pento.net/2020/09/29/more-than-280-characters/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5187:\"

It’s hard to be nuanced in 280 characters.

\n\n\n\n

The Twitter character limit is a major factor of what can make it so much fun to use: you can read, publish, and interact, in extremely short, digestible chunks. But, it doesn’t fit every topic, ever time. Sometimes you want to talk about complex topics, having honest, thoughtful discussions. In an environment that encourages hot takes, however, it’s often easier to just avoid having those discussions. I can’t blame people for doing that, either: I find myself taking extended breaks from Twitter, as it can easily become overwhelming.

\n\n\n\n

For me, the exception is Twitter threads.

\n\n\n\n

Twitter threads encourage nuance and creativity.

\n\n\n\n

Creative masterpieces like this Choose Your Own Adventure are not just possible, they rely on Twitter threads being the way they are.

\n\n\n\n
\n

Being Beyoncé’s assistant for the day: DONT GET FIRED THREAD pic.twitter.com/26ix05Hkhp

— green chyna (@CORNYASSBITCH) June 23, 2019
\n
\n\n\n\n

Publishing a short essay about your experiences in your job can bring attention to inequality.

\n\n\n\n
\n

DOWNTOWN BROOKLYN: I\'m working arraignments tonight, representing poor New Yorkers who were arrested yesterday on Thanksgiving.

It was the coldest Thanksgiving in more than a century. Tonight\'s also bitterly cold, even in the courtroom. I\'m wearing my scarf & coat.

— Rebecca Kavanagh (@DrRJKavanagh) November 24, 2018
\n
\n\n\n\n

And Tumblr screenshot threads are always fun to read, even when they take a turn for the epic (over 4000 tweets in this thread, and it isn’t slowing down!)

\n\n\n\n
\n

Tumblr textposts thread, probably?

— we are a family forged in bureaucracy (@ex_aItiora) August 26, 2019
\n
\n\n\n\n

Everyone can think of threads that they’ve loved reading.

\n\n\n\n

My point is, threads are wildly underused on Twitter. I think I big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.

\n\n\n\n

To help make this easier, I’ve been working on a tool that will help you publish an entire post to Twitter from your WordPress site, as a thread. It takes care of transforming your post into Twitter-friendly content, you can just… write. \"?\"

\n\n\n\n

It doesn’t just handle the tweet embeds from earlier in the thread: it handles handle uploading and attaching any images and videos you’ve included in your post.

\n\n\n\n\n\n\n\n

All sorts of embeds work, too. \"?\"

\n\n\n\n
\n
\n
\n\n\n\n

It’ll be coming in Jetpack 9.0 (due out October 6), but you can try it now in the latest Jetpack Beta! Check it out and tell me what you think. \"?\"

\n\n\n\n

This might not fix all of Twitter’s problems, but I hope it’ll help you enjoy reading and writing on Twitter a little more. \"?\"

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 02:33:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Gary\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:39;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Themes Team Releases a Web Fonts Loader, Likely To Prohibit Hotlinking Any Off-Site Assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105363\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets?utm_source=rss&utm_medium=rss&utm_campaign=themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5815:\"

Last Friday, the WordPress Themes Team announced the release of its new Webfonts Loader project. It is a drop-in script that allows theme authors to load web fonts from the user’s site instead of a third-party CDN. The secondary message included in the team’s announcement is that it no longer plans to allow themes to hotlink Google Fonts in the future.

\n\n\n\n

Throughout most of the team’s history, it has not allowed themes to hotlink or use CDNs for hosting theme assets, such as CSS, JavaScript, and fonts. The one exception to this rule was the use of Google Fonts. This allowed themes to have richer typography options at their disposal from what the team has generally declared a reliable source.

\n\n\n\n

“The exception was made because there was no practical way to not have the exception at the time,” said Aria Stathopoulos, a Themes Team representative and developer behind the Webfonts Loader project. “The exception for Google Fonts was made out of necessity. Now that there is another way, the exception will not be necessary.”

\n\n\n\n

In effect, disallowing the Google Fonts CDN would not be a new ban. It would be a removal of an exception to the existing ban.

\n\n\n\n

Google Fonts has become so embedded into the theme developer toolset over the years, there was no way the team could simply pull the plug and prohibit the use of the CDN overnight. If the Themes Team members wanted to focus more on privacy, they would need to build a tool that made it dead simple for theme authors to use.

\n\n\n\n

There is no hard deadline for when the team will remove the exception for Google Fonts, and it is not set in stone at this point. Stathopoulos said removing it has been the goal from the beginning, disallowing all CDNs. However, it took a while to find an efficient way to handle this. With a viable alternative in place, they can discuss moving forward.

\n\n\n\n

Webfonts Loader for Themes

\n\n\n\n

The Webfonts Loader project keeps it simple for theme authors. It introduces a new wptt_get_webfont_styles() function that developers can plug in a stylesheet URL. Once a page is loaded with that function call, it will download the fonts locally to a /fonts folder in the user’s /wp-content directory. This way, fonts will always be served from the user’s site.

\n\n\n\n

The system is not limited to Google Fonts either. Any URL that serves CSS with an @font-face {} rule will work. It does not currently include authentication for CDNs that require API keys, such as Adobe Fonts. However, that is something the team might add in the future.

\n\n\n\n

“For end-users, moving away from CDNs and locally hosting web fonts will improve performance (fewer handshake roundtrips for SSL), and is the privacy-conscious choice,” said Stathopoulos. “The only ‘valid privacy concern’ is that the web fonts’ CDN does not disclose information that is fundamental to the GDPR: what information gets logged, for how long these logs remain, how they are processed, if there is any cross-referencing with all the other wealth of information the company has from users, etc. The concern is a lack of disclosure and information. If a site owner doesn’t know what kind of information a third-party logs for its visitors, then they should ethically not enforce that on their visitors. With this package, the CDN is removed from the equation and the font still gets served fast — if not faster.”

\n\n\n\n

A Path to Core WordPress

\n\n\n\n

Today, there is now a broader focus on privacy concerns related to third-party resources, particularly with tech giants like Google. Such concerns extend to whether third parties are tracking users or collecting data. Additional concerns are around whether sites are disclosing the use of third-party resources, which may be required in some jurisdictions. Site owners who are often unable to work through the web of potential issues are stuck in the middle.

\n\n\n\n

Jono Alderson opened a ticket to create an API for loading web fonts locally in core WordPress in February 2019. It is a lengthy and detailed proposal, but it has yet to see much buy-in outside of a handful of developers.

\n\n\n\n

“If such a script is standardized and included in WordPress core, one of the main benefits would be more respect for the end-user’s privacy,” said Stathopoulos. “In the end, that’s all privacy is about: respecting users.”

\n\n\n\n

A standard API like Alderson proposes could solve some issues. Namely, it would virtually eliminate any privacy concerns. However, loading fonts locally could allow WordPress to optimize font loading and would create a shared system where plugins and themes do not load duplicate assets because of the current limitations of the enqueuing system. A standard API would also put the responsibility of efficiently loading fonts on WordPress’s shoulders instead of theme and plugin developers.

\n\n\n\n

The Themes Team’s new project is a solid start and strengthens the current proposal.

\n\n\n\n

“If we’re serious about WordPress becoming a fast, privacy-friendly platform, we can’t rely on theme developers to add and manage fonts without providing a framework to support them,” wrote Alderson in the ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 28 Sep 2020 20:58:48 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:40;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: Fuxia Scholz First to Pass 100K Reputation Points on WordPress Stack Exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105282\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange?utm_source=rss&utm_medium=rss&utm_campaign=fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5096:\"

Fuxia Scholz, a prolific WordPress Stack Exchange (WPSE) contributor, is the first member to reach 100,000 reputation points. The popular Q&A community site rewards expert advice by floating the highest quality answers to the top, allowing users to earn reputation points. The gamified help community has proven to be more motivating for developers than many traditional forums, since the upvotes communicate how useful their answers are to others.

\n\n\n\n
\n\n\n\n

Scholz started on Stack Overflow a few months before WordPress had its own site. She wrote around 50 answers and made connections with other WordPress developers ahead of the site’s beta phase in June 2010. Once the site graduated and got its own logo and design, Scholz started writing more.

\n\n\n\n

“One core idea for all Stack Exchange sites is gamification: You earn reputation, and you get access to certain privileges,” Scholz said.

\n\n\n\n

“You can say I got a bit addicted. My favorite questions were about problems for which I didn’t know the answer, and couldn’t find one with a search engine, because no one else had solved that before. I used my answers to teach myself, and I learned a lot this way! In May 2011 my reputation on WPSE was already higher than on Stack Overflow, and for the next years it went up in a steep curve.” Ten years after WPSE launched, Scholz has become the first to reach 100,000 reputation points.

\n\n\n\n

“What reputation and karma do is send a message that this is a community with norms, it’s not just a place to type words onto the internet. (That would be 4chan.)” Stack Overflow co-creator Joel Spolsky said. “We don’t really exist for the purpose of letting you exercise your freedom of speech. You can get your freedom of speech somewhere else. Our goal is to get the best answers to questions. All the voting makes it clear that we have standards, that some posts are better than others, and that the community itself has some norms about what’s good and bad that they express through the vote.”

\n\n\n\n

The reputation points were originally inspired by Reddit Karma. Spolsky admits that the points not a perfect system but they do tend to “drive a tremendous amount of good behavior.” Gamification can shape and encourage certain behaviors but Spolsky said it’s a weak force that cannot motivate people to do things they are not already interested in doing. For Scholz, it was the community aspect and an earned sense of ownership and responsibility that kept her hooked.

\n\n\n\n

“In 2012, the community elected me as a moderator, and that changed a lot,” she said. “Now it wasn’t just a game anymore, it was a duty. I felt responsible for the site. I still do. I also found some friends on there. We met at WordCamps and in private, and worked together on different projects.”

\n\n\n\n

Scholz no longer works in development and said she doesn’t care about WordPress anymore, but she is still a regular contributor on the WPSE.

\n\n\n\n

“I switched careers and work as a writer, translator, and community manager for Chess24.com now,” she said. “But I still care about the site WordPress Stack Exchange! I keep an eye on new tags, handle flagged posts and comments, try to make every new user feel welcome, and I search for people who are abusing the system — vote fraud and spam. And, very rarely, I even write an answer, because I still know all this stuff.

\n\n\n\n

“Checking the site has become a part of my daily routine, like feeding the cat.”

\n\n\n\n

This daily habit has snowballed into Scholz racking up more than 2,000 answers. She is getting upvotes on many of her old answers nearly every day, which is what pushed her over the 100k milestone.

\n\n\n\n

“There is a lot to say about the way our site developed over the years,” Scholz said. “I’m not happy about some things. The enthusiasm of the early days is gone. We don’t have enough regulars, there is no discussion about the site on WordPress Development Meta Stack Exchange, and our chat, once very active, funny, and friendly, is now almost dead.

\n\n\n\n

“Maybe that’s normal, I don’t know. But it’s still ‘my’ site. Reputation and badges don’t really mean anything for a long time now, but keeping the site working, useful and friendly is more important.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 26 Sep 2020 15:27:03 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:41;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:82:\"WPTavern: PhotoPress Plugin Seeks to Revolutionize Photography for WordPress Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=104770\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:209:\"https://wptavern.com/photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users?utm_source=rss&utm_medium=rss&utm_campaign=photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5638:\"

Peter Adams, the owner of the PhotoPress plugin, announced a couple of weeks ago that now is the time for his project to take center stage. “It’s Time for PhotoPress,” read the title of his post in which he laid out a four-phase plan for the future of his project.

\n\n\n\n

Adams is no stranger to manipulating WordPress to suit the needs of photographers. He described photography as his first love and second career. He initially found the art of taking photos in high school and set off to college to become a professional photographer in the early ’90s.

\n\n\n\n

As his university graduation loomed, he was recruited to run web development for an internet ad agency that built websites for Netscape, Bill Clinton’s White House, and dozens of Fortune 500 companies. He spent the next 15 years starting or running tech companies before returning to his roots as a photographer.

\n\n\n\n

Today, he photographs for various magazines and companies. And, that’s where his PhotoPress project comes in.

\n\n\n\n

“As far as WordPress has come, it is at risk of losing an entire generation of photographers to photo website services such as Photoshelter, SmugMug, Squarespace, and PhotoFolio,” he said. Adams wants to change that, making WordPress the go-to platform for photographers around the world.

\n\n\n\n

The Jetpack of Photography Plugins

\n\n\n\n

If you dig into the history of the PhotoPress plugin on WordPress.org, it seems to have a 15-year history. However, this is not the same plugin that was published a decade and a half ago by a different developer. The original plugin is now defunct, and Adams took over when the name was freed up on the directory.

\n\n\n\n

Adams wrote in his announcement post that WordPress has done a great job of delivering several media features over the years. “Yet despite that, there are still many rough edges and missing features that keep WordPress from being the first choice for a photographer that needs to publish a beautiful portfolio of their work, put their image catalog/archive online, or showcase a photo editorial/project.”

\n\n\n\n

He outlined a list of 10 specific problem areas that he wants to address in a “Jetpack-like” plugin for photographers. This is the bread and butter of the first of the planned four phases, which he said is about 80% finished. He had originally planned to develop PhotoPress as a series of separate plugins, each addressing a specific problem. Now, it is a single plugin with modules than can be enabled or disabled.

\n\n\n\n

When asked why the “right time” is now, Adams explained it is because the Gutenberg (block editor) project is a giant leap forward in usability in terms of creating photography blogs.

\n\n\n\nPhotoPress Gallery block in the editor.\n\n\n\n

“Photogs are a rare breed of non-technical users with high design sense,” he said. “Things that I used to have to teach photographers to do using shortcode syntax and custom CSS can now be simple controls with live feedback inside a Gutenberg block. It’s really a game-changer for getting people comfortable with customizing things like gallery styling — which is the number one thing photographers need to do.”

\n\n\n\n

The primary piece of the PhotoPress plugin is its custom PhotoPress Gallery block. It allows users to choose between a range of gallery styles, such as columns, masonry, justified, and mosaic. Each style has its own options. Images can also be launched into a slideshow when one is clicked.

\n\n\n\n

Based on some quick tests, the block’s front-end output will go farther with some themes than others. This is mainly because of conflicting CSS and issues which can be solved by testing against more themes.

\n\n\n\n

Aside from the block, the plugin can automatically extract image metadata and group that data through custom taxonomies, such as cameras, lenses, locations, keywords, and more. WordPress stores this information out of the box, but it is hidden away as post meta. The plugin uses the taxonomy system to make it manageable for end-users.

\n\n\n\n

Ultimately, Adams set out to create a photography plugin that fits in with the WordPress admin user interface and experience, which he has accomplished.

\n\n\n\n

The Future of PhotoPress

\n\n\n\n

The project is still a work in progress. Adams is still moving through Phase I of the four-phase plan. Once it is complete, he can move on to the next steps in the process.

\n\n\n\n

Phase II is to create themes that are designed specifically to work with the PhotoPress plugin. He has three planned thus far. One for handling portfolio sites. Another for creating a stock photo archive. And the last for photojournalism and exhibits. Each will be built on top of his photography theme framework.

\n\n\n\n

The themes in Phase II will likely be commercial products. Adams said he needs a way to fund the next phases of the project. He hopes to have this step underway by the end of the year.

\n\n\n\n

For 2021, he wants to begin tackling Phases III and IV. The former will be a website-as-a-service (WaaS) similar to WordPress.com but for photographers. It will begin as a paid project but could have some free options for emerging photographers and students. The final phase is to build an onboarding system.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 25 Sep 2020 19:08:15 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:42;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"WPTavern: Google Officially Releases Its Web Stories for WordPress Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105227\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:191:\"https://wptavern.com/google-officially-releases-its-web-stories-for-wordpress-plugin?utm_source=rss&utm_medium=rss&utm_campaign=google-officially-releases-its-web-stories-for-wordpress-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5593:\"Web Stories for WordPress dashboard.\n\n\n\n

Two and a half months after the launch of its public beta, Google released its Web Stories for WordPress plugin. So far, the plugin has over 10,000 active installations and has garnered a solid five-star rating from four reviews.

\n\n\n\n

Google created the Web Stories format through its AMP Project to allow publishers to create visually-rich stories. It is primarily geared toward mobile site visitors, allowing them to quickly jump through story pages with small chunks of content.

\n\n\n\n

The Web Stories plugin creates a visual interface within WordPress for creating Stories. It breaks away from the traditional WordPress interface and introduces users to an almost Photoshop-like experience for building out individual Stories. The Stories editor is completely drag-and-drop.

\n\n\n\n

The plugin also offers eight predesigned templates out of the box that cover a small range of niches. However, according to Google’s announcement, the company plans to add more templates in future updates.

\n\n\n\n

Web Stories Are for Storytelling

\n\n\n\n

“Firstly…the power of Stories,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Stories are how we (humans) see the world and share our experiences. Up to now the platforms that we have to tell stories have been limited to books/films/tv/websites/blogs/instagram stories etc.”

\n\n\n\n

“Websites are ok for telling stories but in many ways the format doesn’t really fit the linear arc of storytelling. When Marshall McLuhan said ‘the medium is the message’ in 1964 he was talking about how the medium itself has a social impact, and change the communication itself…and the possibilities for what is communicated and how it is perceived. But we should keep coming back to Stories. Stories are the key here imo. Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Marsland finished his thread by saying that using Stories as a replacement for a brochure or website is a missed opportunity. He said that it was a platform for storytelling and should be used as such.

\n\n\n\n

It is far too early to tell if Web Stories will simply be a fad or still in wide use years from now. The technology certainly lends itself well to telling stories, particularly in mobile format, but I doubt we have seen the best of what is possible on the web. The format feels too limited to be the end-all-be-all of storytelling. It is merely one medium that will live and die by its popularity with users.

\n\n\n\n

With the right design skills, some people will craft beautiful Web Stories. And, that is just what Marsland has done with the first Story he shared:

\n\n\n\nPage from the Wilson and Pootle Web Story by Jamie Marsland.\n\n\n\n

I agree with his conclusion. Web Stories should be about storytelling. When you move outside of that zone, the technology feels out of place.

\n\n\n\n

Where I disagree is that websites are not ideal for storytelling. Ultimately, the WordPress block editor will allow artistic end-users to craft intricate stories, mixing content and design in ways that we have not seen. We are just now scratching the surface. I expect our community of developers to build more intricate tools than what the Web Stories plugin currently allows, and we can do so in a way that revolutionizes storytelling on the web.

\n\n\n\n

New Features

\n\n\n\nStory editor with Unsplash photo integration.\n\n\n\n

The Web Stories plugin now adds support for Unsplash images and Coverr videos out of the box. The plugin adds a new tab with a “media” icon. For users of the first beta version of the plugin, this may be a bit confusing. The previous media icon was for a tab that displayed the user’s media. Now, the user’s media is under the tab with the “upload” icon.

\n\n\n\n

It is also not immediately clear that the Unsplash images and Coverr videos are not hosted on the site itself. There is a “powered by” notice at the bottom of the tab, but it can be easy to miss because it blends in with the media in the background.

\n\n\n\n

Media from Unsplash and Coverr is hosted off-site and not downloaded to the user’s WordPress media library. I could find no mention of this in the plugin’s documentation. Such hotlinking was a cause for debate over the recent official release of the Unsplash plugin.

\n\n\n\n

Google also announced it planned to add more “stock media integrations” in the near future. According to a document shared via a GitHub ticket, such future integrations may include Google Photos and GIF-sharing site Tenor.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 21:13:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:43;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"WPTavern: W3C Drops WordPress from Consideration for Redesign, Narrows CMS Shortlist to Statamic and Craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105108\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft?utm_source=rss&utm_medium=rss&utm_campaign=w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:11563:\"

The World Wide Web Consortium (W3C), the international standards organization for the web, is redesigning its website and will soon be selecting a new CMS. Although WordPress is already used to manage W3C’s blog and news sections of the website, the organization is open to adopting a new CMS to meet its list of preferences and requirements.

\n\n\n\n

Studio 24, the digital agency selected for the redesign project, narrowed their consideration to three CMS candidates:

\n\n\n\n
  1. Statamic
  2. Craft CMS
  3. WordPress
\n\n\n\n

Studio 24 was aiming to finalize their recommendations in July but found that none of them complied with the W3C’s authoring tool accessibility guidelines. The CMS’s that were better at compliance with the guidelines were not as well suited to the other project requirements.

\n\n\n\n

In the most recent project update posted to the site, Studio 24 reported they have shortlisted two CMS platforms. Coralie Mercier, Head of Marketing and Communications at W3C, confirmed that these include Statamic and Craft CMS.

\n\n\n\n

WordPress was not submitted to the same review process as the Studio 24 team claims to have extensive experience working with it. In the summary of their concerns, Studio 24 cited Gutenberg, accessibility issues, and the fact that the Classic Editor plugin will stop being officially maintained on December 31st, 2021:

\n\n\n\n

First of all, we have concerns about the longevity of WordPress as we use it. WordPress released a new version of their editor in 2018: Gutenberg. We have already rejected the use of Gutenberg in the context of this project due to accessibility issues.

If we choose to do away with Gutenberg now, we cannot go back to it at a later date. This would amount to starting from scratch with the whole CMS setup and theming.

Gutenberg is the future of WordPress. The WordPress core development team keeps pushing it forward and wants to roll it out to all areas of the content management system (navigation, sidebar, options etc.) as opposed to limiting its use to the main content editor as is currently the case.

This means that if we want to use WordPress long term, we will need to circumvent Gutenberg and keep circumventing it for a long time and in more areas of the CMS as time goes by. 

\n\n\n\n

Another major factor in the decision to remove WordPress from consideration was that they found “no elegant solution to content localization and translation.”

\n\n\n\n

Studio 24 also expressed concerns that tools like ACF, Fewbricks, and other plugins might not being maintained for the Classic Editor experience “in the context of a widespread adoption of Gutenberg by users and developers.”

\n\n\n\n

“More generally, we think this push to expand Gutenberg is an indication of WordPress focusing on the requirements of their non-technical user base as opposed to their audience of web developers building custom solutions for their clients.”

\n\n\n\n

It seems that the digital agency W3C selected for the project is less optimistic about the future of Gutenberg and may not have reviewed recent improvements to the overall editing experience since 2018, including those related to accessibility.

\n\n\n\n

Accessibility consultant and WordPress contributor Joe Dolson recently gave an update on Gutenberg accessibility audit at WPCampus 2020 Online. He reported that while there are still challenges remaining, many issues raised in the audit have been addressed across the whole interface and 2/3 of them have been solved. “Overall accessibility of Gutenberg is vastly improved today over what it was at release,” Dolson said.

\n\n\n\n

Unfortunately, Studio 24 didn’t put WordPress through the same content creation and accessibility tests that it used for Statamic and Craft CMS. This may be because they had already planned to use a Classic Editor implementation and didn’t see the necessity of putting Gutenberg through the paces.

\n\n\n\n

These tests involved creating pages with “flexible components” which they referred to as “blocks of layout,” for things like titles, WYSIWYG text input, and videos. It also involved creating a template for news items where all the content input by the user would be displayed (without formatting).

\n\n\n\n

Gutenberg would lend itself well to these uses cases but was not formally tested with the other candidates, due to the team citing their “extensive experience” with WordPress. I would like to see the W3C team revisit Gutenberg for a fair shake against the proprietary CMS’s.

\n\n\n\n

W3C Is Prioritizing Accessibility Over Its Open Source Licensing Preferences

\n\n\n\n

The document outlining the CMS requirements for the project states that “W3C has a strong preference for an open-source license for the CMS platform” as well as “a CMS that is long-lived and easy to maintain.” This preference may be due to the economic benefits of using a stable, widely adopted CMS, or it may be inspired by the undeniable symbiosis between open source and open standards.

\n\n\n\n

“The industry has learned by experience that the only software-related standards to fully achieve [their] goals are those which not only permit but encourage open source implementations. Open source implementations are a quality and honesty check for any open standard that might be implemented in software…”

Open Source Initiative
\n\n\n\n

WordPress is the only one of the three original candidates to be distributed under an OSD-compliant license. (CMS code available on GitHub isn’t the same.)

\n\n\n\n

Using proprietary software to publish the open standards that underpin the web isn’t a good look. While proprietary software makers are certainly capable of implementing open standards, regardless of licensing, there are a myriad of benefits for open standards in the context of open source usage:

\n\n\n\n

“The community of participants working with OSS may promote open debate resulting in an increased recognition of the benefits of various solutions and such debate may accelerate the adoption of solutions that are popular among the OSS participants. These characteristics of OSS support evolution of robust solutions are often a significant boost to the market adoption of open standards, in addition to the customer-driven incentives for interoperability and open standards.”

International Journal of Software Engineering & Applications
\n\n\n\n

Although both Craft CMS and Statamic have their code bases available on GitHub, they share similarly restrictive licensing models. The Craft CMS contributing document states:

\n\n\n\n

Craft isn’t FOSS
Let’s get one thing out of the way: Craft CMS is proprietary software. Everything in this repo, including community-contributed code, is the property of Pixel & Tonic.

That comes with some limitations on what you can do with the code:

– You can’t change anything related to licensing, purchasing, edition/feature-targeting, or anything else that could mess with our alcohol budget.
– You can’t publicly maintain a long-term fork of Craft. There is only One True Craft.

\n\n\n\n

Statamic’s contributing docs have similar restrictions:

\n\n\n\n

Statamic is not Free Open Source Software. It is proprietary. Everything in this and our other repos on Github — including community-contributed code — is the property of Wilderborn. For that reason there are a few limitations on how you can use the code:

\n\n\n\n

Projects with this kind of restrictive licensing often fail to attract much contribution or adoption, because the freedoms are not clear.

\n\n\n\n

In a GitHub issue requesting Craft CMS go open source, Craft founder and CEO Brandon Kelly said, “Craft isn’t closed source – all the source code is right here on GitHub,” and claims the license is relatively unrestrictive as far as proprietary software goes, that contributing functions in a similar way to FOSS projects. This rationale is not convincing enough for some developers commenting on the thread.

\n\n\n\n

“I am a little hesitant to recommend Craft with a custom open source license,” Frank Anderson said. “Even if this was a MIT+ license that added the license and payment, much like React used to have. I am hesitant because the standard open source licenses have been tested.”

\n\n\n\n

When asked about the licensing concerns of Studio 24 narrowing its candidates to two proprietary software options, Coralie Mercier told me, “we are prioritizing accessibility.” A recent project update also reports that both CMS suppliers W3C is reviewing “have engaged positively with authoring tool accessibility needs and have made progress in this area.”

\n\n\n\n

Even if you have cooperative teams at proprietary CMS’s that are working on accessibility improvements as the result of this high profile client, it cannot compare to the massive community of contributors that OSD-compliant licensing enables.

\n\n\n\n

It’s unfortunate that the state of open source CMS accessibility has forced the organization to narrow its selections to proprietary software options for its first redesign in more than a decade.

\n\n\n\n

Open standards go hand in hand with open source. There is a mutually beneficial connection between the two that has caused the web to flourish. I don’t see using a proprietary CMS as an extension of W3C values, and it’s not clear how much more benefit to accessibility the proprietary options offer in comparison. W3C may be neutral on licensing debates, but in the spirit of openness, I think the organization should adopt an open source CMS, even if it is not WordPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 20:13:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:44;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:79:\"WPTavern: First Look at Twenty Twenty-One, WordPress’s Upcoming Default Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105166\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme?utm_source=rss&utm_medium=rss&utm_campaign=first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6907:\"

Fashion is ephemeral. Art is eternal. Indeed what is a fashion really? A fashion is merely a form of ugliness so absolutely unbearable that we have to alter it every six months!

\n\n\n\n

Thus wrote Oscar Wilde on Victorian-era fashion in an article titled “The Philosophy of Dress” for the New-York Tribune in 1885.

\n\n\n\n

In many ways, WordPress theming is the same as the ever-changing landscape of fashion. Rounded corners are in one day and out the next. Box shadows are in one year after being frowned up just months earlier. Perhaps web design is so intolerable that we must change it every six months. Or, at least freshen it up every year in the case of WordPress.

\n\n\n\n

If art is eternal, there are only two default, Twenty* themes that I can truly recall from past years: Twenty Ten and Twenty Fourteen — yes, Twenty Twenty is memorable, but it is also still the current default. Twenty Ten was a classic that paid homage to WordPress’s past. Twenty Fourteen was such a leap away from tradition that it is hard to forget. Everything else has seemed to fade to varying degrees.

\n\n\n\n

With WordPress 5.6 and the end of the year looming, it is time to look forward to the latest trend. As Mel Choyce-Dwan noted in the announcement of Twenty Twenty-One, the next default theme, “Pastels and muted colors are pretty in right now.”

\n\n\n\n

She is not wrong. The colors are a refreshing change of pace. Now that we are into the second day of autumn, I am getting the good kind of vibes from some of the more earthy-tones from a couple of the color palettes expected to ship with the theme.

\n\n\n\nPotential color palette options for Twenty Twenty-One.\n\n\n\n

Whether Twenty Twenty-One will be a fashionable theme for the year or art that we can remember a decade from now, only history will be able to judge. For now, let’s enjoy the creation and take a look at what we should expect from the next default WordPress theme.

\n\n\n\n

The Current Twenty Twenty-One

\n\n\n\n

The new default theme is a fork of Automattic’s Seedlet, a project in which I lauded as the next step in the evolution of theming. It is a theme that is focused on WordPress’s future of being completely comprised of blocks. It gives us an ideal insight into where theme development is heading. It makes sense as the foundation for the new default. Few other themes would make for a good starting point right now. With WordPress theme development in flux, Seedlet is simply ahead of the pack in terms of foundational elements.

\n\n\n\nSeedlet WordPress theme screenshot.\n\n\n\n

“This provides us with a thorough system of nested CSS variables to make child theming easier, and to help integrate with the global styles functionality that’s under development for full-site editing,” wrote Choyce-Dwan of using Seedlet as a starting point.

\n\n\n\n

There are no plans to spin up a Google Web Font for this theme. The design team is going native and sticking with the default system font stack. Choyce-Dwan listed several reasons for the choice, such as keeping a neutral font that allows broad use, speed, and customizability via a child theme.

\n\n\n\n

Despite the neutral font, the default pastel green is a fairly opinionated design decision. It will not be used broadly across industries. However, the team plans to create multiple color palettes that will give it more range. Presumably, these palettes can also be overwritten.

\n\n\n\nPastel green color scheme on single post view.\n\n\n\n

Other than the colors, the design is relatively simple. Choyce-Dwan said that the theme’s block patterns support is where it will be truly unique.

\n\n\n\n

I was initially unhappy with the patterns that were going to ship with WordPress 5.5. However, an 11th-hour update improved the situation so that they did not feel entirely experimental. The foundational Seedlet theme for Twenty Twenty-One has some unique patterns that begin to scratch the surface of what’s possible with this WordPress feature. My hope is that the new default theme steps it up a notch.

\n\n\n\n

Currently, the theme does not register any custom patterns. However, it has a placeholder file and a note that they are a work in progress. Choyce-Dwan shared some patterns the team has already designed in the announcement.

\n\n\n\nCurrently-designed block patterns.\n\n\n\n

“We’ll be relying on our talented community designers for more ideas,” she wrote. The team has also created a GitHub template for anyone to contribute pattern design ideas.

\n\n\n\n

Currently, the theme does not support the upcoming full-site editing feature of WordPress. After the Beta 1 release of WordPress 5.6, the team plans to begin exploring the addition of this support. WordPress is expected to ship a public beta of full-site editing in its next major release, but it is unclear whether it will be far enough along to be a headline feature for the Twenty Twenty-One theme.

\n\n\n\n

The team and volunteers have less than a month before the October 20th deadline for committing the new theme to trunk, the core WordPress development branch. At that stage, the theme should be nearly complete and ready for production. Of course, there will be several rounds of patches, bug fixes, and updates before WordPress 5.6 lands in December. Right now is the best time for anyone who wants to get involved with Twenty Twenty-One to do so.

\n\n\n\n

Useful links with more information:

\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 20:01:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:45;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:37:\"HeroPress: Hello World – Hevo Nyika\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=3308\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:176:\"https://heropress.com/essays/hello-world-discovering-the-world-through-wordpress/#utm_source=rss&utm_medium=rss&utm_campaign=hello-world-discovering-the-world-through-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14438:\"\"Pull

Unokwanisa kuverenga rondedzero iyi muChiShona

\n

So I chose a career in Web Development!!

\n

To be honest it’s kind of funny when I think about it and quite surreal to be here talking about my story. It has been a journey and I would like to share my story with you.

\n

I have been lucky in the Dad department. My Dad encouraged me to work hard and dream big from a very young age. I remember occasionally having ‘when I grow up’ talks.

\n

For quite some time I wanted to be a Judge, however awesome this dream sounds it was not very inspired. After binge-watching Judge Judy for a whole weekend, I started calling myself Judge Thelma. Though I don’t remember much of this my sister says that I used to say I would arrest all the men in the World if I ever became a Judge. HAHAHA! (clearly I didn’t understand how the World works)

\n

I did not understand what being a Judge meant or what was required for me to start banging that gavel to my heart’s desire. Eventually, I learnt that I had to become a lawyer first then magistrate before I could be nominated to be a Judge and let us just say that is how I sentenced that dream to a lifetime down the drain.

\n

See what I did there? hahaha!

\nWith Daddy Dearest\n

A few years later, I was in High School and that is when I decided to pursue a career in Computer Science. I did not know what I would be doing or how I would get there but I just knew that I was going to pursue a career in ICT. I wrote my first line of code when I was 16 years old.

\n

This was after I had joined the school’s computer class, initially, I thought I would be learning about Excel Sheets and Word Documents until I was assigned to write my first program in C (talk about a double-take!!). It was not easy but it was very exciting, l remember writing up simple code for a Video Club – a simple check-in/out for VHS tapes and CDs. Dear World, thus began my fascination with computers.

\n

Seven years later, I was now in university studying ICT as I had always wanted. I was doing a Bachelors in Business Management & Information Technology. In my third year, I was interning at a local Webdesign and hosting company. This was never my plan, I only took on that job after I had failed to get a job with local banks or telecommunications companies. Before I was introduced to Website Design I envisioned myself suiting up and working in IT Audit or offering IT support. Even though things did not go as I had planned, I am glad they did not exactly go my way in that aspect. So in 2017, I was designing websites using HTML, CSS, PHP, JavaScripts and Joomla which was the prefered content management system at that company. I knew about WordPress but I was not using it for anything. People have this misconception that WordPress is not for real developers and it is not secure and at that time I was one of those people.

\n

Finding my tribe

\n

One day when I was working at the front desk Thabo Tswana came to give a colleague of mine a purple WooCommerce pen. I did not know what WooCommerce was at that time but I was taken by the purple shirt and pen he was carrying. I asked him about it and he explained what WooCommerce was and that what he was carrying was called ‘swag’. So the love of freebies led me to the WordCamp Harare website, instead of buying a ticket I applied to volunteer. I learnt more about WordPress, I was a volunteer, without any knowledge on WordPress.org or WordPress.com. I only started using WordPress because of the awesome people that l had met at that Wordcamp.

\n

Everyone was so welcoming, a week later with help from Thabo I designed my first ever WP website.

\n

Soon after I was part of the community and a bit more involved in the meetups. We had our first-ever Women Who WordPress meetup in 2018. So many ladies came on board bloggers and developers alike. We were free to talk and discuss a lot of things. We had more time to discuss the difference between WordPress.com and WordPress.org we shared views on how to handle discrimination at work, how to promote your website and a whole lot of other things.

\n

\n

Establishing roots

\n

In 2018, Harare had its first-ever female Lead Organiser Tapiwanashe Manhobo whoop whoop! I was also part of the organising team that year, I was assigned to handle Harare’s first Kids Camp. The planning process was stressful because the economic crisis in Zimbabwe was getting worse, luckily we had over 8 months to plan and with help from sponsors, we managed to pull through. In the end, everything turned out great. I wrote an article about the Kids Camp here.

\n

After the first Kids Camp, we had several WordPressors that were enthusiasts about encouraging kids to embrace ICT. In 2019 we had not planned to have a Kids Camp because of financial constraints but to our surprise, we had some anonymous donations and we managed to have a WordPress Community outreach to a youth centre a week after our WordCamp. We had the outreach at the Centre for Total Transformation which is a non-formal school that caters for underprivileged and vulnerable children. We taught them about WordPress, Computer Hardware and Software.

\n

Here is a small video I took with Ellen when we were about to leave. Did l mention that I am terrible on camera? hahaha!

\n\n

Kids Camp 2019 – Centre for Total Transformation

\n

I have fallen deeply for WordPress because of the Community, I enjoy attending WordCamps, meeting new people and just learning new stuff. I have a huge list of WordCamps I need to attend before l kick the bucket, hopefully. Last year I managed to cross WordCamp

\n

Johannesburg off my bucket list. This year I was going to attend WordCamp Capetown but unfortunately, 2020 had other plans for the whole world. Anyway when everything is back to normal my plan to travel to WordCamps will proceed. (fingers crossed)

\n

Reaping Fruits

\n

Meanwhile, my plan to improve my developing skills has not been on hold. Even though I can still cook up code in C and Java, for now, I have also included WordPress PHP functions to the mix. It was not easy to get to this point, daring myself got me to this slightly better stage. My IQ is not way up there, however, I try to do my best where I can and I am happy to say it has paid off so far.

\n

Around November last year, I was designing as a freelancer while job hunting. Out of the blue l got a call for a job offer from Trust Nhokovenzo who is big on Digital marketing and also part of the WordPress Community. He had asked someone in the community about developers and my name happened to come up. So since February, I have been part of his team at Calmlock Digital Marketing Agency.

\n

There is so much more in the world of WordPress that l am yet to tap into so even though I am ending my write up here, for now, my story is going to continue …

\n

Until next time…

\n

Hevo Nyika

\n

Saka ini ndakasarudza kugadzira mawebhusayiti.

\n

Ndakaita rombo rakanaka pana baba vandakapihwa naMwari. Baba vangu vaindikurudzira kuti ndishande nesimba. Ndinoyeuka pano neapo tichiita hurukuro dzedu dzekuti ‘kana ndakura ndoda kuveyi’.

\n

Kwenguva yakati rebei ndaida kuve Mutongi. Kunyangwe ini ndisingazvirangariri mukoma wangu anotaura kuti ndaiti ndaizosunga varume vese vari pasi rino kana ndikangoita mutongi HAHAHA zveshuwa handaiziva kuti mitemo yenyika inofambiswa seyi.
\nNdanga ndisinga nzwisisi kuti kuva mutongi kwairevei kana zvaidikanwa kwandiri kuti nditange kurova iro ghavheu kuchishuwo chemoyo wangu. Pakupedzisira, ndakadzidza kuti ndaifanirwa kuzoita gweta ipapo magistrate ndisati ndasarudzwa kuita Mutongi naizvozvo ndokupera kwakaita chiroto chekuva Mutongi.

\nNa Baba Vangu\n

Gare gare papfura makore mashoma pandakanga ndave kuHigh School ndakanga ndakuda kuita basa rema kombiyuta. Ndakanyora mutsara wekutanga wekodhi pandaive nemakore gumi nematanhatu. Izvi zvakaitika mushure mekunge ndapinda mukirasi yemakombiyuta, pakutanga ndaifunga kuti ndinenge ndichidzidza nezveExcel Sheets neWord zvisineyi ndakaona ndakunyora kodhi yangu yekutanga muC. Zvaisave nyore kunyora kodhi asi zvainakidza kwazvo, ndorangarira ndichinyora kodhi yeVhidhiyo Kirabhu.

\n

Makore manomwe apfura, ndakanga ndava kuyunivhesiti ndichidzidza ICT zvandakagara ndakaronga. Ndaiita Bachelors muBusiness Management & Information Technology. Mugore rangu rechitatu ndainge ndave kushanda kune imwe kambani yaita zvekugadzira mawebhusaiti. Ndakawana basa iri mushure mekunge ndatadza kuwana basa kumabhanga. Kunyangwe hazvo zvinhu zvisina kuenda sezvandaive ndakaronga, ndinofara kuti hazvina kunyatso enda nenzira yangu. Saka muna 2017 ndaigadzira mawebhusaiti ndichishandisa HTML, CSS, PHP, JavaScript uye Joomla iyo yaive iyo inokurudzirwa kukambani kwandaive. Panguva iyi ndaiziva nezve WordPress asi ndakanga ndisingaishandisi.

\n

Kuwanana neWordPress

\n

Rimwe zuva pandakanga ndichishanda ndakaona Thabo Tswana akauya kuzopa mumwe mukomana wandayishanda naye chinyoreso cheWooCommerce. Ndakanga ndisingazive kuti WooCommerce yaive chii asi ndakafarira chinyoreso nehembe ye WooCommerce yaanga akapfeka. Ndakamubvunza nezvazvo akatsanangura kuti WooCommerce yaive chii. Saka nekudawo zvakanaka, zvemahara ndakaenda pawebhusaiti yeWordCamp Harare ndikabata zvimbo zvegore iroro. Ndakazvipira kubatsirawo vamwe vekuWordPress kuWordCamp Harare. Nerubatsiro kubva kunaThabo ndakagadzira webhusaiti yangu yekutanga yeWordPress vhiki rakatevera .

\n

Mushure mekunge ndaitawo chipato cheavo vanoshandisa WordPress ndakanga ndakuenda kumisangano yeWordPress yaitwa muHarare. Takaita musangano wevakadzi chete muna 2018. Vakadzi vazhinji vakauya kumusangano uyu. Tainga takasununguka kukurukura zvinhu zvakawanda. Takakurukura pamusoro pemutsauko uripo pakati peWordPress.com neWordPress.org takagovana maonero ekugadzirisa rusarura kubasa nezvimwewo.

\n

\n

Nguva yandakatanga kushandisa WordPress

\n

Muna 2018, kurongwa kweWordCamp Harare kwakatungamirwa kekutanga nemusikana ainzi Tapiwanashe Manhobo (waiva mufaro mukuru). Ndakanga ndiri mumwe wevairongawo naye. Hurongwa hwekuronga WordCamp Harare mugore iri hwainetsa pamusaka pekuoma kwehupfumi wemuZimbabwe, zvisineyi takaita rombo rakanaka nokuti takawana rubatsiro kubva kunevamwewo vanhu vakatiwedzera mari. Pakupedzisira, zvese zvakabudirira zvakanaka. Takarongawo WordCamp yevana varipasi pemakore gumi nechishanu, munokwanisa kuverenga pamusoro pezuva iri pawebhisaiti yangu apa.

\n

Mushure mekuita WordCamp yevana, takave nevamwe vanhu veWordPress aifarira kukurudzira vana kuti vagamuchire ICT. Muna 2019 takanga tisina kuronga kuve neWordCamo yeVana nekuda kwezvimhingamupinyi zvemari asi chakatishamisa ndechekuti takawana mari kubvawo kune vamwe. Takaita Camp iyi paCentre for Total Transformation chinova chikoro chisiri chepamutemo chinodzidzisa vana vanotambura. Tadzidzisa vana ava pamusoro peWordPress, Computer Hardware uye Software.

\n\n

Ndofarira WordPress zvakanyanya nekuda kweavo varimu nharaunda yacho, ini ndinonakidzwa nekuenda kumaWordCampi, kusangana nevanhu vatsva uye kungo dzidza zvinhu zvitsva. Gore rakapera ndakakwanisa kuyambuka muganhu weZimbabwe ndichienda kuWordCamp Johannesburg, dai pasina kuti 2020 nyika dzepasi rino dzakawirwa nedenda reCOVID 19 zvimwe ndingadayi ndakaenda kuWordCamp Capetown. Zvisinei hazvo kana denda ranani zvimwe ndichakwanisa kufamba ndichienda kumaWordCamp edzimwe nyika.

\n

Kukowa zvandakadyara

\n

Zvichakadaro, chirongwa changu chekuvandudza hunyanzvi hwangu hachina kumira. Kunyangwe ini ndichiri kukwanisa kubika kodhi muC uye Java, ikozvino, ndasanganisirawo WordPress PHP. Zvaive zvisiri nyore kusvika apa, zvakatora kuzvishingisa nekushanda nesimba. Ndinofara mwari aiva neni pamufambo wangu uyu.

\n

Muna Mbudzi gore rakapera, ndaive ndichigadzira mawebhusayiti apo nditsvaga basa. Pasina nguva ndakataura naTrust Nhokovenzo uyo akaandipa basa mukambani yake, kambani iyi inonzi Calmlock Digital Marketing Agency.

\n

Pane zvimwe zvakawanda kuWordPress zvandisati ndapinda mazviri. Nhaizvozvo kunyangwe ndiri kupedzisa kunyora kwangu apa, nyaya yehupenyu wangu ichaenderera mberi…

\n

Kusvikira nguva inotevera …

\n

…. tsvaga chinangwa chako, chiite mushe mushe ..

\n

The post Hello World – Hevo Nyika appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 06:00:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Thelma Mutete\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:46;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: WordPress Contributors Debate Dashboard Notice for Upcoming Facebook oEmbed Provider Removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105132\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5885:\"

WordPress contributors are discussing different strategies for responding to Facebook and Instagram dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers. When a user attempts to embed content by pasting a URL as they have in the past, they may not understand why it no longer works. They may assume that WordPress broke embeds, causing an increase in the support burden for this change.

\n\n\n\n

A few participants on the trac ticket for this issue have suggested WordPress detect users who will be impacted and attempt to warn them with a notice.

\n\n\n\n

“Since this may impact users unknowingly, it is possible to push a dashboard notice to users who have Facebook/Instagram embeds in their content, showing for site admins, as a one-off that can be dismissed,” Marius Jensen said.

\n\n\n\n

“We’ve previously done post-update-processing to clean up comments, so the idea of looking over content for an embed isn’t completely outlandish, and would help with those who don’t follow WordPress’ usual channels to learn of this.”

\n\n\n\n

Others don’t see the necessity. “Why should we make exception here?” Milan Dinić said. “It’s not the first time oEmbed support was discontinued for a provider, and I don’t remember anything specific was done then.”

\n\n\n\n

There is still some uncertainty about what will happen with existing oEmbeds after Facebook updates its API. During a recent core developer meeting, Helen Helen Hou-Sandí confirmed that WordPress does not clear oEmbed caches regularly. “Technically oEmbed caches are cleared if you save and a valid response is returned, we do not do cron-based garbage collection,” Hou-Sandí said.

\n\n\n\n

In a post today on the core development blog, Jake Spurlock assured users and developers that the existing embeds added before Facebook’s API change should still work:

\n\n\n\n

Because oEmbed responses are cached in the database using the hidden oembed_cache post type, any embed added prior to the October 24th deadline will be preserved past the deprecation date. These posts are not purged by default in WordPress Core, so the contents of the embed will persist unless manually deleted.

\n\n\n\n

Marius Jensen cautioned that there is still the possibility that existing embeds may not work going, depending on what Facebook does.

\n\n\n\n

“We don’t know how they plan on implementing the use of unauthorized embed attempts,” Jensen said. “It could not return an embed code and your link would remain a plain link, or maybe they decide to return some kind of embedded ‘unauthorized’ content. I don’t think anyone has heard any specifics on how Facebook plans on doing this, so we’re all just kinda waiting to either hear more, or see what happens.”

\n\n\n\n

Jensen said WordPress doesn’t re-check the cached results except when something changes with the post, but there may be plugins that clean up temporary data that may create an unpredictable outcome.

\n\n\n\n

“The reliability of the caches are hard to determine (and being caches, it’s sort of in the term that it’s not guaranteed to always be there, but rather fetched and saved for a while when needed),” Jensen said.

\n\n\n\n

Ideally WordPress’ oEmbed caches will prevent millions of embeds from breaking, but it’s still unknown how Facebook and third party plugins could change things.

\n\n\n\n

Coming off a rocky 5.5 core update that deprecated jQuery Migrate and flooded official support forums with reports of broken sites, some contributors are wary of having another situation where users are left in the dark.

\n\n\n\n

“I think a dashboard notice is desirable,” Jon Brown said. “Otherwise we’re not preemptively warning people in a way they can prepare and transition to another solution. We’re letting them know the same instant it’s going to break (when editing a specific post). I don’t think we can safely assume cached data is going to persist forever either, plenty of routines out there purge transient data before its stated expiration date.

\n\n\n\n

“I see this as potentially being similar to the problems seen in dropping JQM. It’ll cause avoidable and silent breakage client side without even any error logging for a site developer to pick up on. In hindsight, what ideally would have happened with JQM would have been incorporating the detection code from Enable jQuery Migrate Helper into core temporarily, or simply installing that plugin automatically on behalf of users.”

\n\n\n\n

Brown suggested WordPress detect calls to the cached embeds and warn users before the calls have the chance to fail so they can consider enabling a plugin to keep their embeds working more reliably.

\n\n\n\n

The discussion remains open in the make.wordpress.org/core post and the corresponding trac ticket. Spurlock said WordPress will likely remove Facebook and Instagram oEmbed providers in the upcoming 5.6 release (scheduled for December 8) but it could also be shipped in a 5.x minor release that happens after October 24.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 04:28:56 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:47;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Gutenberg Hub Launches Landing Page Templates Directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105009\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:175:\"https://wptavern.com/gutenberg-hub-launches-landing-page-templates-directory?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-hub-launches-landing-page-templates-directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7657:\"\n\n\n\n

Munir Kamal has created copy-and-paste blocks. He has built sections or “patterns” from those blocks. He has created a plugin that allows users to completely customize the two features via block options. Yesterday, he released an initial offering of 22 landing page templates that build upon his earlier work.

\n\n\n\n

Gutenberg Hub can almost be called his magnum opus, at least at this stage of his career. It is a continually growing library of free tools for WordPress’s block editor.

\n\n\n\n

Like previous projects, Gutenberg Hub’s landing templates require the EditorPlus plugin. This plugin is essentially a suite of design controls for the core WordPress blocks. The templates make use of these options by default. Given the limitations of the block editor’s current design controls, the use of such a plugin is necessary. Otherwise, there would be few other ways to realistically create a template system like this.

\n\n\n\n

Currently, users must copy the block code — via a convenient “copy” button — from the Gutenberg Hub website and then paste it in the editor. It is not an ideal situation, and I have been asking Kamal whether he would consider building a template inserter for months now.

\n\n\n\n

This time around, he preemptively said, “And, by the way, I am already working on adding a Template Inserter in my EditorPlus plugin. That will allow users to browse and insert these templates directly from Gutenberg without leaving the website.”

\n\n\n\n

He knew the question was coming. No need for me to ask again. He was unable to share a current screenshot of what the inserter looks like, but he is asking for feedback on what people expect of the user experience and interface.

\n\n\n\n

“Earlier, I created a template inserter similar to other blocks plugins, but later I changed my mind and thought that I should integrate with the Gutenberg Patterns API and load the templates into the ‘patterns’ panel in the block inserter,” he said. “But, I am having a few issues and thinking about going back to the original idea to have a Templates button on the top toolbar that opens a popup window to browse and filter templates that users can insert on a click.”

\n\n\n\n

For now, it is still early. However, at least it is on the long-term roadmap and being worked on.

\n\n\n\n

The Landing Page Templates

\n\n\n\nTesting the photography template (with minor adjustments).\n\n\n\n

At the moment, Gutenberg Hub offers 22 landing page templates. The “page” terminology may not mean “full page.” It simply depends on the active theme. Some themes have an open-canvas type of template that allows users to create the entire page via the editor. However, that is not a common feature, so these page templates will be confined to the post content area in most cases.

\n\n\n\n

The templates also work better with themes that have at least a full-width or no-sidebar option. End-users will want a lot of breathing room to use the templates and tinker with their designs.

\n\n\n\n

Kamal has built templates that stretch across a variety of industries. From restaurants to gyms to education to fashion, there is a lot to choose from right now. He promises more are on the way and at least a 23rd template in the next few days.

\n\n\n\n

“For the niches, I did some research from the top WordPress and HTML marketplaces and found the following most common or popular niches,” he said. “I think I will stick with these niches unless I get some more recommendations.”

\n\n\n\n

In comparison, Redux Templates offers access to over 1,000 sections and templates. Of course, there are trade-offs, such as some of those being commercial and the plugin typically requiring other third-party plugins. While quantity is not the only thing to look at, it proves there are miles of landscape that Gutenberg Hub’s templates have not yet explored. But, it is merely the beginning.

\n\n\n\n

Gutenberg Hub’s full-page templates are not quite as plug-and-play as its blocks and section templates. This is not so much a fault from the developer’s end. It is an issue of the platform, which is constantly being updated, and the range of support from current themes. End-users will start seeing some of the current limitations of the system when a layout does not quite look right with one theme but does with another. Or, if their theme has not been updated to support a new feature, such as the Social Links block, the typical horizontal menu design will likely be a normal vertical list of links instead.

\n\n\n\n

These are not insurmountable issues. Gutenberg and themes need more time to mature before projects like Gutenberg Hub’s landing templates are perfect or at least as close to perfect as can be expected.

\n\n\n\n

There are some things that Gutenberg Hub could improve with its templates. With several that I tested, I needed to switch specific blocks to be full width. This should be set up as the default with templates that are clearly meant to be full width in the example screenshots available on the site. It is a minor issue, but correcting this in the editor fixed several layout issues I was having when using the templates.

\n\n\n\n

Monetization Plans

\n\n\n\n

The second question that Kamal has not been prepared to answer fully over the past several months is how he will monetize Gutenberg Hub. Eventually, developers need some return on their investment when building tons of free tools. Many would do it all for free as long as their bills somehow got paid, but the reality is that there will come a tipping point where their projects need funding for long-haul maintenance.

\n\n\n\n

Kamal said he has laid the groundwork for funding but has not finalized anything yet. Currently, he is working on three ideas:

\n\n\n\n
  • Creating a pro version of his EditorPlus plugin.
  • Offering premium templates and blocks but is looking for a talented designer to work with.
  • Using ads specific to Gutenberg users, but he is not a fan of going this route or ads in general.
\n\n\n\n

He is open to feedback on how to best monetize the website and its projects. However, he said he is unwilling to compromise on giving away current and future free templates and tools.

\n\n\n\n

Future Gutenberg Projects

\n\n\n\n

Kamal said he does not have any new Gutenberg-related projects in the pipeline. The current plan is to work on what he has already created, which is a large ecosystem of Gutenberg tools that somehow work together.

\n\n\n\n

Outside of blocks, templates, and plugins, he is beginning to write more free tutorials on the Gutenberg Hub blog and focusing on creating videos around the project, including a new tutorial series for beginners.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 22 Sep 2020 21:05:19 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:48;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"WPTavern: WordPress Mobile Engineers Propose Dual Licensing Gutenberg under GPL v2.0 and MPL v2.0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105025\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:239:\"https://wptavern.com/wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6556:\"

During a Q&A session at WordCamp Europe 2020 online, Matt Mullenweg mentioned that Gutenberg contributors were considering dual licensing for embedding Gutenberg in mobile apps, along with the requirement that they would need to get an agreement from all contributors. WordPress mobile engineer Maxime Biais has just published a proposal for discussion, recommending dual licensing the editor under GPL v2.0 and MPL v2.0.

\n\n\n\n

“The GPL v2.0 license is a blocker for distributing the Gutenberg library in proprietary mobile apps,” Biais said in the corresponding GitHub issue. “Currently the only known users of Gutenberg on mobile are the WordPress mobile apps which are under GPL v2.0 (WordPress for AndroidWordPress for iOS). Mobile apps under the GPL v2.0 are not common and this limits Gutenberg usage in many apps.

\n\n\n\n

“Rich text editor libraries in the mobile space are lacking. There is no well known open source rich text editor for Android or iOS. We believe that Gutenberg could be a key library for many mobile apps, but that will never happen with the GPL v2.”

\n\n\n\n

Mobile app developers are limited by the GPL, because it requires the entire app to be distributed under the same license. The team is proposing dual licensing under MPL v2.0, a weaker copyleft license that is often considered to be more “business-friendly.” It allows users to combine the software with proprietary code. MPL v2.0 requires the source code for any changes to be available under the MPL, ensuring improvements are shared back to the community. The rest of the app can be distributed under any terms with the MPL v2.0 code included as part of a “larger work.”

\n\n\n\n

“The idea here is to keep some of the WordPress-specific modules under the GPL v2.0 only; some of them are not needed and not relevant for using Gutenberg in another software. Ideally, there would be a different way of bundling the project for being used in WordPress or in a non-GPL software,” Biais said.

\n\n\n\n

The GitHub ticket has several comments from developers who hope to be able to use the editor in their own projects. Radek Pietruszewski, tech lead for a collaborative todo app called Nozbe Teams, has been requesting a relicensing of Gutenberg since October 2019.

\n\n\n\n

“Our tech stack is essentially React on web and React Native on iOS and Android,” Pietruszewski said. “We’re a tiny company, and so we share >80% of app’s codebase between these 3 platforms.

\n\n\n\n

“Our app sorely lacks a WYSIWYG editor. We had a working implementation on web, but we decided to scrap it, because there was no way to port it on iOS and Android. There are pretty much no viable rich text editors for iOS or Android, yet alone both. But even then, shipping three completely separate, but somehow compatible editors would be a vast amount of work.”

\n\n\n\n

When Peitruszewski originally made his case to the mobile team, he identified Gutenberg/Aztec as a basic infrastructure that has the potential to enable many different apps:

\n\n\n\n

And that infrastructure is sorely lacking. There are very few rich text editor libraries on both iOS and Android — and most of them suck. And if you want an editor that has a shared API for both platforms… you’re stuck. There are no options – Gutenberg is the only game in town (and it’s really good).

And it’s very hard to create this infrastructure. WYSIWYG editors are very hard, and it takes entire teams years to develop them (and they still usually suck). Almost no-one has the resources to develop it just for themselves, and if they do, they’re unwilling to open-source it.

\n\n\n\n

Automattic’s mobile app engineers have struggled to get regular contributions to the apps, despite them being open source. Dual licensing Gutenberg could open up a new world of contributors with the editor being used more widely across the industry.

\n\n\n\n

“While we might not be big enough to be able to tackle a challenge of developing a rich text editor from scratch, we’re big enough to contribute features and bug fixes to open source projects,” Pietruszewski said.

\n\n\n\n

Matt Mullenweg was the first comment on Biais’ post in favor of the change:

\n\n\n\n

I think Gutenberg has a chance to become a cross-CMS standard, giving users a familiar interface any place they currently have a rich text box. There are hundreds and hundreds of engineers at other companies solving similar problems in a proprietary way, it would be amazing to get them working together but a huge barrier now is supporting Gutenberg in mobile apps, which every modern web service or CMS has. (Hypothetically, think of Mailchimp as a possible consumer and collaborator here, but it could be any company, SaaS, or other open source CMS.)

\n\n\n\n

Unless any major blockers come up in further discussion, this dual licensing change appears to be on track to move forward. Biais noted that a similar license change has already happened on Aztec-Android and Aztec-iOS. The last hurdle is gaining the approval of all the original code contributors or rewriting the code for those who decline to give approval.

\n\n\n\n

Once Gutenberg can be used under the MPL v2.0, the editor will gain a broader reach, with people already on deck wanting to use it. Other companies and projects that are normally outside WordPress’ open source orbit will also have the opportunity to enrich Gutenberg’s ecosystem with contributions back to the project. At the same time, the MPL 2.0 protects Gutenberg from companies that would try to re-release the code as a closed-source project.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 22:59:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:49;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:124:\"WPTavern: GitHub to Use ‘Main’ Instead of ‘Master’ as the Default Branch on All New Repositories Starting Next Month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105014\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:269:\"https://wptavern.com/github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month?utm_source=rss&utm_medium=rss&utm_campaign=github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4844:\"

In August, GitHub announced that it would change the “master” branch name for all new repositories created on the platform to “main” starting October 1. The date is less than two weeks away, and WordPress developers need to be prepared for the change if they use the service for version control or project management.

\n\n\n\n

The larger tech and web development community began conversations through various venues in June, a time in which the Black Lives Matter was gaining more traction in the U.S. and worldwide. The discussion centered on removing any terminology that could be discriminatory or oppressive to specific groups of people. This ongoing discussion has shown that there is a deep division over whether such changes are necessary or even helpful.

\n\n\n\n

The WordPress community is dealing with this division itself. Aaron Jorbin proposed a change at the same time to rename the default branch name on WordPress-owned repositories. Through discussion on his post and elsewhere, the community landed on “trunk,” which keeps WordPress projects in line with its SVN roots.

\n\n\n\n

“To close the circle on this, a decision was made in June and earlier today (August 19),” wrote Helen Hou-Sandí, a lead WordPress developer, in the comments of the original proposal. “I updated the default branch name for new GitHub repositories under the WordPress organization to be trunk after GitHub enabled early access to that feature.”

\n\n\n\n

As evidenced by the comments on the Tavern’s coverage of the proposal and those on the original post, the WordPress development community as a whole did not support this decision.

\n\n\n\n

Jorbin has updated several of WordPress’s repositories and switched them to use trunk instead of master. However, there are still some lingering projects yet to be updated, including the primary WordPress and WordPress Develop repositories. He left a comment with an updated list in June. There is no public word on whether the existing, leftover projects will be changed.

\n\n\n\n

WordPress Developer Preparations

\n\n\n\nCustomizing the default branch for a user’s GitHub repositories.\n\n\n\n

GitHub is merely changing the default branch name for new repositories starting on October 1. This change does not affect existing repositories. Individual users, organization owners, and enterprise administrators can customize the default branch via their account settings now before the switch is made. Owners can also change the default branch name for individual repositories.

\n\n\n\n

The biggest thing that developers need to watch out for is their tooling or other integrations that might still require the master branch. There may be cases where an alternative default branch name will break workflows. If planning to use a different branch name, the best thing to do right now is to spin up the tools you use on a test repository. If something breaks, check to see whether the particular tool you are using will be getting an update. In most cases, this should not be a problem because customized default branch names will be an industry standard.

\n\n\n\n

The great thing about how GitHub is rolling out this feature is that it offers a choice. Those who believe that “master” is oppressive can change the branch name to something they feel is more inclusive. For those who believe otherwise, they can keep their master branch. But, everyone can use the branch name they prefer.

\n\n\n\n

For existing repositories, GitHub is asking that developers be patient for now. The company is investing in tools to make this a seamless experience later this year. There are a few technical hurdles to clear first.

\n\n\n\n

Developers should read the full GitHub guide on setting the default branch for more information.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 20:39:55 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:8:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:31 GMT\";s:12:\"content-type\";s:8:\"text/xml\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:13:\"last-modified\";s:29:\"Thu, 22 Oct 2020 16:15:08 GMT\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 2\";s:16:\"content-encoding\";s:4:\"gzip\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(133,'_transient_timeout_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(134,'_transient_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603384291','no'),(135,'_transient_timeout_dash_v2_88ae138922fe95674369b1cb3d215a2b','1603427491','no'),(136,'_transient_dash_v2_88ae138922fe95674369b1cb3d215a2b','','no'),(139,'theme_mods_twentytwenty','a:1:{s:18:\"custom_css_post_id\";i:-1;}','yes'),(140,'recently_activated','a:0:{}','yes'); /*!40000 ALTER TABLE `wp55_options` ENABLE KEYS */; UNLOCK TABLES; @@ -249,7 +249,7 @@ CREATE TABLE `wp55_posts` ( LOCK TABLES `wp55_posts` WRITE; /*!40000 ALTER TABLE `wp55_posts` DISABLE KEYS */; -INSERT INTO `wp55_posts` VALUES (1,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

\n','Hello world!','','publish','open','open','','hello-world','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost:9999/?p=1',0,'post','',1),(2,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:

\n\n\n\n

Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)

\n\n\n\n

...or something like this:

\n\n\n\n

The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.

\n\n\n\n

As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!

\n','Sample Page','','publish','closed','open','','sample-page','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost:9999/?page_id=2',0,'page','',0),(3,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','

Who we are

Our website address is: http://localhost:9999.

What personal data we collect and why we collect it

Comments

When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.

An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.

Media

If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.

Contact forms

Cookies

If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.

If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.

When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.

If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.

Embedded content from other websites

Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.

These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.

Analytics

Who we share your data with

How long we retain your data

If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.

For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.

What rights you have over your data

If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.

Where we send your data

Visitor comments may be checked through an automated spam detection service.

Your contact information

Additional information

How we protect your data

What data breach procedures we have in place

What third parties we receive data from

What automated decision making and/or profiling we do with user data

Industry regulatory disclosure requirements

','Privacy Policy','','draft','closed','open','','privacy-policy','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost:9999/?page_id=3',0,'page','',0),(4,1,'2020-10-22 16:31:28','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2020-10-22 16:31:28','0000-00-00 00:00:00','',0,'http://localhost:9999/?p=4',0,'post','',0),(5,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','publish','open','open','','simple_view','','','2020-10-22 16:32:47','2020-10-22 16:32:47','',0,'http://localhost:9999/?p=5',0,'post','',0),(6,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','inherit','closed','closed','','5-revision-v1','','','2020-10-22 16:32:26','2020-10-22 16:32:26','',5,'http://localhost:9999/5-revision-v1',0,'revision','',0); +INSERT INTO `wp55_posts` VALUES (1,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

\n','Hello world!','','publish','open','open','','hello-world','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost/?p=1',0,'post','',1),(2,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:

\n\n\n\n

Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)

\n\n\n\n

...or something like this:

\n\n\n\n

The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.

\n\n\n\n

As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!

\n','Sample Page','','publish','closed','open','','sample-page','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost/?page_id=2',0,'page','',0),(3,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','

Who we are

Our website address is: http://localhost.

What personal data we collect and why we collect it

Comments

When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.

An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.

Media

If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.

Contact forms

Cookies

If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.

If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.

When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.

If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.

Embedded content from other websites

Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.

These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.

Analytics

Who we share your data with

How long we retain your data

If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.

For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.

What rights you have over your data

If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.

Where we send your data

Visitor comments may be checked through an automated spam detection service.

Your contact information

Additional information

How we protect your data

What data breach procedures we have in place

What third parties we receive data from

What automated decision making and/or profiling we do with user data

Industry regulatory disclosure requirements

','Privacy Policy','','draft','closed','open','','privacy-policy','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost/?page_id=3',0,'page','',0),(4,1,'2020-10-22 16:31:28','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2020-10-22 16:31:28','0000-00-00 00:00:00','',0,'http://localhost/?p=4',0,'post','',0),(5,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','publish','open','open','','simple_view','','','2020-10-22 16:32:47','2020-10-22 16:32:47','',0,'http://localhost/?p=5',0,'post','',0),(6,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','inherit','closed','closed','','5-revision-v1','','','2020-10-22 16:32:26','2020-10-22 16:32:26','',5,'http://localhost/5-revision-v1',0,'revision','',0); /*!40000 ALTER TABLE `wp55_posts` ENABLE KEYS */; UNLOCK TABLES; @@ -423,7 +423,7 @@ CREATE TABLE `wp55_users` ( LOCK TABLES `wp55_users` WRITE; /*!40000 ALTER TABLE `wp55_users` DISABLE KEYS */; -INSERT INTO `wp55_users` VALUES (1,'test','$P$BDzpK1XXL9P2cYWggPMUbN87GQSiI80','test','test@gmail.com','http://localhost:9999','2020-10-22 16:31:15','',0,'test'); +INSERT INTO `wp55_users` VALUES (1,'test','$P$BDzpK1XXL9P2cYWggPMUbN87GQSiI80','test','test@gmail.com','http://localhost','2020-10-22 16:31:15','',0,'test'); /*!40000 ALTER TABLE `wp55_users` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; @@ -436,4 +436,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2020-10-22 16:38:54 \ No newline at end of file +-- Dump completed on 2020-10-22 16:38:54 diff --git a/tests/Frameworks/WordPress/Version_5_9/wp-config.php b/tests/Frameworks/WordPress/Version_5_9/wp-config.php index caaa747600..69d2da664a 100644 --- a/tests/Frameworks/WordPress/Version_5_9/wp-config.php +++ b/tests/Frameworks/WordPress/Version_5_9/wp-config.php @@ -20,7 +20,7 @@ // ** Database settings - You can get this info from your web host ** // /** The name of the database for WordPress */ -define( 'DB_NAME', 'test' ); +define( 'DB_NAME', 'wp59' ); /** Database username */ define( 'DB_USER', 'test' ); diff --git a/tests/Frameworks/WordPress/Version_6_1/scripts/wp_initdb.sql b/tests/Frameworks/WordPress/Version_6_1/scripts/wp_initdb.sql index 840355354c..367f6e7127 100644 --- a/tests/Frameworks/WordPress/Version_6_1/scripts/wp_initdb.sql +++ b/tests/Frameworks/WordPress/Version_6_1/scripts/wp_initdb.sql @@ -15,6 +15,8 @@ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +USE wp61; + -- -- Table structure for table `users` -- @@ -172,7 +174,7 @@ CREATE TABLE `wp55_options` ( LOCK TABLES `wp55_options` WRITE; /*!40000 ALTER TABLE `wp55_options` DISABLE KEYS */; -INSERT INTO `wp55_options` VALUES (1,'siteurl','http://localhost:9999','yes'),(2,'home','http://localhost:9999','yes'),(3,'blogname','Datadog Test Application','yes'),(4,'blogdescription','Just another WordPress site','yes'),(5,'users_can_register','0','yes'),(6,'admin_email','test@gmail.com','yes'),(7,'start_of_week','1','yes'),(8,'use_balanceTags','0','yes'),(9,'use_smilies','1','yes'),(10,'require_name_email','1','yes'),(11,'comments_notify','1','yes'),(12,'posts_per_rss','10','yes'),(13,'rss_use_excerpt','0','yes'),(14,'mailserver_url','mail.example.com','yes'),(15,'mailserver_login','login@example.com','yes'),(16,'mailserver_pass','password','yes'),(17,'mailserver_port','110','yes'),(18,'default_category','1','yes'),(19,'default_comment_status','open','yes'),(20,'default_ping_status','open','yes'),(21,'default_pingback_flag','0','yes'),(22,'posts_per_page','10','yes'),(23,'date_format','F j, Y','yes'),(24,'time_format','g:i a','yes'),(25,'links_updated_date_format','F j, Y g:i a','yes'),(26,'comment_moderation','0','yes'),(27,'moderation_notify','1','yes'),(28,'permalink_structure','/%postname%','yes'),(29,'rewrite_rules','a:93:{s:11:\"^wp-json/?$\";s:22:\"index.php?rest_route=/\";s:14:\"^wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:21:\"^index.php/wp-json/?$\";s:22:\"index.php?rest_route=/\";s:24:\"^index.php/wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:17:\"^wp-sitemap\\.xml$\";s:23:\"index.php?sitemap=index\";s:17:\"^wp-sitemap\\.xsl$\";s:36:\"index.php?sitemap-stylesheet=sitemap\";s:23:\"^wp-sitemap-index\\.xsl$\";s:34:\"index.php?sitemap-stylesheet=index\";s:48:\"^wp-sitemap-([a-z]+?)-([a-z\\d_-]+?)-(\\d+?)\\.xml$\";s:75:\"index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]\";s:34:\"^wp-sitemap-([a-z]+?)-(\\d+?)\\.xml$\";s:47:\"index.php?sitemap=$matches[1]&paged=$matches[2]\";s:47:\"category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:42:\"category/(.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:23:\"category/(.+?)/embed/?$\";s:46:\"index.php?category_name=$matches[1]&embed=true\";s:35:\"category/(.+?)/page/?([0-9]{1,})/?$\";s:53:\"index.php?category_name=$matches[1]&paged=$matches[2]\";s:17:\"category/(.+?)/?$\";s:35:\"index.php?category_name=$matches[1]\";s:44:\"tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:39:\"tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:20:\"tag/([^/]+)/embed/?$\";s:36:\"index.php?tag=$matches[1]&embed=true\";s:32:\"tag/([^/]+)/page/?([0-9]{1,})/?$\";s:43:\"index.php?tag=$matches[1]&paged=$matches[2]\";s:14:\"tag/([^/]+)/?$\";s:25:\"index.php?tag=$matches[1]\";s:45:\"type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:40:\"type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:21:\"type/([^/]+)/embed/?$\";s:44:\"index.php?post_format=$matches[1]&embed=true\";s:33:\"type/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?post_format=$matches[1]&paged=$matches[2]\";s:15:\"type/([^/]+)/?$\";s:33:\"index.php?post_format=$matches[1]\";s:12:\"robots\\.txt$\";s:18:\"index.php?robots=1\";s:13:\"favicon\\.ico$\";s:19:\"index.php?favicon=1\";s:48:\".*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\\.php$\";s:18:\"index.php?feed=old\";s:20:\".*wp-app\\.php(/.*)?$\";s:19:\"index.php?error=403\";s:18:\".*wp-register.php$\";s:23:\"index.php?register=true\";s:32:\"feed/(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:27:\"(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:8:\"embed/?$\";s:21:\"index.php?&embed=true\";s:20:\"page/?([0-9]{1,})/?$\";s:28:\"index.php?&paged=$matches[1]\";s:41:\"comments/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:36:\"comments/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:17:\"comments/embed/?$\";s:21:\"index.php?&embed=true\";s:44:\"search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:39:\"search/(.+)/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:20:\"search/(.+)/embed/?$\";s:34:\"index.php?s=$matches[1]&embed=true\";s:32:\"search/(.+)/page/?([0-9]{1,})/?$\";s:41:\"index.php?s=$matches[1]&paged=$matches[2]\";s:14:\"search/(.+)/?$\";s:23:\"index.php?s=$matches[1]\";s:47:\"author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:42:\"author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:23:\"author/([^/]+)/embed/?$\";s:44:\"index.php?author_name=$matches[1]&embed=true\";s:35:\"author/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?author_name=$matches[1]&paged=$matches[2]\";s:17:\"author/([^/]+)/?$\";s:33:\"index.php?author_name=$matches[1]\";s:69:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:64:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:45:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$\";s:74:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true\";s:57:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:81:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]\";s:39:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$\";s:63:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]\";s:56:\"([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:51:\"([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:32:\"([0-9]{4})/([0-9]{1,2})/embed/?$\";s:58:\"index.php?year=$matches[1]&monthnum=$matches[2]&embed=true\";s:44:\"([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:65:\"index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]\";s:26:\"([0-9]{4})/([0-9]{1,2})/?$\";s:47:\"index.php?year=$matches[1]&monthnum=$matches[2]\";s:43:\"([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:38:\"([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:19:\"([0-9]{4})/embed/?$\";s:37:\"index.php?year=$matches[1]&embed=true\";s:31:\"([0-9]{4})/page/?([0-9]{1,})/?$\";s:44:\"index.php?year=$matches[1]&paged=$matches[2]\";s:13:\"([0-9]{4})/?$\";s:26:\"index.php?year=$matches[1]\";s:27:\".?.+?/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\".?.+?/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\".?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\".?.+?/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"(.?.+?)/embed/?$\";s:41:\"index.php?pagename=$matches[1]&embed=true\";s:20:\"(.?.+?)/trackback/?$\";s:35:\"index.php?pagename=$matches[1]&tb=1\";s:40:\"(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:35:\"(.?.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:28:\"(.?.+?)/page/?([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&paged=$matches[2]\";s:35:\"(.?.+?)/comment-page-([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&cpage=$matches[2]\";s:24:\"(.?.+?)(?:/([0-9]+))?/?$\";s:47:\"index.php?pagename=$matches[1]&page=$matches[2]\";s:27:\"[^/]+/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\"[^/]+/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\"[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\"[^/]+/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"([^/]+)/embed/?$\";s:37:\"index.php?name=$matches[1]&embed=true\";s:20:\"([^/]+)/trackback/?$\";s:31:\"index.php?name=$matches[1]&tb=1\";s:40:\"([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:35:\"([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:28:\"([^/]+)/page/?([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&paged=$matches[2]\";s:35:\"([^/]+)/comment-page-([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&cpage=$matches[2]\";s:24:\"([^/]+)(?:/([0-9]+))?/?$\";s:43:\"index.php?name=$matches[1]&page=$matches[2]\";s:16:\"[^/]+/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:26:\"[^/]+/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:46:\"[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:22:\"[^/]+/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";}','yes'),(30,'hack_file','0','yes'),(31,'blog_charset','UTF-8','yes'),(32,'moderation_keys','','no'),(33,'active_plugins','a:1:{i:0;s:19:\"datadog/datadog.php\";}','yes'),(34,'category_base','','yes'),(35,'ping_sites','http://rpc.pingomatic.com/','yes'),(36,'comment_max_links','2','yes'),(37,'gmt_offset','0','yes'),(38,'default_email_category','1','yes'),(39,'recently_edited','','no'),(40,'template','twentytwenty','yes'),(41,'stylesheet','twentytwenty','yes'),(42,'comment_registration','0','yes'),(43,'html_type','text/html','yes'),(44,'use_trackback','0','yes'),(45,'default_role','subscriber','yes'),(46,'db_version','48748','yes'),(47,'uploads_use_yearmonth_folders','1','yes'),(48,'upload_path','','yes'),(49,'blog_public','0','yes'),(50,'default_link_category','2','yes'),(51,'show_on_front','posts','yes'),(52,'tag_base','','yes'),(53,'show_avatars','1','yes'),(54,'avatar_rating','G','yes'),(55,'upload_url_path','','yes'),(56,'thumbnail_size_w','150','yes'),(57,'thumbnail_size_h','150','yes'),(58,'thumbnail_crop','1','yes'),(59,'medium_size_w','300','yes'),(60,'medium_size_h','300','yes'),(61,'avatar_default','mystery','yes'),(62,'large_size_w','1024','yes'),(63,'large_size_h','1024','yes'),(64,'image_default_link_type','none','yes'),(65,'image_default_size','','yes'),(66,'image_default_align','','yes'),(67,'close_comments_for_old_posts','0','yes'),(68,'close_comments_days_old','14','yes'),(69,'thread_comments','1','yes'),(70,'thread_comments_depth','5','yes'),(71,'page_comments','0','yes'),(72,'comments_per_page','50','yes'),(73,'default_comments_page','newest','yes'),(74,'comment_order','asc','yes'),(75,'sticky_posts','a:0:{}','yes'),(76,'widget_categories','a:2:{i:2;a:4:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:12:\"hierarchical\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(77,'widget_text','a:0:{}','yes'),(78,'widget_rss','a:0:{}','yes'),(79,'uninstall_plugins','a:0:{}','no'),(80,'timezone_string','','yes'),(81,'page_for_posts','0','yes'),(82,'page_on_front','0','yes'),(83,'default_post_format','0','yes'),(84,'link_manager_enabled','0','yes'),(85,'finished_splitting_shared_terms','1','yes'),(86,'site_icon','0','yes'),(87,'medium_large_size_w','768','yes'),(88,'medium_large_size_h','0','yes'),(89,'wp_page_for_privacy_policy','3','yes'),(90,'show_comments_cookies_opt_in','1','yes'),(91,'admin_email_lifespan','1618936275','yes'),(92,'disallowed_keys','','no'),(93,'comment_previously_approved','1','yes'),(94,'auto_plugin_theme_update_emails','a:0:{}','no'),(95,'initial_db_version','48748','yes'),(96,'wp55_user_roles','a:5:{s:13:\"administrator\";a:2:{s:4:\"name\";s:13:\"Administrator\";s:12:\"capabilities\";a:61:{s:13:\"switch_themes\";b:1;s:11:\"edit_themes\";b:1;s:16:\"activate_plugins\";b:1;s:12:\"edit_plugins\";b:1;s:10:\"edit_users\";b:1;s:10:\"edit_files\";b:1;s:14:\"manage_options\";b:1;s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:6:\"import\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:8:\"level_10\";b:1;s:7:\"level_9\";b:1;s:7:\"level_8\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;s:12:\"delete_users\";b:1;s:12:\"create_users\";b:1;s:17:\"unfiltered_upload\";b:1;s:14:\"edit_dashboard\";b:1;s:14:\"update_plugins\";b:1;s:14:\"delete_plugins\";b:1;s:15:\"install_plugins\";b:1;s:13:\"update_themes\";b:1;s:14:\"install_themes\";b:1;s:11:\"update_core\";b:1;s:10:\"list_users\";b:1;s:12:\"remove_users\";b:1;s:13:\"promote_users\";b:1;s:18:\"edit_theme_options\";b:1;s:13:\"delete_themes\";b:1;s:6:\"export\";b:1;}}s:6:\"editor\";a:2:{s:4:\"name\";s:6:\"Editor\";s:12:\"capabilities\";a:34:{s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;}}s:6:\"author\";a:2:{s:4:\"name\";s:6:\"Author\";s:12:\"capabilities\";a:10:{s:12:\"upload_files\";b:1;s:10:\"edit_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;s:22:\"delete_published_posts\";b:1;}}s:11:\"contributor\";a:2:{s:4:\"name\";s:11:\"Contributor\";s:12:\"capabilities\";a:5:{s:10:\"edit_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;}}s:10:\"subscriber\";a:2:{s:4:\"name\";s:10:\"Subscriber\";s:12:\"capabilities\";a:2:{s:4:\"read\";b:1;s:7:\"level_0\";b:1;}}}','yes'),(97,'fresh_site','0','yes'),(98,'widget_search','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(99,'widget_recent-posts','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(100,'widget_recent-comments','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(101,'widget_archives','a:2:{i:2;a:3:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(102,'widget_meta','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(103,'sidebars_widgets','a:4:{s:19:\"wp_inactive_widgets\";a:0:{}s:9:\"sidebar-1\";a:3:{i:0;s:8:\"search-2\";i:1;s:14:\"recent-posts-2\";i:2;s:17:\"recent-comments-2\";}s:9:\"sidebar-2\";a:3:{i:0;s:10:\"archives-2\";i:1;s:12:\"categories-2\";i:2;s:6:\"meta-2\";}s:13:\"array_version\";i:3;}','yes'),(104,'cron','a:7:{i:1603384279;a:5:{s:32:\"recovery_mode_clean_expired_keys\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:34:\"wp_privacy_delete_old_export_files\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"hourly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:3600;}}s:16:\"wp_version_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:17:\"wp_update_plugins\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_update_themes\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1603384285;a:2:{s:19:\"wp_scheduled_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:25:\"delete_expired_transients\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1603384288;a:1:{s:30:\"wp_scheduled_auto_draft_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1603384345;a:1:{s:28:\"wp_update_comment_type_batch\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:2:{s:8:\"schedule\";b:0;s:4:\"args\";a:0:{}}}}i:1603384346;a:1:{s:8:\"do_pings\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:2:{s:8:\"schedule\";b:0;s:4:\"args\";a:0:{}}}}i:1603470679;a:1:{s:30:\"wp_site_health_scheduled_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"weekly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:604800;}}}s:7:\"version\";i:2;}','yes'),(105,'widget_pages','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(106,'widget_calendar','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(107,'widget_media_audio','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(108,'widget_media_image','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(109,'widget_media_gallery','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(110,'widget_media_video','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(111,'widget_tag_cloud','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(112,'widget_nav_menu','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(113,'widget_custom_html','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(114,'_transient_doing_cron','1603384688.0394918918609619140625','yes'),(115,'_site_transient_update_core','O:8:\"stdClass\":4:{s:7:\"updates\";a:1:{i:0;O:8:\"stdClass\":10:{s:8:\"response\";s:6:\"latest\";s:8:\"download\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:6:\"locale\";s:5:\"en_US\";s:8:\"packages\";O:8:\"stdClass\":5:{s:4:\"full\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:10:\"no_content\";s:70:\"https://downloads.wordpress.org/release/wordpress-5.5.1-no-content.zip\";s:11:\"new_bundled\";s:71:\"https://downloads.wordpress.org/release/wordpress-5.5.1-new-bundled.zip\";s:7:\"partial\";s:0:\"\";s:8:\"rollback\";s:0:\"\";}s:7:\"current\";s:5:\"5.5.1\";s:7:\"version\";s:5:\"5.5.1\";s:11:\"php_version\";s:6:\"5.6.20\";s:13:\"mysql_version\";s:3:\"5.0\";s:11:\"new_bundled\";s:3:\"5.3\";s:15:\"partial_version\";s:0:\"\";}}s:12:\"last_checked\";i:1603384286;s:15:\"version_checked\";s:5:\"5.5.1\";s:12:\"translations\";a:0:{}}','no'),(116,'_site_transient_update_plugins','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384412;s:7:\"checked\";a:3:{s:19:\"akismet/akismet.php\";s:5:\"4.1.6\";s:19:\"datadog/datadog.php\";s:5:\"0.0.0\";s:9:\"hello.php\";s:5:\"1.7.2\";}s:8:\"response\";a:0:{}s:12:\"translations\";a:0:{}s:9:\"no_update\";a:2:{s:19:\"akismet/akismet.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:21:\"w.org/plugins/akismet\";s:4:\"slug\";s:7:\"akismet\";s:6:\"plugin\";s:19:\"akismet/akismet.php\";s:11:\"new_version\";s:5:\"4.1.6\";s:3:\"url\";s:38:\"https://wordpress.org/plugins/akismet/\";s:7:\"package\";s:56:\"https://downloads.wordpress.org/plugin/akismet.4.1.6.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:59:\"https://ps.w.org/akismet/assets/icon-256x256.png?rev=969272\";s:2:\"1x\";s:59:\"https://ps.w.org/akismet/assets/icon-128x128.png?rev=969272\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:61:\"https://ps.w.org/akismet/assets/banner-772x250.jpg?rev=479904\";}s:11:\"banners_rtl\";a:0:{}}s:9:\"hello.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:25:\"w.org/plugins/hello-dolly\";s:4:\"slug\";s:11:\"hello-dolly\";s:6:\"plugin\";s:9:\"hello.php\";s:11:\"new_version\";s:5:\"1.7.2\";s:3:\"url\";s:42:\"https://wordpress.org/plugins/hello-dolly/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/plugin/hello-dolly.1.7.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-256x256.jpg?rev=2052855\";s:2:\"1x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-128x128.jpg?rev=2052855\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:66:\"https://ps.w.org/hello-dolly/assets/banner-772x250.jpg?rev=2052855\";}s:11:\"banners_rtl\";a:0:{}}}}','no'),(117,'_site_transient_timeout_theme_roots','1603386087','no'),(118,'_site_transient_theme_roots','a:3:{s:14:\"twentynineteen\";s:7:\"/themes\";s:15:\"twentyseventeen\";s:7:\"/themes\";s:12:\"twentytwenty\";s:7:\"/themes\";}','no'),(119,'_site_transient_update_themes','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384287;s:7:\"checked\";a:3:{s:14:\"twentynineteen\";s:3:\"1.7\";s:15:\"twentyseventeen\";s:3:\"2.4\";s:12:\"twentytwenty\";s:3:\"1.5\";}s:8:\"response\";a:0:{}s:9:\"no_update\";a:3:{s:14:\"twentynineteen\";a:6:{s:5:\"theme\";s:14:\"twentynineteen\";s:11:\"new_version\";s:3:\"1.7\";s:3:\"url\";s:44:\"https://wordpress.org/themes/twentynineteen/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/theme/twentynineteen.1.7.zip\";s:8:\"requires\";s:5:\"4.9.6\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:15:\"twentyseventeen\";a:6:{s:5:\"theme\";s:15:\"twentyseventeen\";s:11:\"new_version\";s:3:\"2.4\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentyseventeen/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentyseventeen.2.4.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:12:\"twentytwenty\";a:6:{s:5:\"theme\";s:12:\"twentytwenty\";s:11:\"new_version\";s:3:\"1.5\";s:3:\"url\";s:42:\"https://wordpress.org/themes/twentytwenty/\";s:7:\"package\";s:58:\"https://downloads.wordpress.org/theme/twentytwenty.1.5.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}}s:12:\"translations\";a:0:{}}','no'),(120,'_site_transient_timeout_browser_6daa110c3e56e442b403473c9591e946','1603989088','no'),(121,'_site_transient_browser_6daa110c3e56e442b403473c9591e946','a:10:{s:4:\"name\";s:6:\"Chrome\";s:7:\"version\";s:12:\"86.0.4240.80\";s:8:\"platform\";s:9:\"Macintosh\";s:10:\"update_url\";s:29:\"https://www.google.com/chrome\";s:7:\"img_src\";s:43:\"http://s.w.org/images/browsers/chrome.png?1\";s:11:\"img_src_ssl\";s:44:\"https://s.w.org/images/browsers/chrome.png?1\";s:15:\"current_version\";s:2:\"18\";s:7:\"upgrade\";b:0;s:8:\"insecure\";b:0;s:6:\"mobile\";b:0;}','no'),(122,'_site_transient_timeout_php_check_56babb1797dd31750a342dc4c8a11025','1603989088','no'),(123,'_site_transient_php_check_56babb1797dd31750a342dc4c8a11025','a:5:{s:19:\"recommended_version\";s:3:\"7.4\";s:15:\"minimum_version\";s:6:\"5.6.20\";s:12:\"is_supported\";b:1;s:9:\"is_secure\";b:1;s:13:\"is_acceptable\";b:1;}','no'),(124,'_site_transient_timeout_community-events-e0e4f94be3c2d577e126ec3b012627f2','1603427490','no'),(125,'_site_transient_community-events-e0e4f94be3c2d577e126ec3b012627f2','a:4:{s:9:\"sandboxed\";b:0;s:5:\"error\";N;s:8:\"location\";a:1:{s:2:\"ip\";s:12:\"192.168.16.0\";}s:6:\"events\";a:2:{i:0;a:10:{s:4:\"type\";s:6:\"meetup\";s:5:\"title\";s:58:\"Discussion Group: WordPress Troubleshooting Basics: Part 1\";s:3:\"url\";s:68:\"https://www.meetup.com/learn-wordpress-discussions/events/273993927/\";s:6:\"meetup\";s:27:\"Learn WordPress Discussions\";s:10:\"meetup_url\";s:51:\"https://www.meetup.com/learn-wordpress-discussions/\";s:4:\"date\";s:19:\"2020-10-23 06:00:00\";s:8:\"end_date\";s:19:\"2020-10-23 07:00:00\";s:20:\"start_unix_timestamp\";i:1603458000;s:18:\"end_unix_timestamp\";i:1603461600;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"US\";s:8:\"latitude\";d:37.779998779297;s:9:\"longitude\";d:-122.41999816895;}}i:1;a:10:{s:4:\"type\";s:8:\"wordcamp\";s:5:\"title\";s:17:\"WordCamp Bulgaria\";s:3:\"url\";s:35:\"https://bulgaria.wordcamp.org/2020/\";s:6:\"meetup\";N;s:10:\"meetup_url\";N;s:4:\"date\";s:19:\"2020-10-24 10:00:00\";s:8:\"end_date\";s:19:\"2020-10-24 10:00:00\";s:20:\"start_unix_timestamp\";i:1603522800;s:18:\"end_unix_timestamp\";i:1603522800;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"BG\";s:8:\"latitude\";d:42.733883;s:9:\"longitude\";d:25.48583;}}}}','no'),(126,'can_compress_scripts','0','no'),(127,'_transient_timeout_feed_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(128,'_transient_feed_9bbd59226dc36b9b26cd43f15694c5c3','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:49:\"\n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:27:\"News – – WordPress.org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:13:\"lastBuildDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 20:10:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"en-US\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"generator\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"https://wordpress.org/?v=5.6-beta1-49274\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:10:{i:0;a:6:{s:4:\"data\";s:60:\"\n \n\n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WordPress 5.6 Beta 1 is now available for testing!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8236:\"\n

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:38:\"The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:363:\"This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September.  WordPress 5.5.1 Launch On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8713:\"\n

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"WordPress 5.5.1 Maintenance Release\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"https://wordpress.org/news/2020/09/wordpress-5-5-1-maintenance-release/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 19:13:53 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8979\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:460:\"WordPress 5.5.1 is now available! This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade. You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process. […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9020:\"\n

WordPress 5.5.1 is now available!

\n\n\n\n

This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade.

\n\n\n\n

You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process.

\n\n\n\n

WordPress 5.5.1 is a short-cycle maintenance release. The next major release will be version 5.6.

\n\n\n\n

To see a full list of changes, you can browse the list on Trac, read the 5.5.1 RC1 and 5.5.1 RC2 posts, or visit the 5.5.1 documentation page.

\n\n\n\n

Thanks and props!

\n\n\n\n

The 5.5.1 release was led by @audrasjb, @azhiyadev, @davidbaumwald, @desrosj, @johnbillion, @planningwrite, @sergeybiryukov and @whyisjake.

\n\n\n\n

Thank you to everyone who helped make WordPress 5.5.1 happen:

\n\n\n\nAmit Dudhat, Andrea Fercia, Andrey “Rarst” Savchenko, Andy Fragen, Angel Hess, avixansa, bobbingwide, Brian Hogg, chunkysteveo, Clayton Collie, David Baumwald, David Herrera, dd32, demetris, Dominik Schilling, dushakov, Earle Davies, Enrique Sánchez, Frankie Jarrett, fullofcaffeine, Garrett Hyder, Gary Jones, gchtr, Hauwa, Herre Groen, Howdy_McGee, Ipstenu (Mika Epstein), Jb Audras, Jeremy Felt, Jeroen Rotty, Joen A., Johanna de Vos, John Blackbourn, John James Jacoby, Jonathan Bossenger, Jonathan Desrosiers, Jonathan Stegall, Joost de Valk, Jorge Costa, Justin Ahinon, Kalpesh Akabari, Kevin Hagerty, Knut Sparhell, Kyle B. Johnson, landau, Laxman Prajapati, Lester Chan, mailnew2ster, Marius L. J., Mark Jaquith, Mark Uraine, Matt Gibson, Michael Beckwith, Mikey Arce, Mohammad Jangda, Mukesh Panchal, Nabil Moqbel, net, oakesjosh, O André, Omar Reiss, Ov3rfly, Paddy, Pascal Casier, Paul Biron, Peter Wilson, rajeshsingh520, Rami Yushuvaev, rebasaurus, riaanlom, Riad Benguella, Rodrigo Arias, rtagliento, salvoaranzulla, Sanjeev Aryal, sarahricker, Sergey Biryukov, Stephen Bernhardt, Steven Stern (sterndata), Thomas M, Timothy Jacobs, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), TwentyZeroTwo, Winstina, wittich, and Yoav Farhi.\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8979\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"The Month in WordPress: August 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wordpress.org/news/2020/09/the-month-in-wordpress-august-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 09:32:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8983\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:362:\"August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world. WordPress 5.5 Launch […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9605:\"\n

August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Launch

\n\n\n\n

The team launched WordPress 5.5 on August 11. The major release comes with a host of features like automatic updates for plugins and themes, enabling updates over uploaded ZIP files, a block directory, XML sitemaps, block patterns, inline image editing, and lazy-loading images, to name a few. WordPress 5.5 is now available in 50 languages too! You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. Subsequent to the 5.5 release, the 5.5.1 release candidate came out on August 28, which will be followed by its official launch of the minor release on September 1.

\n\n\n\n

A record 805 people contributed to WordPress 5.5, hailing from 58 different countries. @audrasjb has compiled many more stats like that and they’re well worth a read!

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.7 and 8.8

\n\n\n\n

The core team launched Gutenberg 8.7 and 8.8. Version 8.7 saw many improvements to the Post Block suite, along with other changes like adding a block example to the Buttons block, consistently autosaving edits, and updating the group block description. Version 8.8 offers updates to Global Styles, the Post Block suite, and Template management. The release significantly improves the back-compatibility of the new Widget Screen, and also includes other important accessibility and mobile improvements to user interfaces like the Toolbar, navigation menus, and Popovers. For full details on the latest versions of these Gutenberg releases, visit these posts about 8.7 and 8.8.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Check out the brand new Learn WordPress platform!

\n\n\n\n

Learn WordPress is a brand new cross-team initiative led by the WordPress Community team, with support from the training team, the TV team, and the meta team. This platform is a learning repository on learn.wordpress.org, where WordPress learning content will be made available. Video workshops published on the site will be followed up by supplementary discussion groups based on workshop content. The first of these discussion groups have been scheduled, and you can join an upcoming discussion on the dedicated meetup group. The community team invites members to contribute to the project. You can apply to present a workshop, assist with reviewing submitted workshops, and add ideas for workshops that you would like to see on the site. You can also apply to be a discussion group leader to organize discussions directly through the learn.wordpress.org platform. We are also creating a dedicated Learn WordPress working group and have posted a call for volunteers. Meetup organizers can use Learn WordPress content for their meetup events (without applying as a discussion group leader). Simply ask your meetup group to watch one of the workshops in the weeks leading up to your scheduled event, and then host a discussion group for that content as your event.

\n\n\n\n

Want to get involved with the Community team? Follow the Community blog, or join them in the #community-events channel in the Making WordPress Slack group. To organize a local WordPress community event, visit the handbook page

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8983\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n\n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"WordPress 5.5 “Eckstine”\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"https://wordpress.org/news/2020/08/eckstine/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 11 Aug 2020 19:03:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8799\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:354:\"Version 5.5 \"Eckstine\" of WordPress is available for download or update in your WordPress dashboard. With this release, your site gets new power in three major areas: \nspeed (lazy-loading images), search (sitemaps included by default), and security (auto-updates for plugins and themes), along with many new features and improvements to the block editor.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:3:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:48:\"https://s.w.org/images/core/5.5/auto-updates.mp4\";s:6:\"length\";s:6:\"238264\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:50:\"https://s.w.org/images/core/5.5/block-patterns.mp4\";s:6:\"length\";s:7:\"3518792\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:56:\"https://s.w.org/images/core/5.5/inline-image-editing.mp4\";s:6:\"length\";s:7:\"3145140\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Matt Mullenweg\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:71062:\"\n

Here it is! Named “Eckstine” in honor of Billy Eckstine, this latest and greatest version of WordPress is available for download or update in your dashboard.

\n\n\n\n
\"\"
\n\n\n\n
\n

Welcome to WordPress 5.5.

\n\n\n\n

In WordPress 5.5, your site gets new power in three major areas:
speed, search, and security.

\n
\n\n\n\n
\n
\n\n\n\n
\n

Speed

\n\n\n\n

Posts and pages feel faster, thanks to lazy-loaded images.

\n\n\n\n

Images give your story a lot of impact, but they can sometimes make your site seem slow.

\n\n\n\n

In WordPress 5.5, images wait to load until they’re just about to scroll into view. The technical term is ‘lazy loading.’

\n\n\n\n

On mobile, lazy loading can also keep browsers from loading files meant for other devices. That can save your readers money on data — and help preserve battery life.

\n\n\n\n

Search

\n\n\n\n

Say hello to your new sitemap.

\n\n\n\n

WordPress sites work well with search engines.

\n\n\n\n

Now, by default, WordPress 5.5 includes an XML sitemap that helps search engines discover your most important pages from the very minute you go live.

\n\n\n\n

So more people will find your site sooner, giving you more time to engage, retain and convert them to subscribers, customers or whatever fits your definition of success.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Security

\n\n\n\n
Now you can choose to update plugins and themes automatically–or pick just a few–from the screens you’ve always used.
\n\n\n\n

Auto-updates for Plugins and Themes

\n\n\n\n

Now you can set plugins and themes to update automatically — or not! — in the WordPress admin. So you always know your site is running the latest code available.

\n\n\n\n

You can also turn auto-updates on or off for each plugin or theme you have installed — all on the same screens you’ve always used.

\n\n\n\n

Update by uploading ZIP files

\n\n\n\n

If updating plugins and themes manually is your thing, now that’s easier too — just upload a ZIP file.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Highlights from the block editor

\n\n\n\n

Once again, the latest WordPress release packs a long list of exciting new features for the block editor. For example:

\n\n\n\n
\n\n\n\n
\n
\n

Block patterns

\n\n\n\n

New block patterns make it simple and fun to create complex, beautiful layouts, using combinations of text and media that you can mix and match to fit your story.

\n\n\n\n

You will also find block patterns in a wide variety of plugins and themes, with more added all the time. Pick any of them from a single place — just click and go!

\n
\n\n\n\n
\n

The new block directory

\n\n\n\n

Now it’s easier than ever to find the block you need. The new block directory is built right into the block editor, so you can install new block types to your site without ever leaving the editor.

\n\n\n\n

Inline image editing

\n\n\n\n

Crop, rotate, and zoom your photos right from the image block. If you spend a lot of time on images, this could save you hours!

\n
\n
\n\n\n\n
\n\n\n\n

And so much more.

\n\n\n\n

The highlights above are a tiny fraction of the new block editor features you’ve just installed. Open the block editor and enjoy!

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Accessibility

\n\n\n\n

Every release adds improvements to the accessible publishing experience, and that remains true for WordPress 5.5.

\n\n\n\n

Now you can copy links in media screens and modal dialogs with a button, instead of trying to highlight a line of text.

\n\n\n\n

You can also move meta boxes with the keyboard, and edit images in WordPress with your assistive device, as it can read you the instructions in the image editor.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

For developers

\n\n\n\n

5.5 also brings a big box of changes just for developers.

\n\n\n\n
\n
\n

Server-side registered blocks in the REST API

\n\n\n\n

The addition of block types endpoints means that JavaScript apps (like the block editor) can retrieve definitions for any blocks registered on the server.

\n\n\n\n

Defining environments

\n\n\n\n

WordPress now has a standardized way to define a site’s environment type (staging, production, etc). Retrieve that type with wp_get_environment_type() and execute only the appropriate code.

\n\n\n\n

Dashicons

\n\n\n\n

The Dashicons library has received its final update in 5.5. It adds 39 block editor icons along with 26 others.

\n\n\n\n

Passing data to template files

\n\n\n\n

The template loading functions (get_header()get_template_part(), etc.) have a new $args argument. So now you can pass an entire array’s worth of data to those templates.

\n
\n\n\n\n
\n

More changes for developers

\n\n\n\n
  • The PHPMailer library just got a major update, going from version 5.2.27 to 6.1.6.
  • Now get more fine-grained control of redirect_guess_404_permalink().
  • Sites that use PHP’s OPcache will see more reliable cache invalidation, thanks to the new wp_opcache_invalidate() function during updates (including to plugins and themes).
  • Custom post types associated with the category taxonomy can now opt-in to supporting the default term.
  • Default terms can now be specified for custom taxonomies in register_taxonomy().
  • The REST API now officially supports specifying default metadata values through register_meta().
  • You will find updated versions of these bundled libraries: SimplePie, Twemoji, Masonry, imagesLoaded, getID3, Moment.js, and clipboard.js.
\n
\n
\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

The Squad

\n\n\n\n

Leading this release were Matt MullenwegJake Spurlock, and David Baumwald. Supporting them was this highly enthusiastic release squad:

\n\n\n\n\n\n\n\n

Joining the squad throughout the release cycle were 805 generous volunteer contributors who collectively worked on over 523 tickets on Trac and over 1660 pull requests on GitHub.

\n\n\n\n

Put on a Billy Eckstine playlist, click that update button (or download it directly), and check the profiles of the fine folks that helped:

\n\n\nA2 Hosting, a4jp . com, a6software, Aaron D. Campbell, Aaron Jorbin, abderrahman, Abha Thakor, Achal Jain, achbed, Achyuth Ajoy, acosmin, acsnaterse, Adam Silverstein, Addie, addyosmani, adnan.limdi, adrian, airamerica, Ajay Ghaghretiya, Ajit Bohra, akbarhusen, akbarhusen429, Akhilesh Sabharwal, Akira Tachibana, Alain Schlesser, Albert Juhé Lluveras, Alex Concha, Alex Kirk, Alex Lende, Alex Shiels, Ali Shan, ali11007, Allen Snook, amaschas, Amit Dudhat, anbumz, andfinally, Andrea Fercia, Andrea Middleton, Andrea Tarantini, Andrei Draganescu, Andrew Duthie, Andrew Nacin, Andrew Nevins, Andrew Ozz, Andrey \"Rarst\" Savchenko, Andrés Maneiro, Andy Fragen, Andy Meerwaldt, Andy Peatling, Angel Hess, Angela Jin, Angelika Reisiger, Anh Tran, Ankit Gade, Ankit K Gupta, Ankit Panchal, Anne McCarthy, Anthony Burchell, Anthony Hortin, Anton Timmermans, Antonis Lilis, apedog, archon810, argentite, Arpit G Shah, Arslan Ahmed, asalce, ashiagr, ashour, Atharva Dhekne, Aurélien Joahny, aussi, automaton, avixansa, Ayesh Karunaratne, BackuPs, Barry, Barry Ceelen, Bart Czyz, bartekcholewa, bartkalisz, Bastien Ho, Bastien Martinent, bcworkz, bdbch, bdcstr, Ben Dunkle, Bence Szalai, bencroskery, Benjamin Gosset, Benoit Chantre, Bernhard Reiter, BettyJJ, bgermann, bigcloudmedia, bigdawggi, Bill Erickson, Birgir Erlendsson (birgire), Birgit Pauli-Haack, BjornW, bobbingwide, bonger, Boone Gorges, Boris Brdarić, Boy Witthaya, Brandon Kraft, Brandon Payton, Brent Swisher, Brian Hogg, Brian Krogsgard, bruandet, Bunty, Burhan Nasir, caiocrcosta, Cameron Voell, cameronamcintyre, Carike, Carl Wuensche, Carlos Galarza, Carolina Nymark, Caroline Moore, Carrigan, ceyhun, Chad, Chad Butler, Charles Fulton, Chetan Prajapati, Chintan hingrajiya, Chip Snyder, Chloé Bringmann, Chouby, Chris Van Patten, chriscct7, Christian Chung, Christian Jongeneel, Christian Sabo, Christian Wach, Christoph Herr, Christopher Churchill, chunkysteveo, cklee, clayray, Clayton Collie, Clifford Paulick, codeforest, Commeuneimage, Copons, Corey McKrill, cpasqualini, Cristovao Verstraeten, Csaba (LittleBigThings), Curtis Belt, Cyrus Collier, D.PERONNE, d6, Daniel Bachhuber, Daniel Hüsken, Daniel James, Daniel Llewellyn, Daniel Richards, Daniel Roch, Daniele Scasciafratte, Danny, Darko G., Darren Ethier (nerrad), Dave McHale, Dave Whitley, David A. Kennedy, David Aguilera, David Anderson, David Artiss, David Baumwald, David Brumbaugh, David E. Smith, David Herrera, David Ryan, David Shanske, David Smith, david.binda, davidvee, dchymko, Debabrata Karfa, Deepak Lalwani, dekervit, Delowar Hossain, demetris, Denis Yanchevskiy, derekakelly, Derrick Hammer, Derrick Tennant, Diane Co, Dilip Bheda, Dimitris Mitsis, dingo-d, Dion Hulse, Dixita Dusara, djennez, dmenard, dmethvin, doc987, Dominik Schilling, donmhico, Dono12, Doobeedoo, Dossy Shiobara, dpacks, dratwas, Drew Jaynes, DrLightman, DrProtocols, dsifford, dudo, dushakov, Dustin Bolton, dvershinin, Dylan Kuhn, Earle Davies, ecotechie, Eddie Moya, Eddy, Edi Amin, ehtis, Eileen Violini, Ekaterina, Ella van Durpe, elmastudio, Emanuel Blagonic, Emilie LEBRUN, Emmanuel Hesry, Enej Bajgoric, Enrico Sorcinelli, Enrique Piqueras, Enrique Sánchez, Eric, Eric Andrew Lewis, Eric Binnion, Erik Betshammar, Erin \'Folletto\' Casali, esemlabel, esoj, espiat, Estela Rueda, etoledom, etruel, Ev3rywh3re, Evan Mullins, Fabian Kägy, Fabian Todt, Faisal Ahmed, Felix Arntz, Felix Edelmann, ferdiesletering, finomeno, Florian Brinkmann, Florian TIAR, Florian Truchot, florianatwhodunit, FolioVision, Francesca Marano, Francois Thibaud, Frank Goossens, Frank Klein, Frank.Prendergast, Frankie Jarrett, Franz Armas, fullofcaffeine, Gabriel Koen, Gabriel Maldonado, Gabriel Mays, gadgetroid, Gal Baras, Garavani, garethgillman, Garrett Hyder, Gary Cao, Gary Jones, Gary Pendergast, gchtr, Geert De Deckere, Gemini Labs, Gennady Kovshenin, geriux, Giorgio25b, gisselfeldt, glendaviesnz, goldsounds, Goto Hayato, Govind Kumar, Grégory Viguier, gradina, Greg Ziółkowski, gregmulhauser, grierson, Grzegorz.Janoszka, gsmumbo, Guido Scialfa, guidobras, Gunther Pilz, gwwar, H-var, hakre, Halacious, hankthetank, Hapiuc Robert, Hareesh, haukep, Hauwa, Haz, Hector Farahani, Helen Hou-Sandi, Henry Wright, Herre Groen, hlanggo, hommealone, Hoover, Howdy_McGee, Hronak Nahar, huntlyc, Ian Belanger, Ian Dunn, Ian Stewart, ianjvr, ifrins, infinum, Ipstenu (Mika Epstein), Isabel Brison, ishitaka, J.D. Grimes, jackfungi, jacklinkers, Jadon N, jadpm, jagirbahesh, Jake Spurlock, Jake Whiteley, James Koster, James Nylen, Jan Koch, Jan Reilink, Jan Thiel, Janvo Aldred, Jarret, Jason Adams, Jason Coleman, Jason Cosper, Jason Crouse, Jason LeMahieu (MadtownLems), Jason Rouet, JasWSInc, Javier Casares, Jayson Basanes, jbinda, jbouganim, Jean-Baptiste Audras, Jean-David Daviet, Jeff Chandler, Jeff Farthing, Jeff Ong, Jeff Paul, Jen, Jenil Kanani, Jeremy Felt, Jeremy Herve, Jeremy Yip, Jeroen Rotty, jeryj, Jesin A, Jignesh Nakrani, Jim_Panse, Jip Moors, jivanpal, Joe Dolson, Joe Hoyle, Joe McGill, Joen Asmussen, Johanna de Vos, John Blackbourn, John Dorner, John James Jacoby, John P. Green, John Richards II, John Watkins, johnnyb, Jon Quach, Jon Surrell, Jonathan Bossenger, Jonathan Champ, Jonathan Christopher, Jonathan Desrosiers, Jonathan Stegall, jonkolbert, Jonny Harris, jonnybot, Jono Alderson, Joost de Valk, Jorge Bernal, Jorge Costa, Joseph Dickson, Josepha Haden, Josh Smith, JoshuaWold, Joy, Juanfra Aldasoro, juanlopez4691, Jules Colle, julianm, Juliette Reinders Folmer, Julio Potier, Julka Grodel, Justin Ahinon, Justin de Vesine, Justin Tadlock, justlevine, justnorris, K. Adam White, kaggdesign, Kailey (trepmal), Kaira, Kaitlin Bolling, Kalpesh Akabari, KamataRyo, Kantari Samy, Kaspars, Kavya Gokul, keesiemeijer, Kelly Dwan, kennethroberson5556, Kevin Hagerty, Kharis Sulistiyono, Khokan Sardar, kinjaldalwadi, Kiril Zhelyazkov, Kirsty Burgoine, Kishan Jasani, kitchin, Kite, Kjell Reigstad, Knut Sparhell, Konstantin Obenland, Konstantinos Xenos, ksoares, KT Cheung, Kukhyeon Heo, Kyle B. Johnson, lalitpendhare, landau, Laterna Studio, laurelfulford, Laurens Offereins, Laxman Prajapati, Lester Chan, Levdbas, Lew Ayotte, Lex Robinson, linyows, lipathor, Lisa Schuyler, liuhaibin, ljharb, logig, lucasbustamante, luiswill, Luke Cavanagh, Luke Walczak, lukestramasonder, M Asif Rahman, M.K. Safi, Maarten de Boer, Mahfoudh Arous, mailnew2ster, manojlovic, Manuel Schmalstieg, maraki, Marcin Pietrzak, Marcio Zebedeu, Marco Pereirinha, MarcoZ, Marcus, Marcus Kazmierczak, Marek Dědič, Marek Hrabe, Mario Valney, Marius Jensen, Mark Chouinard, Mark Jaquith, Mark Parnell, Mark Uraine, markdubois, markgoho, Marko Andrijasevic, Marko Heijnen, MarkRH, markshep, markusthiel, Martijn van der Kooij, martychc23, Mary Baum, Matheus Martins, Mathieu Viet, Matias Ventura, matjack1, Matt Cromwell, Matt Gibson, Matt Mullenweg, Matt Radford, Matt van Andel, mattchowning, Matthew Boynes, Matthew Eppelsheimer, Matthew Gerring, Matthias Kittsteiner, Matthias Pfefferle, Matthieu Mota, mattyrob, Maxime Culea, Maxime Pertici, maxme, Mayank Majeji, mcshane, Mel Choyce-Dwan, Menaka S., mensmaximus, metalandcoffee, Michael, Michael Arestad, Michael Arestad, Michael Beckwith, Michael Fields, Michael Nelson, Michele Butcher-Jones, Michelle, Miguel Fonseca, mihdan, Miina Sikk, Mikael Korpela, mikaumoto, Mike Crantea, Mike Glendinning, Mike Haydon, Mike Schinkel [WPLib Box project lead], Mike Schroder, Mikey Arce, Milana Cap, Milind More, mimi, mislavjuric, Mohammad Jangda, Mohammad Rockeybul Alam, Mohsin Rasool, Monika Rao, Morgan Kay, Morten Rand-Hendriksen, Morteza Geransayeh, moto hachi ( mt8.biz ), mrgrt, mrmist, mrTall, msaggiorato, Muhammad Usama Masood, Mukesh Panchal, munyagu, Nabil Moqbel, Nadir Seghir, Nahid Ferdous Mohit, Nalini Thakor, Naoko Takano, narwen, Nate Gay, Nathan Rice, Navid, neonkowy, net, netpassprodsr, Nextendweb, Ngan Tengyuen, Nick Daugherty, Nicky Lim, nicolad, Nicolas Juen, NicolasKulka, Nidhi Jain, Niels de Blaauw, Niels Lange, nigro.simone, Nik Tsekouras, Nikhil Bhansi, Nikolay Bachiyski, Nilo Velez, Niresh, nmenescardi, Noah Allen, NumidWasNotAvailable, oakesjosh, obliviousharmony, ockham, Olga Gleckler, Omar Alshaker, Omar Reiss, onokazu, Optimizing Matters, Ov3rfly, ovann86, overclokk, p_enrique, Paal Joachim Romdahl, Pablo Honey, Paddy, palmiak, Paresh Shinde, Parvand, Pascal Birchler, Pascal Casier, Paul Bearne, Paul Biron, Paul Fernhout, Paul Gibbs, Paul Ryan, Paul Schreiber, Paul Stonier, Paul Von Schrottky, pavelevap, Pedro Mendonça, pentatonicfunk, pepe, Peter \"Pessoft\" Kolínek, Peter Westwood, Peter Wilson, Phil Derksen, Phil Johnston, Philip Jackson, Pierre Gordon, pigdog234, pikamander2, pingram, Pionect, Piyush Patel, pkarjala, pkvillanueva, Prashant Baldha, pratik028, Pravin Parmar, Presskopp, Presslabs, Priyank Patel, Priyo Mukul, ProGrafika, programmin, Puneet Sahalot, pvogel2, r-a-y, Raaj Trambadia, Rachel Peter, raine, rajeshsingh520, Ramanan, Rami Yushuvaev, RavanH, Ravat Parmar, ravenswd, rawrly, rebasaurus, Red Sand Media Group, Remy Perona, Remzi Cavdar, Renatho, renggo888, retlehs, retrofox, riaanlom, Riad Benguella, Rian Rietveld, riasat, Rich Tabor, Ringisha, ritterml, Rnaby, Rob Cutmore, Rob Migchels, rob006, Robert Anderson, Robert Chapin, Robert Peake, Robert Windisch, Rodrigo Arias, Ronald Huereca, Rostislav Wolný, Roy Tanck, rtagliento, ruxandra, Ryan Boren, Ryan Fredlund, Ryan Kienstra, Ryan McCue, Ryan Welcher, Ryota Sakamoto, ryotsun, Sören Wrede, Søren Brønsted, Sachit Tandukar, Sagar Jadhav, Sajjad Hossain Sagor, Sal Ferrarello, Salvatore Formisano, salvoaranzulla, Sam Fullalove, Sam Webster, Samir Shah, Samuel Wood (Otto), samueljseay, Sander van Dragt, Sanjeev Aryal, Sanket Mehta, sarahricker, Sathiyamoorthy V, Sayed Taqui, scarolan, scholdstrom, Scott Kingsley Clark, Scott Reilly, Scott Smith, Scott Taylor, scribu, scruffian, Sean Hayes, seanpaulrasmussen, seayou, senatorman, Sergey Biryukov, Sergey Predvoditelev, Sergio de Falco, sergiomdgomes, Shannon Smith, Shantanu Desai, shaunandrews, Shawn Hooper, shawnz, Shital Marakana, shulard, siliconforks, Simon Wheatley, simonjanin, sinatrateam, sjmur, skarabeq, skorasaurus, skoskie, slushman, snapfractalpop, SpearsMarketing, sphakka, squarecandy, sreedoap, Stanimir Stoyanov, Stefano Minoia, Stefanos Togoulidis, Steph Wells, Stephen Bernhardt, Stephen Cronin, Stephen Edgar, Steve Dufresne, stevegibson12, Steven Stern (sterndata), Steven Word, stevenkussmaul, stevenlinx, Stiofan, Subrata Sarkar, SUM1, Sunny, Sunny Ratilal, Sushyant Zavarzadeh, suzylah, Sybre Waaijer, Synchro, Sérgio Estêvão, Takayuki Miyauchi, Tammie Lister, Tang Rufus, TeBenachi, Tessa Watkins LLC, Tetsuaki Hamano, theMikeD, theolg, Thierry Muller, Thimal Wickremage, Thomas M, Thorsten Frommen, Thrijith Thankachan, Tiago Hillebrandt, Till Krüss, Timothy Jacobs, Tkama, tmdesigned, tmoore41, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), Tofandel, tomdude, Tommy Ferry, Tony G, Toro_Unit (Hiroshi Urabe), torres126, Torsten Landsiedel, Toru Miki, Travis Northcutt, treecutter, truongwp, tsimmons, Tung Du, Udit Desai, Ulrich, Vagios Vlachos, valchovski, Valentin Bora, Vayu Robins, veromary, Viktor Szépe, vinkla, virginienacci, Vladimir, Vladislav Abrashev, vortfu, voyager131, vtieu, webaware, Weston Ruter, William Earnhardt, williampatton, Winstina, wittich, wpdesk, WPDO, WPMarmite, wppinar, Yahil Madakiya, yashrs, yoancutillas, Yoav Farhi, yohannp, yuhin, Yui, Yuri Salame, Yvette Sonneveld, Zack Tollman, zaheerahmad, zakkath, Zebulan Stanphill, zieladam, and Česlav Przywara.\n\n\n\n

 

\n\n\n\n

Many thanks to all of the community volunteers who contribute in the support forums. They answer questions from people across the world, whether they are using WordPress for the first time or since the first release. These releases are more successful for their efforts!

\n\n\n\n

Finally, thanks to all the community translators who worked on WordPress 5.5. Their efforts bring WordPress fully translated to 46 languages at release time, with more on the way.

\n\n\n\n

If you want to learn more about volunteering with WordPress, check out Make WordPress or the core development blog.

\n
\n\n\n\n
\n
\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8799\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"WordPress 5.5 Release Candidate 2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"https://wordpress.org/news/2020/08/wordpress-5-5-release-candidate-2/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 04 Aug 2020 19:12:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8764\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:420:\"The second release candidate for WordPress 5.5 is here! WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time! You can test the WordPress 5.5 release candidate in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding edge nightlies” option) Or download the release […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2503:\"\n

The second release candidate for WordPress 5.5 is here!

\n\n\n\n

WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

For a more detailed breakdown of the changes included in WordPress 5.5, check out the WordPress 5.5 beta 1 post. The WordPress 5.5 Field Guide is also out! It’s your source for details on all the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8764\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n\n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"The Month in WordPress: July 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"https://wordpress.org/news/2020/08/the-month-in-wordpress-july-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 03 Aug 2020 13:54:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8755\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:340:\"July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:11539:\"\n

July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Updates

\n\n\n\n

July was full of WordPress 5.5 updates! The WordPress 5.5 Beta 1 came out on July 7, followed by Beta 2 on July 14, Beta 3 on July 21, and Beta 4 on July 27. Subsequently, the team also published the first release candidate of WordPress 5.5 on July 28. 

\n\n\n\n

WordPress 5.5, which is slated for release on August 11, 2020, is a major update with features like automatic updates for plugins and themes, a block directory, XML sitemaps, block patterns, and lazy-loading images, among others. To learn more about the release, check out its field guide post.

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.5 and 8.6

\n\n\n\n

The core team launched Gutenberg 8.5 and 8.6. Version 8.5 – the last plugin release will be included entirely (without experimental features) in WordPress 5.5, introduced improvements to block drag-and-drop and accessibility, easier updates for external images, and support for the block directory. Version 8.6 comes with features like Cover block video position controls and block pattern updates. For full details on the latest versions on these Gutenberg releases, visit these posts about 8.5 and 8.6.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Reimagining Online WordPress Events

\n\n\n\n

The Community team made the difficult decision to suspend in-person WordPress events for the rest of 2020 in light of the COVID-19 pandemic. The team has also started working on reimagining online events. Based on feedback from the community members, the team decided to make changes to the current online WordCamp format. Key changes include wrapping up financial support for A/V vendors, ending event swag support for newer online WordCamps, and suspending the Global Community Sponsorship program for 2020. The team encourages upcoming online WordCamps to experiment with their events to facilitate an effective learning experience for attendees while avoiding online event fatigue. The team is currently working on a proposal to organize community-supported recorded workshops and synchronous discussion groups to help community members learn WordPress.

Want to get involved with the Community team? Follow the Community blog here, or join them in the #community-events channel in the Making WordPress Slack group. To organize a Meetup or WordCamp, visit the handbook page

\n\n\n\n

WordCamp US 2020 is canceled

\n\n\n\n

The organizers of WordCamp US 2020 have canceled the event in light of the continued pandemic and online event fatigue. The flagship event, which was originally scheduled for October 27-29 as an in-person event, had already planned to transition to an online event. Several WCUS Organizers will be working with the WordPress Community team to focus on other formats and ideas for online events, including a 24-hour contributor day, and contributing to the workshops initiative currently being discussed. Matt Mullenweg’s State of the Word (which typically accompanies WordCamp US) is likely to take place in a different format later in 2020.

\n\n\n\n

Plugin and theme updates are now available over zip files

\n\n\n\n

After eleven years, WordPress now allows users to update plugins and themes by uploading a ZIP file, in WordPress 5.5.  The feature, which was merged on July 7, has been one of the most requested features in WordPress. Now, when a user tries to upload a plugin or theme zip file from the WordPress dashboard by clicking the “Install Now” button, WordPress will direct users to a new screen that compares the currently-installed extension with the uploaded versions. Users can then choose between continuing with the installation or canceling. WordPress 5.5 will also offer automatic plugin and theme updates

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n
  • The Block directory is coming to WordPress with the 5.5 release. Plugin authors can now submit their Block plugins to the directory.
  • The Core team has opened up the call for features in the WordPress 5.6 release. You can comment on the post with features that you’d like to be included, current UX pain points, or maintenance tickets that need to be addressed. August 20 is the deadline for feature requests. 
  • Editor features such as the new Navigation block, the navigation screen, and the widget screen that were originally planned to be merged with WordPress 5.5 have been pushed for the next release
  • The Theme team is inviting proposals on whether to allow themes to place an additional top-level menu link in the admin.
  • BuddyPress 6.2 beta is out in the wild, and the team will soon release the stable version. The update includes changes that will make BuddyPress fully compatible with WordPress 5.5.
  • WordCamp EU 2021, which was being planned as an in-person event in Porto, Portugal, is moving online. The team is considering an in-person WordCamp EU in 2022. 
  • The Polyglots team has prepared and finalized a Translation Editor & Locale Manager Vetting Criteria to provide more clarity on how global mentors assign PTE/GTE/Locale Managers and to help locale teams set their own guidelines. The document, which was finalized after a lot of discussion, is now available in the Polyglots handbook.
  • Members of the Community team are discussing whether WordCamp volunteers, WordCamp attendees, or Meetup attendees should be awarded a WordPress.org profile badge. The ongoing discussion will be open for comments until August 13.
  • The WP Notify project, which aims to create a better way to manage and deliver notifications to the relevant audience, is on to its next steps. The team has finalized the initial requirements, and is kicking off the project build.
  • The WordPress documentation team is considering a ban on links to commercial websites in a revision to its external linking policy. The policy change does not remove external links to commercial sites from WordPress.org and only applies to documentation sites. The idea is to protect documentation from being abused, and to prevent the WordPress project from being biased. Discussion on this post is still ongoing, and a decision has not yet been made. Feel free to comment on the discussion posts, if you would like to share your thoughts on the topic.
\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8755\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"WordPress 5.5 Release Candidate\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://wordpress.org/news/2020/07/wordpress-5-5-release-candidate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 28 Jul 2020 19:08:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8732\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:370:\"The first release candidate for WordPress 5.5 is now available! This is an important milestone in the community’s progress toward the final release of WordPress 5.5. “Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2970:\"\n

The first release candidate for WordPress 5.5 is now available!

\n\n\n\n

This is an important milestone in the community’s progress toward the final release of WordPress 5.5.

\n\n\n\n

“Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

What’s in WordPress 5.5?

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developer notes tag for updates on those and other changes that could affect your products.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

The WordPress 5.5 Field Guide, due very shortly, will give you a more detailed dive into the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8732\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 4\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-4/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 27 Jul 2020 20:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8719\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:313:\"WordPress 5.5 Beta 4 is now available! This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 4 in two ways: Try the WordPress Beta Tester plugin (choose the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"David Baumwald\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3812:\"\n

WordPress 5.5 Beta 4 is now available!

\n\n\n\n

This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 4 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 3 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 3, 43 bugs have been fixed. Here are a few changes in beta 4:

\n\n\n\n
  • Add \"loading\" as an allowed kses image attribute (see #50731).
  • Add filter for the plugin/theme auto-update message in the Info tab of Site health (see #50663).
  • $_SERVER[\'SERVER_NAME\'] not a reliable when generating email host names (see #25239)
  • Several backported fixes from Gutenberg are included in WordPress 5.5 Beta 4 (See PR #24218)
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8719\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 3\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-3/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 21 Jul 2020 17:51:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8706\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:324:\"WordPress 5.5 Beta 3 is now available! This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 3 in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3876:\"\n

WordPress 5.5 Beta 3 is now available!

\n\n\n\n

This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 3 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 2 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 2, 43 bugs have been fixed. Here are a few changes in beta 3:

\n\n\n\n
  • Plugin and theme versions are now shared in the emails when automatically updated (see #50350).
  • REST API routes without a permission_callback now trigger a _doing_it_wrong() warning (see #50075).
  • Over 23 Gutenberg changes and updates (see #24068 and #50712).
  • A bug with the new import and export database Dashicons has been fixed (see #49913).
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8706\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}s:27:\"http://www.w3.org/2005/Atom\";a:1:{s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:4:\"href\";s:32:\"https://wordpress.org/news/feed/\";s:3:\"rel\";s:4:\"self\";s:4:\"type\";s:19:\"application/rss+xml\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:44:\"http://purl.org/rss/1.0/modules/syndication/\";a:2:{s:12:\"updatePeriod\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"\n hourly \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:15:\"updateFrequency\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"\n 1 \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:4:\"site\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"14607090\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:9:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:30 GMT\";s:12:\"content-type\";s:34:\"application/rss+xml; charset=UTF-8\";s:25:\"strict-transport-security\";s:11:\"max-age=360\";s:6:\"x-olaf\";s:3:\"⛄\";s:13:\"last-modified\";s:29:\"Wed, 21 Oct 2020 20:10:31 GMT\";s:4:\"link\";s:63:\"; rel=\"https://api.w.org/\"\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(129,'_transient_timeout_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(130,'_transient_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603384291','no'),(131,'_transient_timeout_feed_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(132,'_transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:16:\"WordPress Planet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"en\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WordPress Planet - http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:50:{i:0;a:6:{s:4:\"data\";s:13:\"\n\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Loginizer Plugin Gets Forced Security Update for Vulnerabilities Affecting 1 Million Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106557\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users?utm_source=rss&utm_medium=rss&utm_campaign=loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5484:\"

WordPress.org has pushed out a forced security update for the Loginizer plugin, which is active on more than 1 million websites. The plugin offers brute force protection in its free version, along with other security features like two-factor auth, reCAPTCHA, and PasswordLess login in its commercial upgrade.

\n\n\n\n

Last week security researcher Slavco Mihajloski discovered an unauthenticated SQL injection vulnerability, and an XSS vulnerability, that he disclosed to the plugin’s authors. Loginizer version 1.6.4 was released on October 16, 2020, with patches for the two issues, summarized on the plugin’s blog:

\n\n\n\n

1) [Security Fix] : A properly crafted username used to login could lead to SQL injection. This has been fixed by using the prepare function in PHP which prepares the SQL query for safe execution.

2) [Security Fix] : If the IP HTTP header was modified to have a null byte it could lead to stored XSS. This has been fixed by properly sanitizing the IP HTTP header before using the same.

\n\n\n\n

Loginizer did not disclose the vulnerability until today in order to give users the time to upgrade. Given the severity of the vulnerability, the plugins team at WordPress.org pushed out the security update to all sites running Loginizer on WordPress 3.7+.

\n\n\n\n

In July, 2020, Loginizer was acquired by Softaculous, so the company was also able to automatically upgrade any WordPress installations with Loginizer that had been created using Softaculous. This effort, combined with the updates from WordPress.org, covered a large portion of Loginizer’s user base.

\n\n\n\n
\n

Any #WordPress install with @loginizer probably isn\'t using another WAF solution. As you can notice from the graph 600k+500k active installs were updated upside down, so … Preauth SQLi in it, reported by me. Update! Crunching write up :) https://t.co/gkEVWun9wt pic.twitter.com/XWXVMYO1ED

— mslavco (@mslavco) October 19, 2020
\n
\n\n\n\n

The automatic update took some of the plugin’s users by surprise, since they had not initiated it themselves and had not activated automatic updates for plugins. After several users posted on the plugin’s support forum, plugin team member Samuel Wood said that “WordPress.org has the ability to turn on auto-updates for security issues in plugins” and has used this capability many times.

\n\n\n\n

Mihajloski published a more detailed proof-of-concept on his blog earlier today. He also highlighted some concerns regarding the systems WordPress has in place that allowed this kind of of severe vulnerability to slip through the cracks. He claims the issue could have easily been prevented by the plugin review team since the plugin wasn’t using the prepare function for safe execution of SQL queries. Mihajloski also recommended recurring code audits for plugins in the official directory.

\n\n\n\n

“When a plugin gets into the repository, it must be reviewed, but when is it reviewed again?” Mihajloski said. “Everyone starts with 0 active installs, but what happens on 1k, 10k, 50k, 100k, 500k, 1mil+ active installs?”

\n\n\n\n

Mihajloski was at puzzled how such a glaring security issue could remain in the plugin’s code so long, given that it is a security plugin with an active install count that is more than many well known CMS’s. The plugin also recently changed hands when it was acquired by Softaculus and had been audited by multiple security organizations, including WPSec and Dewhurst Security.

\n\n\n\n

Mihajloski also recommends that WordPress improve the transparency around security, as some site owners and closed communities may not be comfortable with having their assets administered by unknown people at WordPress.org.

\n\n\n\n

“WordPress.org in general is transparent, but there isn’t any statement or document about who, how and when decides about and performs automatic updates,” Mihajloski said. “It is kind of [like] holding all your eggs in one basket.

\n\n\n\n

“I think those are the crucial points that WP.org should focus on and everything will came into place in a short time: complete WordPress tech documentation for security warnings, a guide for disclosure of the bugs (from researchers, but also from a vendor aspect), and recurring code audits for popular plugins.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 22 Oct 2020 03:47:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"Post Status: Joe Casabona on creating quality content and courses\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=80099\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"https://poststatus.com/joe-casabona-on-creating-quality-content-and-courses/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1407:\"

David Bisset interviews Joe Casabona, an independent creator and teacher, and discusses what it\'s like to be a creator as his job, plus some news topics.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Sandhills Development

\n\n\n\n

Sandhills Development crafts ingenuity, developed with care:

\n\n\n\n
  • Easy Digital Downloads – Sell digital products with WordPress
  • AffiliateWP – A full-featured affiliate marketing solution
  • Sugar Calendar – WordPress event management made simple
  • WP Simple Pay – A lightweight Stripe payments plugin
\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:17:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: MakeStories 2.0 Launches Editor for WordPress, Rivaling Google’s Official Web Stories Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106327\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin?utm_source=rss&utm_medium=rss&utm_campaign=makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8860:\"Recipe slide from the MakeStories WordPress plugin.\n\n\n\n

Earlier today, MakeStories launched version 2.0 of its plugin for creating Web Stories with WordPress. In many ways, this is a new plugin launch. The previous version simply allowed users to connect their WordPress installs to the MakeStories site. With the new version, users can build and edit their stories directly from the WordPress admin.

\n\n\n\n

Version 2.0 of the plugin still requires an account and a connection with the MakeStories.io website. However, it is simple to set up. Users can log in without leaving their WordPress admin interface. This API connection means that user-created Stories are stored on the MakeStories servers. If an end-user wanted to jump platforms from WordPress to something else, this would allow them to take their Stories with them.

\n\n\n\n

“One of the things we would like to assure is your content is still yours, and none of the user data is being consumed or analyzed by us,” said Pratik Ghela, the founder and product manager at MakeStories. “We only take enough data to help serve you better.”

\n\n\n\n

The plugin is a competing solution to the official Web Stories plugin by Google. While the two share similarities in the final output (they are built to utilize the same front-end format for creating Stories on the web), they take different paths to get there.

\n\n\n\n

The two share similarities on the backend too. However, MakeStories may be more polished in some areas. For example, it allows users to zoom in on the small canvas area. Having the ability to reorder slides from the grid view also feels more intuitive.

\n\n\n\n

“The main unique selling proposition of our plugin is that it comes with a guarantee of the MakeStories team,” said Ghela. “We as a team have been building this for over two years, and we are proud to be one of the tools that has stood the test of time, and competition and is still growing at a very fast pace.”

\n\n\n\n

The team also wants to make the Story-creating process faster, safer, and rewarding. The goal is to cater to designers, developers, and content creators. Ghela also feels like his team’s support turnaround time of a few hours will be the key to success and is a good reason for users to give this plugin a try before settling on something else.

\n\n\n\n

“We feel that our goal is to see Web Stories flourish,” he said. “And we may have different types of users looking out for various options. So, the official plugin from Google and the one from MakeStories at least opens up the options for users to choose from. And we feel that the folks at Google are also building a great editor, and, at the end of the day, it’s up to the user to select what they feel is the best.

\n\n\n\n

Technically, MakeStories is a SaaS (software as a service) product. Even though it is a free plugin, there will eventually be a commercial component to it. Currently, it is free at least until the first quarter of 2021, which may be extended based on various factors. There is no word on what pricing tiers may be available after that.

\n\n\n\n

“There will always be a free tier, and we have always stood for it that your data belongs to you,” said Ghela. “In case you do not like the pricing, we will personally assist you to port out from using our editor and still keep the data and everything totally intact.”

\n\n\n\n

Diving Into the Plugin

\n\n\n\nStory management screen.\n\n\n\n

MakeStories is a drag-and-drop editor for building Web Stories. It works and feels much like typical design editors like Gimp or Photoshop. It shares similarities with QuarkXPress or InDesign, for those familiar with page layout programs. In some ways, it feels a lot like a light-colored version of Google’s Web Stories plugin with more features and a slightly more intuitive interface.

\n\n\n\n

The end goal is simple: create a Story through designing slides/pages that site visitors will click through as the narrative unfolds.

\n\n\n\n

The plugin provides a plethora of shapes, textures, and animations. These features are easy to find and implement. It also includes free access to images, GIFs, and videos. These are made possible via API integrations with Unsplash, Tenor, and Pexels.

\n\n\n\n

MakeStories includes access to 10 templates at the moment. However, what makes this feature stand out is that end-users can create and save custom templates for reuse down the road.

\n\n\n\nEditing a Story from a predesigned template.\n\n\n\n

One of the more interesting, almost hidden, features is the available text patterns. The plugin allows users to insert these patterns from a couple of dozen choices. This makes it easier to visualize a design without having to build everything from scratch.

\n\n\n\nInserting a text pattern and adjusting its size.\n\n\n\n

While the editing process is a carefully-crafted experience that makes the plugin worth a look, it is the actual publishing aspect of the workflow that is a bit painful. Traditional publishing in WordPress means hitting the “publish” button to make content live. This is not the case with the MakeStories plugin. It takes you through a four-step process of entering various publisher details, setting up metadata and SEO, validating the Story content, and analytics. It is not that these steps are necessarily bad. For example, MakeStories lets you know when images are missing alt text, which is needed information screen readers. The problem is that it feels out of place to go through all of these details when I, as a user, simply want my content published. And, many of these details, such as the publisher (author), should be automatically filled in.

\n\n\n\n

Updating a Story is not as simple as hitting an “update” button either. The system needs to run through some of the same steps too.

\n\n\n\n

Ghela said the publishing process might be a bit tough but will prove fruitful in the end. The plugin takes care of the technical aspects of adding title tags, meta, and other data on the front end after the user fills in the form fields.

\n\n\n\n

“We will definitely work on improving the flow as the community evolves and improve it a lot to be easier, faster, and, most importantly, still very customizable,” he said.

\n\n\n\n

The MakeStories team has no plans of stopping at its current point on the roadmap. Ghela sounded excited about some of the upcoming additions they are planning, including features like teams, branding, easy template customization, polls, and quizzes.

\n\n\n\n

On the Web Stories Format

\n\n\n\nUN report on COVID-19 and poverty published with MakeStories.\n\n\n\n

Many will ultimately hesitate to use any plugin that implements Web Stories given Google’s history of dropping projects. There is also a feeling that the format is a bit of a fad and will not stand the test of time.

\n\n\n\n

“We greatly believe in AMP and Web Stories as a content format,” said Ghela. “We, as an agency, have been involved a lot in AMP and have done a lot of experiments with it, including a totally custom WooCommerce site in fully-native, valid AMP with support for variable products, subscriptions, and other functionalities.”

\n\n\n\n

The company is all-in on the format and feels like it will be around for the long term, particularly if there is a good ecosystem around monetization.

\n\n\n\n

“We think that the initial reactions are because there are not enough proven results and because we never imagined the story format to come to the web,” said Ghela. “There were definitely plugins that did this. Few folks tried to build stories using good ol’ HTML, CSS, and JavaScript. But, the performance and UX were not that great. On the other hand, the engineers at the AMP Team are making sure that everything is just perfect. The UX, load time, WCV Score, just everything.”

\n\n\n\n

He feels that some of the early criticisms are unwarranted and that the web development community should give the format a try and provide feedback.

\n\n\n\n

“The more data we all get, actually gives the AMP team a clear idea of what’s needed, and they can design the roadmap accordingly,” he said. “So, just giving out early reactions won’t help, but constructive criticism and getting back to the AMP team with what you are doing will.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:12:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"WordPress.org blog: WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7956:\"

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"WPTavern: WordPress 5.6 Release Team Pulls the Plug on Block-Based Widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106466\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:193:\"https://wptavern.com/wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8762:\"Current block-based widgets admin screen design.\n\n\n\n

I was wrong. I assured our readers that “the block-based widget system will be ready for prime time when WordPress 5.6 lands” in my previous post on the new feature’s readiness. I also said that was on the condition of not trying to make it work with the customizer — that experience was still broken. However, the 5.6 team pulled the plug on block-based widgets for the second time this year.

\n\n\n\n

One week ago, WordPress 5.6 release lead Josepha Haden seemed to agree that it would be ready. However, things can change quickly in a development cycle, and tough decisions have to be made with beta release deadlines.

\n\n\n\n

This is not the first feature the team has punted to a future release. Two weeks ago, they dropped block-based nav menus from the 5.6 feature list. Both features were originally planned for WordPress 5.5.

\n\n\n\n

A new Widgets admin screen has been under development since January 2019, which was not long after the initial launch of the block editor in WordPress 5.0. For now, the block-based widgets feature has been punted to WordPress 5.7. It has also been given the “early” tag, which means it should go into core WordPress soon after the 5.7 release cycle begins. This will give it more time to mature and more people an opportunity to test it.

\n\n\n\n

Helen Hou-Sandì, the core tech lead for 5.6, provided a historical account of the decision and why it was not ready for inclusion in the new ticket:

\n\n\n\n

My question for features that affect the front-end is “can I try out this new thing without the penalty of messing up my site?” — that is, user trust. At this current moment, given that widget areas are not displayed anything like what you see on your site without themes really putting effort into it and that you have to save your changes live without revisions to get an actual contextual view, widget area blocks do not allow you to try this new feature without penalizing you for experimenting.

\n\n\n\n

She went on to say that the current experience is subpar at the moment. Problems related to the customizer experience, which I covered in detail over a month ago, were also mentioned.

\n\n\n\n

“So, when we come back to this again, let’s keep sight of what it means to keep users feeling secure that they can get their site looking the way they want with WordPress, and not like they are having to work around what we’ve given them,” said Hou-Sandì.

\n\n\n\n

This is a hopeful outlook despite the tough decision. Sometimes, these types of calls need to be made for the good of the project in the long term. Pushing back a feature to a future version for a better user experience can be better than launching early with a subpar experience.

\n\n\n\n

“The good part of this is that now widgets can continue to be ‘re-imagined’ for 5.7, and get even more enhancements,” said lead WordPress developer Andrew Ozz in the ticket. “Not sure how many people have tested this for a bit longer but having blocks in the widgets areas (a.k.a. sidebars) opens up many new possibilities and makes a lot of the old, limited widgets obsolete. The ‘widget areas’ become something like ‘specialized posts with more dynamic content,’ letting users (and designers) do a lot of stuff that was either hard or impossible with the old widgets.”

\n\n\n\n

After the letdown of seeing one of my most anticipated features of 5.6 being dropped, it is encouraging to see the positive outlook from community leaders on the project.

\n\n\n\n

“You know, I was really hopeful for it too, and that last-minute call was one I labored over,” said Haden. “When I last looked, it did seem close to ready, but then more focused testing was done and there were some interactions that are a little rough for users. I’m grateful for that because the time to discover painful user experiences is before launch rather than after!”

\n\n\n\n

Despite dropping its second major feature, WordPress 5.6 still has some big highlights that will be shipping in less than two months. The new Twenty Twenty-One theme looks to be a breath of fresh air and will explore block-related features not seen in previous default themes. Haden also pointed out auto-updates for major releases, application passwords support for the REST API, and accessibility improvements as features to look forward to.

\n\n\n\n

WordPress 5.6 Beta 1 is expected to ship today.

\n\n\n\n

Adding New Features To an Old Project

\n\n\n\n

At times, it feels like the Gutenberg project has bitten off more than it can chew. Many of the big feature plans continually miss projections. Between full-site editing, global styles, widgets, nav menus, and much more, it is tough to get hyper-focused on one feature and have it ready to ship. On the other hand, too much focus one way can be to the detriment to other features in the long run. All of these pieces must eventually come together to create a more cohesive whole.

\n\n\n\n

WordPress is also 17 years old. Any new feature could affect legacy features or code. The goal for block-based widgets is to transition an existing feature to work within a new system without breaking millions of websites in the process. Twenty-one months of work on a single feature shows that it is not an easy problem to solve.

\n\n\n\n

“You are so right about complex engineering problems!” said Haden. “We are now at a point in the history of the project where connecting all of the pieces can have us facing unforeseen complications.”

\n\n\n\n

The project also needs to think about how it can address some of the issues it has faced with not quite getting major features to completion. Is the team stretched too thin to focus on all the parts? Are there areas we can improve to push features forward?

\n\n\n\n

“There will be a retrospective where we can identify what parts of our process can be improved in the future, but I also feel like setting stretch goals is good for any software project,” said Haden. “Many contributors have a sense of urgency around bringing the power of blocks to more spaces in WordPress, which I share, but when it’s time to ship, we have to balance that with our deep commitment to usability.”

\n\n\n\n

One problem that has become increasingly obvious is that front-end editing has become tougher over the years. Currently, widgets and nav menus can be edited in two places in WordPress with wildly different interfaces. Full-site editing stands to add an entirely new interface to the mix.

\n\n\n\n

“I think one of the problems that we’re trying to solve with Gutenberg has always been a more consistent experience for editing elements across the WordPress interface,” said Haden. “No user should have to learn five different workflows to make sure their page looks the way they imagined it when it’s published.”

\n\n\n\n

In the meantime, which may be numbered in years, end-users will likely have these multiple interfaces to deal with — overlap while new features are being developed. This may simply be a necessary growing pain of an aging project, one that is trying to lead the pack of hungry competitors in the CMS space.

\n\n\n\n

“There’s a lot of interest in reducing the number of workflows, and I’m hopeful that we can consolidate down to just one beautiful, intuitive interface,” said Haden.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 21:16:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce Tests New Instagram Shopping Checkout Feature, Now in Closed Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106398\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2878:\"

Instagram’s checkout feature, which allows users to purchase products without leaving the app, has become an even more important part of Facebook’s long-term investment in e-commerce now that the pandemic has so heavily skewed consumer behavior towards online shopping. When Instagram introduced checkout in 2019, it reported that 130 million users were tapping to reveal product tags in shopping posts every month.

\n\n\n\nimage credit: Instagram\n\n\n\n

Business owners who operate an existing store can extend their audience to Instagram by funneling orders from the social network into their own stores, without shoppers having to leave Instagram. Checkout supports integration with several e-commerce platform partners, including Shopify and BigCommerce, and will soon be available for WooCommerce merchants.

\n\n\n\n

WooCommerce is testing a new Instagram Shopping Checkout feature for its Facebook for WooCommerce plugin. The free extension is used on more than 900,000 websites and will provide the bridge for store owners who want to tap into Instagram’s market. The checkout capabilities are currently in closed beta. Anyone interested to test the feature can sign up for consideration. Businesses registered in the USA that meet certain other requirements may be selected to participate, and the beta is also expanding to other regions soon.

\n\n\n\n

WooCommerce currently supports shoppable posts, which are essentially products sourced from a product catalog created on Facebook that are then linked to the live store through an Instagram business account. Instagram’s checkout takes it one step further to provide a native checkout experience inside the app. Merchants pay no selling fees until December 31, 2020. After that time, the fee is 5% per shipment or a flat fee of $0.40 for shipments of $8.00 or less. 

\n\n\n\n

On the customer side, shoppers only have to enter their information once and thereafter it is stored for future Instagram purchases. Instagram also pushes shipment and delivery notifications inside the app. Store owners will need to weigh whether the convenience of the in-app checkout experience is worth forking over 5% to Facebook, or if they prefer funneling users over to the live store instead.

\n\n\n\n

Instagram Shopping Checkout is coming to WooCommerce in the near future but the company has not yet announced a launch date, as the feature is just now entering closed beta.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 04:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Past Twenty* WordPress Themes To Get New Block Patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106396\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/past-twenty-wordpress-themes-to-get-new-block-patterns?utm_source=rss&utm_medium=rss&utm_campaign=past-twenty-wordpress-themes-to-get-new-block-patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6608:\"

Mel Choyce-Dwan, the Default Theme Design Lead for WordPress 5.6, kick-started 10 tickets around two months ago that would bring new features to the old default WordPress themes. The proposal is to add unique block patterns, a feature added to WordPress 5.5, to all of the previous 10 Twenty* themes. It is a lofty goal that could breathe some new life into old work from the previous decade.

\n\n\n\n

Currently, only the last four themes are marked for an update by the time WordPress 5.6 lands. Previous themes are on the list to receive their block patterns in a future release. For developers and designers interested in getting involved, the following is a list of the Trac tickets for each theme:

\n\n\n\n\n\n\n\n

If you are wondering where Twenty Eighteen is in that list, that theme does not actually exist. It is the one missing year the WordPress community has had since the one-default-theme-per-year era began with Twenty Ten. It is easy to forget that we did not get a new theme for the 2017-2018 season. With all that has happened in the world this year, we should count ourselves fortunate to see a new default theme land for WordPress this December. WordPress updates and its upcoming default theme are at least one consistency that we have had in an otherwise chaotic time.

\n\n\n\n

More than anything, it is nice to see some work going toward older themes — not just in terms of bug fixes but feature updates. The older defaults are still a part of the face of WordPress. Twenty Twenty and Twenty Seventeen each have over one million active installs. Twenty Nineteen has over half a million. The other default themes also have significant user bases in the hundreds of thousands — still some of the most-used themes in the directory. We owe it to those themes’ users to keep them fresh, at least as long as they maintain such levels of popularity.

\n\n\n\n

This is where the massive theme development community could pitch in. Do some testing of the existing patches. Write some code for missing patterns or introduce new ideas. This is the sort of low-hanging fruit that almost anyone could take some time to help with.

\n\n\n\n

First Look at the New Patterns

\n\n\n\n

None of the proposed patterns have landed in trunk, the development version of WordPress, yet. However, several people have created mockups or added patches that could be committed soon.

\n\n\n\n

One of my favorite patterns to emerge thus far is from Beatriz Fialho for the Twenty Nineteen theme. Fialho has created many of the pattern designs proposed thus far, but this one, in particular, stands out the most. It is a simple two-column, two-row pattern with a circular image, heading, and paragraph for each section. Its simplicity fits in well with the more elegant, business-friendly look of the Twenty Nineteen theme.

\n\n\n\nServices pattern for Twenty Nineteen.\n\n\n\n

It is also fitting that Twenty Nineteen get a nice refresh with new patterns because it was the default theme to launch with the block editor. Ideally, it would continually be updated to showcase block-related features.

\n\n\n\n

While many people will focus on some of the more recent default themes, perhaps the most interesting one is a bit more dated. Twenty Thirteen was meant to showcase the new post formats feature in WordPress 3.6. According to Joen Asmussen, the theme’s primary designer, the original idea was for users to compose a ribbon of alternating colors as each post varied its colors.

\n\n\n\n

“The alternating ribbon of colors did not really come to pass because post formats were simply not used enough to create an interesting ribbon,” he wrote in the Twenty Thirteen ticket. “However, perhaps for block patterns, we have an opportunity to revisit those alternating ribbons of colors. In other words, I’d love to see those warm bold colors used in big swathes that take up the whole pattern background.”

\n\n\n\n
Patterns designed to match post formats.\n\n\n\n

This could be a fun update for end-users who are still using that feature that shall not be named post formats.

\n\n\n\n

There is a lot to like about many of the pattern mockups so far. I look forward to seeing what lands along with WordPress 5.6 and in future updates.

\n\n\n\n

Establishing Pattern Category Standard

\n\n\n\n

With the more recent Twenty Twenty-One theme’s block patterns and the new patterns being added to some of the older default themes, it looks like a specific pattern category naming scheme is starting to become a standard. Of the patches thus far, each is creating a new pattern category named after the theme itself.

\n\n\n\n

This makes sense. Allowing users to find all of their theme’s patterns in one location means that they can differentiate between them and those from core or other plugins. Third-party theme authors should follow suit and stick with this convention for the same reason.

\n\n\n\n

Developers can also define multiple categories for a single pattern. This allows theme authors to create a category that houses all of their patterns in one location. However, they can also split them into more appropriate context-specific categories for discoverability.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 19 Oct 2020 21:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"BuddyPress: BuddyPress 7.0.0-beta1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://buddypress.org/?p=315150\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://buddypress.org/2020/10/buddypress-7-0-0-beta1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4332:\"

BuddyPress 7.0.0-beta1 is now available for testing!

\n\n\n\n

Please note the plugin is still in development, so we recommend running this beta release on a testing site.

\n\n\n\n

You can test BuddyPress 7.0.0-beta1 in 4 ways :

\n\n\n\n\n\n\n\n

The 7.0.0 stable release is slated to the beginning of December, and we’d love you to give us a hand to get there!

\n\n\n\n

Please note BuddyPress 7.0.0 will require at least WordPress 4.9.

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute. Here are some of the big changes and features to pay close attention to while testing (Check out this report on Trac for the full list).

\n\n\n\n
\n\n\n\n

New Administration screens to manage BuddyPress types

\n\n\n\n

In BuddyPress 7.0.0 site administrators will be able to add, edit or delete Member & Group types using their WordPress Administration Screens just like they would do for Post tags.

\n\n\n\n

Read this development note to learn more about it.

\n\n\n\n
\n\n\n\n

Let’s welcome 3 new BP Blocks into our Block Editor

\n\n\n\n
  • The Activity Embed block let authors embed an activity into their post or page.
  • Use the BP Members block to select community users you want to feature into a post or a page.
  • Enjoy the BP Groups block to pick the groups you want to highlight into a post or a page.
\n\n\n\n

Get to know these new blocks reading this development note.

\n\n\n\n
\n\n\n\n

Improved support for WP CLI

\n\n\n\n

WP-CLI is the command-line interface for WordPress. You can update plugins, configure multisite installs, and much more, without using a web browser. In 7.0.0, you will be able to Enjoy new BuddyPress CLI commands to manage BuddyPress Group Meta, BuddyPress Activity Meta, activate or deactivate the BuddyPress signup feature and create BuddyPress specific testing code for plugins.

\n\n\n\n

Discover more about it from this development note.

\n\n\n\n
\n\n\n\n

And so much more such as improvements to the BP REST API, our Template pack, images and iframes lazy loading support…

\n\n\n\n
\n\n\n\n

How You Can Help

\n\n\n\n

Do you speak a language other than English? Help us translate BuddyPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, file one on BuddyPress Trac.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 22:30:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:12:\"Mathieu Viet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:89:\"WPTavern: Using the Web Stories for WordPress Plugin? You Better Play By Google’s Rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105848\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:215:\"https://wptavern.com/using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules?utm_source=rss&utm_medium=rss&utm_campaign=using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4080:\"Web Stories dashboard screen in WordPress.\n\n\n\n

What comes as a surprise to few, Google has updated its content guidelines for its Web Stories format. For users of its recently-released Web Stories for WordPress plugin, they will want to follow the extended rules for their Stories to appear in the “richer experiences” across Google’s services. This includes the grid view on Search, Google Images, and Google Discover’s carousel.

\n\n\n\n

Google released its Web Stories plugin in late September to the WordPress community. It is a drag-and-drop editor that allows end-users to create custom Stories from a custom screen in their WordPress admin.

\n\n\n\n
Visual Stories on Search.
\n\n\n\n

The plugin does not directly link to Google’s content guidelines anywhere. For users who do not do a little digging, they may be caught unaware if their stories are not surfaced in various Google services.

\n\n\n\n

On top of the Discover and Webmaster guidelines, Web Stories have six additional restrictions related to the following:

\n\n\n\n
  • Copyrighted content
  • Text-heavy Web Stories
  • Low-quality assets
  • Lack of narrative
  • Incomplete stories
  • Overly commercial
\n\n\n\n

While not using copyrighted content is one of those reasonably-obvious guidelines, the others could trip up some users. Because Stories are meant to represent bite-sized bits of information on each page, they may become ineligible if most pages have more than 180 words of text. Videos should also be limited to fewer than 60 seconds on each page.

\n\n\n\n

Low-quality media could be a flag for Stories too. Google’s guidelines point toward “stretched out or pixelated” media that negatively impacts the reader’s experience. They do not offer any specific resolution guidelines, but this should mostly be a non-issue today. The opposite issue is far more likely — users uploading media that is too large and not optimized for viewing on the web.

\n\n\n\n

The “lack of narrative” guideline is perhaps the vaguest, and it is unclear how Google will monitor or police narrative. However, the Stories format is about storytelling.

\n\n\n\n

“Stories are the key here imo,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Google specifically states that Stories need a “binding theme or narrative structure” from one page to the next. Essentially, the company is telling users to use the format for the purpose it was created for. They also do not want users to create incomplete stories where readers must click a link to finish the Story or get information.

\n\n\n\nCNN’s Web Story on Remembering John Lennon.\n\n\n\n

Overly commercial Stories are frowned upon too. While Google will allow affiliate marketing links, they should be restricted to a minor part of the experience.

\n\n\n\n

Closing his Twitter thread, Marsland seemed to hit the point. “I’ve seen some initial Google Web Stories where the platform is being used as a replacement for a brochure or website,” he wrote. “In my view that’s a huge missed opportunity. If I was advising brands I would say ‘Tell Stories’ this is a platform for Story Telling.”

\n\n\n\n

If users of the plugin follow this advice, their Stories should surface on Google’s rich search experiences.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 20:51:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:45:\"WPTavern: Stripe Acquires Paystack for $200M+\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106269\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:131:\"https://wptavern.com/stripe-acquires-paystack-for-200m?utm_source=rss&utm_medium=rss&utm_campaign=stripe-acquires-paystack-for-200m\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3196:\"

The big news in the world of e-commerce today is Stripe’s acquisition of Paystack, a Nigeria-based payments system that is widely used throughout African markets. The company, which became informally known as “the Stripe of Africa” picked up $8 million in Series A funding in 2018, led by Stripe, Y Combinator, and Tencent. Paystack has grown to power more than 60,000 businesses, including FedEx, UPS, MTN, the Lagos Internal Revenue Service, and AXA Mansar.

\n\n\n\n

Stripe’s acquisition of the company is rumored to be more than $200M, a small price to pay for a foothold in emerging African markets. In the company’s announcement, Stripe noted that African online commerce is growing 21% year-over-year, 75% faster than the global average. Paystack dominates among payment systems, accounting for more than half of all online transactions in Nigeria.

\n\n\n\n

“In just five years, Paystack has done what many companies could not achieve in decades,” Stripe EMEA business lead Matt Henderson said. “Their tech-first approach, values, and ambition greatly align with our own. This acquisition will give Paystack resources to develop new products, support more businesses and consolidate the hyper-fragmented African payments market.”

\n\n\n\n

Long term, Stripe plans to embed Paystack’s capabilities in its Global Payments and Treasury Network (GPTN), the company’s programmable infrastructure for global money movement.

\n\n\n\n

“Paystack merchants and partners can look forward to more payment channels, more tools, accelerated geographic expansion, and deeper integrations with global platforms,” Paystack CEO and co-founder Shola Akinlade said. He also assured customers that there’s no need to make any changes to their technical integrations, as Paystack will continue expanding and operating independently in Africa.

\n\n\n\n

Paystack is used as a payment gateway for thousands of WordPress-powered stores through plugins for WooCommerce, Easy Digital Downloads, Paid Membership Pro, Give, Contact Form 7, and an assortment of booking plugins. The company has an official WordPress plugin, Payment Forms for Paystack, which is active on more than 6,000 sites, but most users come through the Paystack WooCommerce Payment Gateway (20,000+ active installations).

\n\n\n\n

Stripe’s acquisition was a bit of positive news during what is currently a turbulent time in Nigeria, as citizens are actively engaged in peaceful protests to end police brutality. Paystack’s journey is an encouraging example of the flourishing Nigerian tech ecosystem and the possibilities available for smaller e-commerce companies that are solving problems and removing barriers for businesses in emerging markets.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 22:26:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:10;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WPTavern: Diving Into the Book Review Block Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106273\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:145:\"https://wptavern.com/diving-into-the-book-review-block-plugin?utm_source=rss&utm_medium=rss&utm_campaign=diving-into-the-book-review-block-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6791:\"

Created by Donna Peplinskie, a Product Wrangler at Automattic, the Book Review Block plugin is nearly three years old. However, it only came to my attention during a recent excursion to find interesting block plugins.

\n\n\n\n

The plugin does pretty much what it says on the cover. It is designed to review books. It generally has all the fields users might need to add to their reviews, such as a title, author, image, rating, and more. The interesting thing is that it can automatically fill in those details with a simple ISBN value. Plus, it supports Schema markup, which may help with SEO.

\n\n\n\n

Rain or shine, sick or well, I read every day. I am currently a month and a half shy of a two-year reading streak. When the mood strikes, I even venture to write a book review. As much as I want to share interesting WordPress projects with the community, I sometimes have personal motives for testing and writing about plugins like Book Review Block. Anything that might help me or other avid readers share our thoughts on the world of literature with others is of interest.

\n\n\n\n

Admittedly, I was excited as I plugged in the ISBN for Rhthym of War, the upcoming fourth book of my favorite fantasy series of all time, The Stormlight Archive. I merely needed to click the “Get Book Details” button.

\n\n\n\n

Success! The plugin worked its magic and pulled in the necessary information. It had my favorite author’s name, the publisher, the upcoming release date, and the page count. It even had a long description, which I could trim down in the editor.

\n\n\n\nDefault output of the Book Review block.\n\n\n\n

There was a little work to make this happen before the success. To automatically pull in the book details, end-users must have an API Key from Google. It took me around a minute to set that up and enter it into the field available in the block options sidebar. The great thing about the plugin is that it saves this key so that users do not have to enter each time they want to review a book.

\n\n\n\n

Book Review Block a good starting point. It is straightforward and simple to use. It is not yet at a point where I would call it a great plugin. However, it could be.

\n\n\n\n

Falling Short

\n\n\n\n

The plugin’s Book Review block should be taking its cues from the core Media & Text block. When you get right down to it, the two are essentially doing the same thing visually. Both are blocks with an image and some content sitting next to each other.

\n\n\n\n

The following is a list of items where it should be following core’s lead:

\n\n\n\n
  • No way to edit alt text (book title is automatically used).
  • The image is always aligned left and the content to the right with no way to flip them.
  • The media and content are not stackable on mobile views.
  • Cannot adjust the size of the image or content columns.
  • While inline rich-text controls are supported, users cannot add Heading, List, or Paragraph blocks to the content area and use their associated block options.
\n\n\n\n

That is the shortlist that could offer some quick improvements to the user experience. Ultimately, the problems with the plugin essentially come down to not offering a way to customize the output.

\n\n\n\n

One of the other consistent problems is that the book image the plugin loads is always a bit small. This seems to be more of an issue from the Google Books API than the plugin. Each time I tested a book, I opted to add a larger image — the plugin does allow you to replace the default.

\n\n\n\n

The color settings are limited. The block only offers a background color option with no way to adjust the text color. A better option for plugin users is to wrap it in a Group block and adjust the background and text colors there.

\n\n\n\nBook Review block wrapped inside a Group block.\n\n\n\n

It would also be nice to have wide and full-alignment options, which is an often-overlooked featured from many block plugin authors.

\n\n\n\n

Using the Media & Text Block to Recreate the Book Review Block

\n\n\n\n

The Book Review Block plugin has a lot of potential, and I want to see it evolve by providing more flexibility to end-users. Because the Media & Text block is the closest core block to what the plugin offers, I decided to recreate a more visually-appealing design with it.

\n\n\n\nBook review section created with the Media & Text block.\n\n\n\n

I made some adjustments on the content side of things. I used the Heading block for the book title, a List block for the book metadata, and a Paragraph block for the description.

\n\n\n\n

The Media & Text block also provided me the freedom to adjust the alignment, stack the image and content on mobile views, and tinker with the size of the image. Plus, it has that all-important field for customizing the image alt attribute.

\n\n\n\n

The Media & Text block gave me much more design mileage.

\n\n\n\n

However, there are limitations to the core block. It does not fully capture some of the features available via the Book Review block. The most obvious are the automatic book details via an ISBN and the Schema markup. Less obvious, there is no easy way to recreate the star rating — I used emoji stars — and long description text does not wrap under the image. To recreate that, you would have to opt to use a left-aligned image followed by content.

\n\n\n\n

Overall, the Media & Text block gives me the ability to better style the output, which is what I am more interested in as a user. I want to put my unique spin on things. That is where the Book Review Plugin misfires. It is also the sort of thing that the plugin author can iterate on, offering more flexibility in the future.

\n\n\n\n

This is where many block plugins go wrong, particularly when there is more than one or two bits of data users should enter. Blocks represent freedom in many ways. However, when plugin developers stick to a rigid structure, users can sometimes lose that sense of freedom that they would otherwise have with building their pages.

\n\n\n\n

One of the best blocks, hands down, that preserves that freedom is from the Recipe Block plugin. It has structured inputs and fields. However, it allows freeform content for end-users to make it their own.

\n\n\n\n

When block authors push beyond this rigidness, users win.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 20:44:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:11;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce 4.6 Makes New Home Screen the Default for New and Existing Stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3018:\"

WooCommerce 4.6 was released today. The minor release dropped during WooSesh, a global, virtual conference dedicated to WooCommerce and e-commerce topics. It features the new home screen as the default for all stores. Previously, the screen was only the default on new stores. Existing store owners had to turn the feature on in the settings.

\n\n\n\n
\n\n\n\n

The updated home screen, originally introduced in version 4.3, helps store admins see activity across the site at a glance and includes an inbox, quick access to store management links, and an overview of stats on sales, orders, and visitors. This redesigned virtual command center arrives not a moment too soon, as anything that makes order management more efficient is a welcome improvement, due to the sheer volume of sales increases that store owners have seen over the past eight months.

\n\n\n\n

In stark contrast to industries like hospitality and entertainment that have proven to be more vulnerable during the pandemic, e-commerce has seen explosive growth. During the State of the Woo address at WooSesh 2020, the WooCommerce team shared that e-commerce is currently estimated to be a $4 trillion market that will grow to $4.5 trillion by 2021. WooCommerce accounts for a sizable chunk of that market with an estimated total payment volume for 2020 projected to reach $20.6 billion, a 74% increase compared to 2019.

\n\n\n\n

The WooCommerce community is on the forefront of that growth and is deeply invested in the products that are driving stores’ success. The WooCommerce team shared that 75% of people who build extensions also build and maintain stores for merchants, and 70% of those who build stores for merchants also build and maintain extensions or plugins. In 2021, they plan to invest heavily in unlocking more features in more countries and will make WooCommerce Payments the native payment method for the global platform.

\n\n\n\n

A new report from eMarketer shows that US e-commerce growth has jumped 32.4%, accelerating the online shopping shift by nearly two years. Experts also predict the top 10 e-commerce players will swallow up more of US retail spending to account for 63.2% of all online sales this year, up from 57.9% in 2019.

\n\n\n\n

The increase in e-commerce spending may not be entirely tied to the pandemic, as some experts believe this historic time will mark permanent changes in consumer spending habits. This is where independent stores, powered by WooCommerce and other technologies, have the opportunity to establish a strong reputation for themselves by providing quality products and reliable service, as well as by being more nimble in the face of pandemic-driven increases in volume.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 03:48:32 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:12;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:101:\"WPTavern: The Future of Starter Content: WordPress Themes Need a Modern Onboarding and Importing Tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106177\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool?utm_source=rss&utm_medium=rss&utm_campaign=the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7385:\"Image credit: picjumbo.com on Pexels.\n\n\n\n

Starter content. It was a grand idea, one of those big dreams of WordPress. It was the new kid on the block in late 2016. Like the introduction of post formats in 2011, the developer community was all in for at least that particular release version. Then, it was on to the next new thing, with the feature dropping off the radar for all but the most ardent evangelists.

\n\n\n\n

Some of us were burned over the years, living and dying by the progress of features that we wanted most.

\n\n\n\n

Released in WordPress 4.7, starter content has since seemed to be going the way of post formats. After four years, only 141 themes in the WordPress theme directory support the feature. There has been no movement to push it beyond its initial implementation. And, it never really covered the things that theme authors wanted in the first place. It was a start. But, themers were ultimately left to their own devices, rolling custom solutions for something that never panned out — fully-featured demo and imported content. Four years is an eternity in the web development world, and there is no sense in waiting around to see if WordPress would push forward.

\n\n\n\n

Until Helen Hou-Sandí published Revisiting Starter Content last week, most would have likely assumed the feature would be relegated to legacy code used by old-school fans of the feature and those theme authors who consider themselves completionists.

\n\n\n\n

“Starter content in 4.7 was always meant to be a step one, not the end goal or even the resting point it’s become,” wrote Hou-Sandí. “There are still two major things that need to be done: themes should have a unified way of showing users how best to put that theme to use in both the individual site and broader preview contexts, and sites with existing content should also be able to take advantage of these sort of ‘ideal content’ definitions.”

\n\n\n\n

Step two should have been this four-year-old accompanying ticket to allow users to import starter content into existing, non-fresh sites.

\n\n\n\n

Since the initial feature dropped, the theme landscape has changed. Let’s face it. WordPress might simply not be able to compete with theme companies that are pushing the limits, creating experiences that users want at much swifter speeds.

\n\n\n\n

Look at where the Brainstorm Force’s Starter Templates plugin for its Astra theme is now. Users can click a button and import a full suite of content-filled pages or even individual templates. And, the Astra theme is not alone in this. It has become an increasingly-common standard to offer some sort of onboarding to users. GoDaddy’s managed WordPress service fills a similar need on the hosting end.

\n\n\n\nAstra’s starter templates and content.\n\n\n\n

As WordPress use becomes more widespread, the more it needs a way to onboard users.

\n\n\n\n

This essentially boils down to the question: how can I make it look like the demo?

\n\n\n\n

Ah, the age-old question that theme authors have been trying to solve. Whether it has been limitations in the software or, perhaps, antiquated theme review guidelines related to demo and imported content, this has been a hurdle that has been tough to jump. But, some have sailed over it and moved on. While WordPress has seemingly been twiddling its thumbs for years, Brainstorm Force and other theme companies have solved this and continued to innovate.

\n\n\n\n

This is not necessarily a bad thing. There are plenty of ideas to steal copy and pull into the core platform.

\n\n\n\n

One of the other problems facing the WordPress starter content feature is that it is tied to the customizer. With the direction of the block system, it is easy to ask what the future holds. The customizer — originally named the theme customizer — was essentially a project to allow users to make front-end adjustments and watch those customizations happen in real time. However, new features like global styles and full-site editing are happening on their own admin screens. Most theme options will ultimately be relegated to global styles, custom templates, block styles, and block patterns. There may not be much left for the customizer to do.

\n\n\n\n

Right now, there are too many places in WordPress to edit the front-end bits of a WordPress site. My hope is that all of these things are ultimately merged into one less-confusing interface. But, I digress…

\n\n\n\n

Starter content should be rethought. Whoever takes the reins on this needs a fresh take that adopts modern methods from leading theme companies.

\n\n\n\n

The ultimate goal should be to allow theme authors to create multiple sets of templates/content that end-users can preview and import. It should not be tied to whether it is a new site. Any site owner should be able to import content and have it automagically go live. It should also be extendable to allow themes to support page builders like Elementor, Beaver Builder, and many others.

\n\n\n\n

This seems to be in line with Hou-Sandí’s thoughts. “For a future release, we should start exploring what it might look like to opt into importing starter content into existing sites, whether wholesale or piecewise,” she wrote. “Many of us who work in the WordPress development/consulting space tend not to ever deal in switching between public themes on our sites, but let’s not forget that’s a significant portion of our user audience and we want to continue to enable them to not just publish but also publish in a way that matches their vision.”

\n\n\n\n

Let’s do it right this go-round, keep a broad vision, and provide an avenue for theme authors to adopt a standardized core WordPress method instead of having everyone build in-house solutions.

\n\n\n\n

I haven’t even touched on the recent call to use starter content for WordPress.org theme previews. It will take more than ideas to excite many theme authors about the possibility. That ticket has sat for seven years with no progress, and most have had it on their wish list for much longer. It is an interesting proposal, one that has been tossed around in various team meetings for years.

\n\n\n\n

Like so many other things, theme authors have either given up hope or moved onto doing their own thing. They need to be brought into the fold, not only as third parties who are building with core WordPress tools but as developers who are contributing to those features.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 14 Oct 2020 20:07:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:13;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:116:\"WPTavern: Google Podcasts Manager Adds More Data from Search: Impressions, Top-Discovered Episodes, and Search Terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:271:\"https://wptavern.com/google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms?utm_source=rss&utm_medium=rss&utm_campaign=google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2568:\"

Google announced an expansion of listener engagement metrics today for those using its Podcast Manager. Previously, audience insights included data about the types of devices listeners are using, where listeners tune in and drop off during a given episode, total number of listens, and listening duration, but the service lacked analytics regarding how visitors were discovering the podcast.

\n\n\n\n

Google is remedying that today by expanding the dashboard to show impressions, clicks, top-discovered episodes, and search terms that brought listeners to the podcast. This information can help podcasters understand how their content is getting discovered so they can better tailor their episodes to attract more new listeners.

\n\n\n\n

The podcasting industry has seen remarkable growth over the past five years, which previously led experts to project that marketers will spend over $1 billion in advertising by 2021. After the pandemic hit, podcast listening took a downturn in the U.S. but at the same time, podcast creators have found more time to create new shows and episodes. Businesses are turning to the medium to supplement traditional marketing methods that no longer have the same impact now that consumer spending habits heavily favor online products.

\n\n\n\n

Along with the new metrics available inside Google Podcasts Manager, the company also published a guide to optimizing podcasts for Google Search. It highlights four important items for making sure a podcast can be found:

\n\n\n\n
  • Detailed show and episode metadata
  • Ensure the podcast’s webpage and RSS data match
  • Include cover art
  • Ensure Googlebot can access your audio files
\n\n\n\n

A detailed breakdown of your audience’s listening habits isn’t worth much if you’re having trouble getting your podcast discovered. Any podcasting plugin for WordPress should handle these basic optimization recommendations, but if you are still having trouble being found via Google, you can dig deeper into the podcast setup guide for more detailed recommendations.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 22:57:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:14;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Are Block-Based Widgets Ready To Land in WordPress 5.6?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/are-block-based-widgets-ready-to-land-in-wordpress-5-6?utm_source=rss&utm_medium=rss&utm_campaign=are-block-based-widgets-ready-to-land-in-wordpress-5-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8214:\"

Two weeks ago, the Gutenberg team put out an open call for block-based widgets feedback. I had already written a lengthy review of the new system earlier in September but was asked by a member of the team to share my thoughts on the most recent iteration. With the upcoming freeze for WordPress 5.6 Beta 1 just a week away, I figured it would not hurt to do another deep dive.

\n\n\n\n

For reference, my latest testing is against version 9.2.0-alpha-172f589 of the Gutenberg plugin, which was a build from earlier today. Gutenberg development moves fast, but everything should be accurate to that point.

\n\n\n\n

Ultimately, many of the problems I pointed out over a month ago still exist. However, the team has cleaned most of the minor issues, such as pointing the open/close arrows for sidebars (block areas) in the correct direction and making it more consistent with the post-editing screen. The UI is much more polished.

\n\n\n\n

Before I dive into all the problems, I want to answer the question I am proposing. Yes, the block-based widget system will be ready for prime time when WordPress 5.6 lands. It is not there yet, but it is at a point where there is a clear finish line that is reachable in the next two months.

\n\n\n\n

I will ignore the failure of block-based widgets in the customizer, which landed in Gutenberg 8.9 and was removed in 9.1. I will also look past the recent proposal to reconstruct the widgets screen to use the Customize API, at least for now. There is a boatload of problems that block-based widgets present for the customizer, and those problems are insurmountable for WordPress 5.6. Long term, WordPress needs to have a single place for editing widget/block areas. Users will likely have to live with some inconsistencies for a while.

\n\n\n\n

Assuming the team does not try to throw a last-minute Hail Mary and implement full editing of blocks in the customizer this round, it is safe to say that block-based widgets are well on their way toward a successful WordPress 5.6 debut.

\n\n\n\n

The User Experience

\n\n\n\nBlock-based widgets screen.\n\n\n\n

As a user, I genuinely enjoy using the new Widgets admin screen. The open-ended, free-form block areas create untold possibilities for designing my WordPress sites. Traditional widgets were limited in scope. Users were buckled down to a handful of core widgets, possibly some plugin widgets, and whatever their theme author offered up. However, with blocks, the pool of choices expands to at least triple the out-of-the-box options (I am not counting embed-type blocks individually). Plus, blocks provide a far more extensive set of design options than a traditional widget.

\n\n\n\n

In comparison, traditional widgets are outdated. Blocks are superior in almost every way. However, there are still problems with this new system.

\n\n\n\n

The biggest issue right now is that end-users can exit the Widgets screen without saving their changes. There is no warning to let them know that all their work is about to be lost in the ether. This is one of those OMGBBQ-level items that need to happen before WordPress 5.6 drops.

\n\n\n\n

One nice-to-have-but-not-necessary feature would be the ability to drag blocks from one block area to another. In the old widgets system, users could move widgets from sidebar to sidebar. The current alternative is to copy a widget, paste it in a new block area, and remove the original.

\n\n\n\n

I am also not a fan of not having an option for the top toolbar, which is available on the post-editing screen. One of the reasons for using this toolbar is because I dislike the default popup toolbar on individual blocks. It is distracting and often gets in the way of my work.

\n\n\n\n

Legacy widgets seem to still be a work in progress. The Legacy Widget block did not work at all for me at times. Then, it magically began to work. However, Gutenberg does now automatically add registered third-party widgets to the block inserter just as if they were blocks.

\n\n\n\nGetting a plugin’s widget to work.\n\n\n\n

This presented its own problems. The only way I managed to make third-party plugin widgets work was to insert the widget, save, and refresh the widgets screen. At that point, the widgets appeared and became editable.

\n\n\n\n

The Theme Author Experience

\n\n\n\n

One of my biggest concerns for theme authors right now is that there does not seem to be any documentation in the block editor handbook. There is plenty of time to make that happen, but there are things theme authors need to be aware of. Having a centralized location, even while the feature is under development, would help them gear up for the 5.6 release.

\n\n\n\n

Some of these questions, which may be answered in various Make blog posts, should exist on a dedicated documentation page:

\n\n\n\n
  • How can a theme opt out of block-based widgets?
  • What are the hooks to add custom styles for the Widgets screen?
  • Can themes target specific sidebar styles on the Widgets screen?
  • Is it possible to consistently style sections like traditional widgets on the front end?
  • Can themes opt into wide and full-alignment within block areas, which could essentially be used similarly to the post content area?
\n\n\n\n

These are some of the questions I would want to be answered as a former theme author. I am no longer in the thick of the theme design game and presume that those who are would have a larger list of questions.

\n\n\n\n

One less-obvious piece of documentation should center on how to handle fallbacks or default widgets. Traditionally, themes that needed to show a default set of widgets would check if the sidebar has widgets and fall back to using the_widget() to output one or more defaults. While theme authors can still do that, we should start to transition them across the board to the block system.

\n\n\n\n

Should theme authors copy/paste block HTML as a fallback? Would the starter content system be better for this, and can starter widget content handle blocks? What is the recommended method for widget fallbacks in WordPress 5.6?

\n\n\n\n

There is still the ongoing issue of how theme authors should handle the traditional widget and widget title wrapper HTML in the new block paradigm. One patch added since the Gutenberg 9.1 release wraps every top-level block with the widget wrapper. If this lands in the 9.2 release, it will likely make the issue worse.

\n\n\n\n

In the traditional system, both the widget title and content are wrapped within a container together. However, if a user adds a Heading block (widget title) and another block (widget content), each block is wrapped separately with the theme’s widget wrappers. The only way to rectify the situation as it stands is for end-users to add a Group block for each “widget” they want, which would require an extensive amount of re-education for WordPress users. It is not an ideal scenario.

\n\n\n\nEach block is wrapped as an individual section.\n\n\n\n

Instead of attempting to directly “fix” this issue, WordPress should instead do nothing to the output. Blocks and traditional widgets are fundamentally different.

\n\n\n\n

Let theme authors take the reins on this one and explore possibilities. However, give them the tools to do so, such as supporting block patterns.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 21:35:39 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:15;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: WordCamp Austin 2020 Finds Success with VR Experience for Sessions and Networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106119\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking?utm_source=rss&utm_medium=rss&utm_campaign=wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7246:\"

WordCamp Austin 2020 attendees are raving about their experiences attending the virtual event last Friday. It was no secret that the camp’s organizers planned to use Hubs Virtual Rooms by Mozilla to create a unique environment, but few could imagine how much more interactive and personalized the experience would be than a purely Zoom-based WordCamp.

\n\n\n\n

After selecting a custom avatar, attendees entered the venue using a VR headset or the browser to check out sessions or network in the hallway track.

\n\n\n\n
\n

Here’s a small taste of the experience at @WordCampATX today. #WordPress logos and no sponsor banners on any elevator doors. #WCATX pic.twitter.com/Nv2p2VchXf

— David Bisset (@dimensionmedia) October 9, 2020
\n
\n\n\n\n

Speaker and Q&A sessions were broadcast through Zoom but organizers can also embed YouTube videos and streams within the standalone VR environment.

\n\n\n\n

“The VR experience was the most life-like WordCamp experience I’ve had since the start of global lockdowns,” attendee and speaker David Vogelpohl said. “You could attend sessions in one of two virtual presentation halls depending on what track you wanted to see at that time. The speaker presented on a virtual stage and you could see the other attendees watching the presentation.”

\n\n\n\n

Vogelpohl said he enjoyed his experience getting to know others in the Slack and VR venue. Organizers preserved the general vibe of the “hallway track” to recreate what is arguably one of the most valuable aspects of in-person WordCamps.

\n\n\n\n
\n

So cool – checking out the Virtual Space of WordCamp Austin – love the background noise of people talking, ran into @ChrisWiegman and @Josh412 #WCATX pic.twitter.com/68EdgDN2Om

— Birgit Pauli-Haack (@bph) October 9, 2020
\n
\n\n\n\n

“In the hallway track between the virtual presentation halls was a large foyer where you could meet new people, spot a friend speaking with someone else, and virtually step aside from a group conversation to have a private conversation,” Vogelpohl said.

\n\n\n\n

“It was great to see folks like Josepha circling around speaking with attendees, Josh Pollock nerding out in a corner with a group of advanced WP developers, and having random friends drop into a conversation I was having with a group of others. While VR WordCamp doesn’t wholly replace the value of attending a WordCamp live, a lot of the best parts of meeting and collaborating with others was captured in the VR context.”

\n\n\n\n

The live music interludes, which showcased talents from around the community, also provided a way for virtual attendees to stay connected while waiting for the next session.

\n\n\n\n

Behind the Scenes with Anthony Burchell: Creative Director for WordCamp Austin’s Virtual World

\n\n\n\n

WordPress core contributor Anthony Burchell, who started a company dedicated to creating interactive XR sound and art experiences, was the creative director behind the WordCamp Austin’s VR backdrop.

\n\n\n\n

“For WordCamp Austin we wanted to give folks something to be excited about outside of the typical webcam and chat networking,” Burchell said. “I feel that virtual events are not utilizing the networking layer nearly enough to make folks feel like they are really at an event. I’ve seen many compelling formats for virtual events utilizing webcams and chat rooms, but in the end, it feels like there’s been a missing element of presence; something video games and virtual reality excel at.”

\n\n\n\n
\n

Virtual mission control for #WCATX pic.twitter.com/WyrFkIsW2Q

— Anthony Burchell (@antpb) October 9, 2020
\n
\n\n\n\n

Setting up the virtual world involves spinning up a self-hosted instance of Hubs Cloud, which Burchell said is very similar to the complexity of making a WordPress site.

\n\n\n\n

“The most time consuming part of creating a 3D world for an event is making the 3D assets for the space,” Burchell said. “In total I streamed 11 hours of video leading up to the event to give a glimpse into the process.”

\n\n\n\n

Burchell’s YouTube playlist documents the incredible amount of work that went into creating the WordCamp’s virtual venue for attendees to enjoy.

\n\n\n\n

“While it took quite a bit of time to prepare, the code and assets are completely reusable for another event,” Burchell said. “A lot of the time was spent trying to make the space purpose built for the goals of the camp. Much like a real WordCamp, I found the majority of folks packing into the theater rooms for presentations and dipping out a little early to network with friends in the hallway area. That was very much by design!”

\n\n\n\n

Burchell and the other organizers were careful to ensure that the Hubs space was not the primary viewing experience of the camp but rather an extension of the networking activities that attendees could drop in on. The event had nearly identical numbers of attendees joining the virtual space as it did for those joining the video channels. At the end of the afterparty, Burchell turned on flying for all attendees to conclude the successful event:

\n\n\n\n
\n\n
\n\n\n\n

“With Hubs we were able to give attendees the ability to express themselves within a venue vs within a camera and chat box,” Burchell said. “It was incredible to see characteristics of folks in the community shine through a virtual avatar! Just the simple act of seeing your WordCamp friends in the hallway joking and chatting just as they would at a real life event was enough to make me feel like I was transported to a real WordCamp.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 22:31:02 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:16;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Privacy-Conscious WordPress Plugin Caches and Serves Gravatar Images Locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105825\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally?utm_source=rss&utm_medium=rss&utm_campaign=privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5285:\"

Ari Stathopoulos released his new Local Gravatars plugin last week. The goal of the plugin is to allow site owners to take advantage of the benefits of a global avatar system while mitigating privacy concerns by hosting the images locally.

\n\n\n\n

In essence, it is a caching system that stores the images on the site owner’s server. It is an idea that Peter Shaw proposed in the comments on an earlier Tavern article covering local avatar upload. It is a middle ground that may satisfy some users’ issues with how avatars currently work in WordPress.

\n\n\n\n

“I am one of the people that blocks analytics, uses private sessions when visiting social sites, I use DuckDuckGo instead of Google, and I don’t like the ‘implied’ consents,” said Stathopoulos. “I built the plugin for my own use because I don’t know what Gravatar does, I don’t understand the privacy policies, and I am too lazy to spend two hours analyzing them. It’s faster for me to build something that is safe and doesn’t leave any room for misunderstandings.”

\n\n\n\n

He is referring to Automattic’s extensive Privacy Policy. He said it looks benign. However, he does not like the idea of any company being able to track what sites he visits without explicit consent.

\n\n\n\n

“And when I visit a site that uses Gravatar, some information is exposed to the site that serves them — including my IP,” said Stathopoulos. “Even if it’s just for analytics purposes, I don’t think the company should know that page A on site B got 1,000 visitors today with these IPs from these countries. There is absolutely no reason why any company not related to the page I’m actually visiting should have any kind of information about my visit.”

\n\n\n\n

The Local Gravatars plugin must still connect to the Gravatar service. However, the connection is made on the server rather than the client. Stathopoulos explained that the only information exposed in this case is the server’s IP and nothing from the client, which eliminates any potential privacy concerns.

\n\n\n\n

The Latest Plugin Update

\n\n\n\n

Stathopoulos updated the plugin earlier today to address some performance concerns for pages that have hundreds or more Gravatar images. In the version 1.0.1 update, he added a maximum processing time of five seconds and changed the cache cleanup process from daily to weekly. Both of these are filterable via code.

\n\n\n\n

“Now, if there are Gravatars missing in a page request, it will get as many as it can, and, after five seconds, it will stop,” said Stathopoulos. “So if there are 100 Gravatars missing and it gets the first 20, the rest will be blank (can be filtered to use a fallback URL, or even fall back to the remote URL, though that would defeat the privacy improvement). The next page request will get the next 20, and so on. At some point, all will be there, and there will be no more delays.”

\n\n\n\n

He did point out that performance could temporarily suffer when installing it on a site that has individual posts with 1,000s of comments and a lot of traffic. However, nothing would crash on the site, and the plugin should eventually lead to a performance boost in this scenario. For such large sites, owners could use the existing filter hooks to tweak the settings.

\n\n\n\n

Right now, the plugin is primarily an itch he wanted to scratch for his own purposes. However, if given enough usage and feedback, he may include a settings screen to allow users to control some of the currently-filterable defaults, such as the cleanup timeframe and the maximum process time allowed.

\n\n\n\n

The Growing List of Alternatives

\n\n\n\n

With growing concerns around privacy in the modern world, Local Gravatars is another tool that end-users can employ if they have any concerns around the Gravatar service. For those who are OK with an auto-generated avatar, Pixel Avatars may be a solution.

\n\n\n\n

“I’ve seen some of them, and they are wonderful!” Stathopoulos said of alternatives for serving avatars. “However, this plugin is slightly different in that the avatars the user already has on Gravatar.com are actually used. They can see the image they have uploaded. The user doesn’t need to upload a separate avatar, and an automatic one is not used by default.”

\n\n\n\n

He would not mind using an auto-generated avatar when commenting on blogs or news sites at times. However, Stathopoulos prefers Gravatar for community-oriented sites.

\n\n\n\n

“My Gravatar is part of my online identity, and when I see, for example, a comment from someone on WordPress.org, I know who they are by their Gravatar,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 21:06:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:17;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: WordPress 5.6 to Introduce Application Passwords for REST API Authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105997\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2604:\"

In 2015, WordPress 4.4 introduced a REST API, but one thing that has severely limited its broader use is the lack of authentication capabilities for third-party applications. After considering the benefits and drawbacks of many different types of authentication systems, George Stephanis published a proposal for integrating Application Passwords, into core.

\n\n\n\n

Stephanis highlighted a few of the major benefit that were important factors in the decision to use Application Passwords: the ease of making API requests, ease of revoking credentials, and the ease of requesting API credentials. The project is available as a standalone feature plugin, but Stephanis and his collaborators recommended WordPress merge a pull request that is based off the feature plugin’s codebase.

\n\n\n\n

After WordPress 5.6 core tech lead Helen Hou-Sandi gave the green light for Application Passwords to be merged into core, the developer community responded enthusiastically to the news.

\n\n\n\n

“I am/we are 100% in favor of this,” Joost deValk commented on the proposal. “Opening this up is like opening the dawn of a new era of WordPress based web applications. Suddenly authentication is not something you need to fix when working with the API and you can just build awesome stuff.”

\n\n\n\n

Stephanis’ proposal also mentioned how beneficial a REST API authentication system would be for the Mobile teams‘ contributors who are relying on awkward workarounds while integrating Gutenberg support.

\n\n\n\n

“This would be a first step to replace the use of XMLRPC in the mobile apps and it would allow us to add more features for self hosted users,” Automattic mobile engineer Maxime Biais said.

\n\n\n\n

After the REST API was added to WordPress five years ago, many had the expectation that WordPress-based web applications would start popping up everywhere. Without a reliable authentication system, it wasn’t easy for developers to just get inspired and build something quickly. Application Passwords in WordPress 5.6 will open up a lot of possibilities for those who were previously deterred by the lack of core methods for authenticating third-party access.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 23:01:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:18;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"WPTavern: WP Agency Summit Begins Its Second Annual Virtual Event October 12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105160\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:197:\"https://wptavern.com/wp-agency-summit-begins-its-second-annual-virtual-event-october-12?utm_source=rss&utm_medium=rss&utm_campaign=wp-agency-summit-begins-its-second-annual-virtual-event-october-12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6357:\"

Jan Koch, the founder and host of WP Agency Summit, is kicking off his second annual event on October 12. The five-day event will feature 37 speakers from a wide range of backgrounds across the WordPress industry. It is a free virtual event that anyone can attend.

\n\n\n\n

“The focus for the 2020 WP Agency Summit is showing attendees how to bring back the fun into scaling their agencies,” said Koch. “It is all about reducing the daily hustle by teaching how to successfully build and manage teams, how to work with enterprises (allowing for fewer customers but bigger projects), how to build sustainable recurring revenue, and how to position your agency to dominate your niche.”

\n\n\n\n

This year’s event includes three major changes to make the content more accessible to a larger group of people. Each session will be available between October 12 – 16 instead of the previous 48-hour window that attendees had to find time for in 2019.

\n\n\n\n

After the event has concluded, access to the content will be behind a paywall. Koch reduced the price to $77 for lifetime access for those who purchase pre-launch, which will increase to $127 during the event. Last year’s prices ballooned to $497, which meant that it was simply not affordable for many who found it too late.

\n\n\n\n

Some of the proceeds this year are going toward transcribing all the videos so that hearing-impaired users can enjoy the content.

\n\n\n\n

This year’s event will also focus on a virtual networking lounge for attendees. “I’ve seen how well it worked at the WP FeedBack Summit — we even had BobWP record a podcast episode on the fly in that lounge!” said Koch. “I’ve seen many new friendships develop, people connecting with new suppliers or getting themselves booked on podcasts, and sharing experiences about their businesses.”

\n\n\n\n

The lounge will be open during the entirety of the summit, which will allow attendees to jump into the conversation on their own time.

\n\n\n\n

A More Diverse Speaker Lineup

\n\n\n\n

Koch received some backlash for the lack of gender diversity last year. The 2019 event had over 20 speakers from a diverse male lineup. However, only four women from our industry led sessions.

\n\n\n\n

When asked about this issue in 2019, Koch responded, “I recognize this as a problem with my event. The reason I have so much more male than female speakers is quite simple, the current speaker line-up is purely based on connections I had when I started planning for the event. It was a relatively short amount of time for me, so I wasn’t able to build relationships with more female WP experts beforehand.”

\n\n\n\n

The host said he paid attention to the feedback he received. While not hitting the 50/50 split goal he had for 2020’s event, 16 of the 37 speakers are women.

\n\n\n\n

Koch said he strived to get speakers from a wider range of backgrounds. He wanted to bring in both freelancers and multi-million dollar agency owners. He also focused on getting people from multiple countries to represent WordPress agencies.

\n\n\n\n

“I did reach out to around 130 people four months before the event to make new connections,” he said. “The community around the Big Orange Heart (a non-profit for mental well-being) also helped a lot with introducing me to new members of the WP community.”

\n\n\n\n

Koch said he learned two valuable lessons when branching out beyond his existing connections for this year’s event:

\n\n\n\n

Firstly, don’t hesitate to reach out to people you think will never talk to you because they’re running such big companies. For example, I immediately got confirmations from Mario Peshev from Devrix, Brad Touesnard from Delicious Brains, or Marieke van de Rakt from Yoast. When first messaging them, I had little hope they’d set aside time to jump on an interview with me – but they were super supportive and accommodating! The WordPress community really is a welcoming environment if you approach people in a humble way.

Secondly, build connections with sincerity. Do not just focus on what you can get from that connection but how you can help the other person. I know this sounds cheesy and you’ve heard this quite often — but it is true. Once I got the first response from new contacts and explained my goal of connecting fellow WordPress community members virtually, most immediately agreed because they also benefit from new connections and being positioned as a thought-leader in this event.

\n\n\n\n

WP Agency Summit? WP FeedBack Summit?

\n\n\n\n

For readers who recall the Tavern’s coverage of the WP FeedBack Summit earlier this year, the article specifically stated that the WP FeedBack Summit was a continuation of 2019’s WP Agency Summit. The official word at the time from WP FeedBack’s public relations team was the following:

\n\n\n\n

Last year’s event, the WP Agency Summit has been rebranded under the umbrella of WP FeedBack’s brand when Jan Koch the host of last’s year WP Agency Summit joined WP FeedBack as CTO.

\n\n\n\n

Koch said that it was a standalone event and not directly connected to WP Agency Summit but had the same target audience. However, the WP FeedBack Summit did use the previous WP Agency Summit’s stats and data to promote the event.

\n\n\n\n

“The WP FeedBack Summit was hosted under the WP FeedBack brand because I joined their team as CTO in March this year,” he said. “Vito [Peleg] and I had the idea to host a virtual conference around WordPress because of WordCamp Asia being canceled — we wanted to help connect the community online through our summit.

\n\n\n\n

Koch left WP FeedBack soon after the summit ended and is currently back on his own and has a goal of making WP Agency Summit a yearly event.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 17:01:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:19;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: Navigation Screen Sidelined for WordPress 5.6, Full-Site Editing Edges Closer to Public Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105839\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:247:\"https://wptavern.com/navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta?utm_source=rss&utm_medium=rss&utm_campaign=navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4676:\"

The new block-based navigation screen is once again delayed after it was originally slated for WordPress 5.5 and then put on deck for 5.6. Contributors have confirmed that it will not be landing in WordPress core until 2021 at the earliest.

\n\n\n\n

“The Navigation screen is still in experimental state in the Gutenberg plugin, so it hasn’t had any significant real-world use and testing yet,” Editor Tech Lead Isabel Brison said. She made the call to remove it from the 5.6 lineup after the feature missed the deadline for bringing it out of the experimental state. It still requires a substantial amount of development work and accessibility feedback before moving forward.

\n\n\n\n

Contributors will focus instead on making sure the Widgets screen gets out the door for 5.6 and plan to pick up again on Navigation towards the end of November.

\n\n\n\n

WordPress 5.6 lead Josepha Haden gave an update this week on the progress of all the anticipated features, including the planned public beta for full-site editing (FSE).

\n\n\n\n

“I don’t expect FSE to be feature complete by the time WP5.6 is released,” Haden said. “What I expect is that FSE will be functional for simple, routine user flows, which we can start testing and iterating on. That feedback will also help us more confidently design and build our complex user flows.”

\n\n\n\n

Frank Klein, an engineer at Human Made, asked in the comments of another update why full-site editing is being tied to 5.6 progress in the first place, since it will still only be available in the plugin at the time of release.

\n\n\n\n

“The main value is that it provides a good checkpoint along the path of FSE’s development,” Kjell Reigstad said. “Full-site editing is very much in progress. It is still experimental, but the general approach is coming into view, and becoming clearer with every plugin release.”

\n\n\n\n

Reigstad posted an update on what developers can expect regarding block-based theming and the upcoming release, since the topic is closely tied to full-site editing. He emphasized that the infrastructure is already in place and that, despite it still being experimental, future block-based themes should work in a similar way to how they are working now.

\n\n\n\n

“The focus is now shifting towards polishing the user experience: using the site editor to create templates, using the query block, iterating on the post and site blocks, and implementing the Global Styles UI,” Reigstad said.

\n\n\n\n

“The main takeaway is that when 5.6 is released, the full-site editing feature set will look similar to where it is today, with added polish to the UI, and additional features in the Query block.”

\n\n\n\n

Theme authors are entering a new time of uncertainty and transition, but Reigstad reassured the community that themes as we know them today are not on track to be phased out in the immediate future.

\n\n\n\n

“There is currently no plan to deprecate the way themes are built today,” Reigstad said. “Your existing themes will continue to work as they always have for the foreseeable future.” He also encouraged contributors to get involved in an initiative to help theme authors transition to block-based themes. (This project is not targeted for the 5.6 release.)

\n\n\n\n

Developers can follow important FSE project milestones on GitHub, and subscribe to the weekly Gutenberg + Themes updates to track progress on block-based theming. A block-based version of the Twenty Twenty-One theme is in the works and should pick up steam after 5.6 beta 1, expected on October 20.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 22:57:37 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:20;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"WPTavern: EditorPlus 1.9 Adds Animation Builder for the Block Editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105678\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:181:\"https://wptavern.com/editorplus-1-9-adds-animation-builder-for-the-block-editor?utm_source=rss&utm_medium=rss&utm_campaign=editorplus-1-9-adds-animation-builder-for-the-block-editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4535:\"

Munir Kamal shows no signs of slowing down. He continues to push forward with new features for his EditorPlus plugin, which allows end-users to customize the look of the blocks in their posts and pages. He calls it the “no-code style editor for WordPress.”

\n\n\n\n

The latest addition to his plugin? Animation styles for every core block.

\n\n\n\n

My first thought was that this would bloat the plugin with large amounts of unnecessary CSS and JavaScript for what is essentially a few bells and whistles. However, Kamal pulled it off with minimal custom CSS.

\n\n\n\n

Inspired by features from various website builders, he wanted to bring more and more of those things to the core block editor. The animations feature is just another ticked box on a seemingly never-ending checklist of features. And, so far, it’s all still free.

\n\n\n\n

Since we last covered EditorPlus in June, Kamal has added the ability to insert icons via any rich-text area (e.g., paragraphs, lists, etc.). He has also added shape divider, typography, style copying, and responsive editing options for the core WordPress blocks.

\n\n\n\n

How Do Animations Work?

\n\n\n\n

In the version 1.9 release of EditorPlus, Kamal added “entrance” animations. These types of animations happen when a visitor sees the block for the first time on the screen. For example, users could set the Image block to fade into visibility as a reader views the block.

\n\n\n\n

Currently, the plugin adds seven animations:

\n\n\n\n
  • Fade
  • Slide
  • Bounce
  • Zoom
  • Flip
  • Fold
  • Roll
\n\n\n\nAdding a Slide animation for the Cover block text.\n\n\n\n

Each animation has its own subset of options to control how it behaves on the page. The bounce animation, for example, allows users to select the bounce direction. Other options include duration, delay, speed curve, delay, and repeat. There are enough choices to spend an inordinate amount of time tinkering with the output.

\n\n\n\n

One of the best features of this new feature is that Kamal has included an Animation Player under the block options. By clicking the play button, users can view the animation in action without previewing the post.

\n\n\n\n

Watch a quick video of the Animations feature:

\n\n\n\n
\n\n
\n\n\n\n

After testing and using each animation, everything seemed to work well. The one downside — and this is not limited to animations — is that applying styles on the block level sometimes does not make sense. In many cases, it would help users to have options to style or animate the items within the block, such as the images in the Gallery block. When I broached the subject with Kamal, he was open to the idea of finding a solution to this in the future.

\n\n\n\n

What Is Next for EditorPlus?

\n\n\n\n

At a certain point, too many block options can almost feel like overkill and become unwieldy. EditorPlus does allow users to disable specific features from its settings screen, which can help get rid of some unwanted options. Kamal said he would like to continue making it more modular so that users can use only the features they need.

\n\n\n\n

“What I plan is to have micro-level feature control for this extension so that a user can switch off individual styling panels like, Typography, Background, etc.,” he said. “Even further, I plan to bring these controls based on the user role as well. So an admin can disable these features for the editor, author, etc.”

\n\n\n\n

That may be a bit down the road though. For now, he wants to focus on adding new features that he already has planned.

\n\n\n\n

“I do plan to add more animation features,” said Kamal. “I got too many ideas, such as scroll-controlled animation, hover animation, text animation, Lottie animation, background animation, animated shape dividers, and more. But, having said that, I will be careful adding only those features that don’t affect page performance much.”

\n\n\n\n

Outside of extra styles and animations for existing blocks, he plans to jump on the block-building train in future releases. EditorPlus users could see accordion, toggle, slider, star rating, and other blocks in an upcoming release.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:53:40 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:21;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"Donncha: Hide featured image if it’s in the post\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://odd.blog/?p=89503242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://odd.blog/2020/10/08/hide-featured-image-if-its-in-the-post/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3885:\"

I’ve been running a photoblog at inphotos.org since 2005 on WordPress. (And thanks to writing this I noticed it’s 15 years old today!)

\n\n\n\n
\n\n\n\n

In that time WordPress has changed dramatically. At first I used Flickr to host my images, but after a short time I hosted the images myself. (Good thing too since Flickr limited free user accounts to 1000 images, so I wrote a script to download the Flickr images I used in posts.)

\n\n\n\n
\n\n\n\n

For quite a long time I used the featured image instead of inserting the image into the post content, but then about two years ago I went back to inserting the photo into the post. Unfortunately that meant the photo was shown twice, once as a featured image, and once in the post content.

\n\n\n\n

The last theme I used supported custom post types, one of which was a photo type that displayed the featured image but hid the post content. It was an ok compromise, but not perfect.

\n\n\n\n
\n\n\n\n

Recently I started using Twenty Twenty, but after 15 years I had a mixture of posts with:

\n\n\n\n
  • Featured image with no image in the post.
  • Featured image with the same image in the post.
\n\n\n\n

I knew I needed something more flexible. I wanted to hide the featured image if it also appeared in the post content. I procrastinated and never got around to it until this evening when I discovered it was actually quite easy.

\n\n\n\n\n\n\n\n

Copy the following code into the function.php of your child theme and you’ll be all set! It relies on you having unique filenames for your images. If you don’t then remove the call to basename(), and that may help.

\n\n\n
\nfunction maybe_remove_featured_image( $html ) {\n        if ( $html == \'\' ) {\n                return \'\';\n        }\n        $post = get_post();\n        $post_thumbnail_id = get_post_thumbnail_id( $post );\n        if ( ! $post_thumbnail_id ) {\n                return $html;\n        }\n\n        $image_url = wp_get_attachment_image_src( $post_thumbnail_id );\n        if ( ! $image_url ) {\n                return $html;\n        }\n\n        $image_filename = basename( parse_url( $image_url[0], PHP_URL_PATH ) );\n        if ( strpos( $post->post_content, $image_filename ) ) {\n                return \'\';\n        } else {\n                return $html;\n        }\n}\nadd_filter( \'post_thumbnail_html\', \'maybe_remove_featured_image\' );\n
\n\n\n

The post_thumbnail_html filter acts on the html generated to display the featured image. My code above gets the filename of the featured image, checks if it’s in the current post and if it is returns a blank string. Feedback welcome if you have a better way of doing this!

\n\n\n\n
\n\n\n\n

\n\n

Related Posts

\n

Source

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:43:35 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Donncha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:22;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches Automatic Platform Optimization for WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105641\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-automatic-platform-optimization-for-wordpress?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-automatic-platform-optimization-for-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6128:\"

Just a day after launching its new privacy-first web analytics product last week, Cloudflare announced Automatic Platform Optimization (APO) for WordPress. The new service boasts staggering performance improvements for sites that might otherwise be slowed down by shared hosting, slow database lookups, or sluggish plugins:

\n\n\n\n

Our testing… showed a 72% reduction in Time to First Byte (TTFB), 23% reduction to First Contentful Paint, and 13% reduction in Speed Index for desktop users at the 90th percentile, by serving nearly all of your website’s content from Cloudflare’s network. 

\n\n\n\n

APO uses Cloudflare Workers to cache dynamic content and serve the website from its edge network. In most cases this eliminates origin requests and origin processing time. That means visitors requesting your website will get near instant load times. Cloudflare reports that its testing shows APO delivers consistent load times of under 400ms for HTML Time to First Byte (TTFB).

\n\n\n\n

The effects of using APO are similar to hosting static files on a CDN, but without the need to manage a complicated tech stack. Content creators retain their ability to create dynamic websites without any changes to their workflow for the sake of performance.

\n\n\n\n

Version 3.8 of Cloudflare’s official WordPress plugin was recently updated to include support for APO. It detects when users make changes to their content and purges the content stored on Cloudflare’s edge.

\n\n\n\n

The new service is available to Cloudflare users with a single click of a button. APO is included at no cost for existing Cloudflare customers on the Professional, Business, and Enterprise plans. Users on the Free plan can add it to their sites for $5/month. The service is a flat fee and is not metered.

\n\n\n\n

Cloudflare’s announcement has so far been well-received by WordPress professionals and hosting companies and many have already begun testing it.

\n\n\n\n
\n

So the week after @Cloudflare Birthday Week I try and play with as many of the new products as possible. Today was the WordPress APO on my simple demo site. You can see TTFB dropped from ~350ms to ~75ms! https://t.co/zg976EjrZI pic.twitter.com/KuaHqtHLom

— Matt Bullock (@mibullock) October 6, 2020
\n
\n\n\n\n

WordPress lead developer Mark Jaquith called APO “incredible news for the WordPress world.”

\n\n\n\n

“On sites I manage this is going to lower hosting complexity and easily save hundreds of dollars a month in hosting costs,” Jaquith said.

\n\n\n\n

After running several speed tests from six different locations around the world, early testers at Kinsta got remarkable results using APO:

\n\n\n\n

“By caching static HTML on Cloudflare’s edge network, we saw a 70-300% performance increase. As expected, the testing locations furthest away from Tokyo saw the biggest reduction in load time.

“If your WordPress site uses a traditional CDN that only caches CSS, JS, and images, upgrading to Cloudflare’s WordPress APO is a no-brainer and will help you stay competitive with modern Jamstack and static sites that live on the edge by default.”

\n\n\n\n

George Liu, a “self-confessed page speed addict” and Cloudflare Community MVP, performed a series of detailed tests on the new APO product with his blog. After many comparisons, he found that Cloudoflare’s WordPress plugin with APO turned on delivers results similar to his heavily optimized WordPress blog that uses a custom Cloudflare Worker caching configuration.

\n\n\n\n

“You’ll find that Cloudflare WordPress plugin’s one click Automatic Platform Optimization button does wonders for page speed for the average WordPress user not well versed in page speed optimizations,” Liu said.

\n\n\n\n

“Cloudflare’s WordPress plugin Automatic Platform Optimization will in theory beat all other WordPress caching solutions other than you rolling out your own Cloudflare Worker based caching like I did. So you get a good bang for your buck at US$5/month for Cloudflare’s WordPress plugin APO.”

\n\n\n\n

Liu also warned of some speed bumps with the initial rollout, as Cloudflare’s APO supports a limited set of WordPress cookies for bypassing the Cloudflare CDN cache, leaving certain use cases unsupported. APO does not seem to work on subdomains and users are also reporting that it’s not compatible with other caching plugins. It also disables real visitor IP address detection.

\n\n\n\n

Cloudflare is aware of many of these issues, which have been raised in the comments of the announcement, and is in the process of adding more cookies to the list to bypass caching. Due to some plugin conflicts, APO may not be as plug-and-play as it sounds for some users right now, but the product is very promising and should improve over time with more feedback.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 04:18:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:23;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Kick off Block-Based WordPress Theme Development With the Theme.json Creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105832\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator?utm_source=rss&utm_medium=rss&utm_campaign=kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4674:\"

Gutenberg 9.1 made a backward-incompatible change to its theme.json file (experimental-theme.json while full-site editing is under the experimental flag). This is the configuration file that theme developers will need to create as part of their block-based themes. Staying up to date with such changes can be a challenge for theme authors, but Ari Stathopoulos, a Themes Team representative, wrote a full guide for developers.

\n\n\n\n

Jon Quach, a Principal Designer at Automattic, has also been busy creating a tool to help theme authors transition to block-based themes. He recently built a UI-based project called Theme.json Creator that builds out the JSON code for theme authors. Plus, it is up to date with the most recent changes in the Gutenberg plugin.

\n\n\n\n

Tools like these will be what the development community needs as it gets over the inevitable hump of moving away from the traditional theme development paradigm and into a new era where themes are made almost entirely of blocks and a config file.

\n\n\n\n

While plugin development is becoming more complex with the addition of JavaScript, theme development is taking a sharp turn toward its roots of HTML and CSS. We are barreling toward a future in which far more people will be able to create WordPress themes. Even the possibility of sharing pieces of themes (e.g., template parts and patterns) is on the table. This could not only empower theme designers by lowering the barrier to entry, it could also empower some end-users to make the jump into theme building.

\n\n\n\n

However, the theme.json file is one aspect of future theme authorship that is extremely developer-oriented. JSON is a universal format shared between various programming languages. It is meant to be read by machines and is not quite as human-friendly as other formats. As the theme.json file grows to accommodate more configuration options over time, the less friendly it will become to simply typing keys and values in.

\n\n\n\n

It makes sense to build tools to simplify this part of the theme building process.

\n\n\n\n

That is where the Theme.json Creator tool comes in. Theme authors pick and choose the options they want to support and input custom values. Then, the tool spits out everything in properly-formatted JSON.

\n\n\n\nUsing the Theme.json Creator tool.\n\n\n\n

One big thing the tool does not yet cover is custom CSS variables. This feature is a recent addition to the theme.json specification. It allows theme authors to create any custom property that WordPress will automatically output as CSS. In his announcement post, Stathopoulos covered how to create a typographic scale with custom properties and use those variables for editor features, such as line-height and font-size values.

\n\n\n\n

Currently, Theme.json Creator’s primary focus is on global styles. However, Gutenberg allows theme authors to configure default styles on the block level. For example, theme designers can set the color or typography options for the core Heading block to be different from the default global styles. This provides theme authors with fine-tuned control over every block.

\n\n\n\n

Theme.json Creator does not yet support configuration at this level. However, it would be interesting to see if Quach adds it in the future.

\n\n\n\n

The focus on setting up global styles is a good start for now. This is still an experimental feature. The great thing about it is that it can help theme authors begin to see how one piece of the block-based themes puzzle fits in. It is a starting point for an entirely new method of adding theme support for features when most are accustomed to adding multiple add_theme_support() PHP function calls.

\n\n\n\n

With the direction that theme development seems to be heading, it is easy to imagine that it could evolve into a completely UI-based affair at some point down the line. If templates are made up of blocks and patterns, which anyone can already build with the block editor, and if styles will essentially boil down to a config file, there will be little-to-no programming required to build a basic WordPress theme.

\n\n\n\n

If someone is not already at least jotting down notes for a plugin that allows users to create and package a block-based theme, I would be surprised. For now, Theme.json Creator is removing the need to write code for at least one part of the theme design process.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 07 Oct 2020 20:53:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:24;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: Jetpack 9.0 Introduces Loom Block, Twitter Threads Feature, and Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105743\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4033:\"
\n\n\n\n

Jetpack’s highly anticipated 9.0 release has landed, introducing some of the new features the team has previewed over the past week. Users can now publish WordPress posts to Twitter as threads. This new feature is available as part of the Publicize module when you have connected a Twitter account.

\n\n\n\n

Posting Twitter threads is a feature that only works with the block editor, as it takes advantage of how content is naturally split into chunks (blocks).

\n\n\n\n

In the comments on his demo post, Automattic engineer Gary Pendergast gave a more detailed breakdown of the logic Jetpack uses to ensure full sentences aren’t broken up in the tweets.

\n\n\n\n

“With the mental model now being focused on mapping blocks to tweets, it’s much easier to make logical decisions about how to handle each block,” Pendergast said. “So, a paragraph block is the text of a tweet, if the paragraph is too long for a single tweet, it tries to split the paragraph up by sentences. If a sentence is too long, then it resorts to splitting by words. Then, if there’s an embed/image/video/gallery block following that paragraph, we can attach it to the tweet containing that paragraph. There are additional rules for other blocks, but that’s the basic process. It then just iterates over all of the supported blocks in the post.”

\n\n\n\n

Pendergast published his post as thread to demonstrate the new feature in action. The advantage of posting a thread from your WordPress site is that it doesn’t end up getting lost in Twitter’s fast-moving timeline. Most important Twitter threads evaporate from public consciousness almost as soon as they are published. Publishing threads from your website ensures they are better indexed and easier to reference in the future.

\n\n\n\n

Jetpack Adds Loom Block for Embedding Screen Recordings

\n\n\n\n

Loom was added to Jetpack as a new oEmbed provider three weeks ago. The video recording service allows for recording camera, microphone, and desktop simultaneously. The service is especially popular in educational settings. Jetpack 9.0 introduces a new Loom block for embedding recordings.

\n\n\n\n\n\n\n\n

“Loom is growing in popularity as it is being recommended more and more to assist in distance learning efforts,” Jetpack Director of Innovation Jesse Friedman said. “Now more than ever we want to be able to help those working, learning, and teaching from home. The Loom block was a natural addition to join the other Jetpack video blocks which now include YouTube, TikTok, DailyMotion, and Vimeo.”

\n\n\n\n

Loom’s free tier allows users to record up to 25 videos, but the Pro plan is free for educators. Friedman confirmed that Jetpack does not have any kind of partnership with Loom. The team decided to support the product to assist professionals, educators, and students. Having it available as a block also makes it more convenient for those using P2 for communication.

\n\n\n\n

As anticipated, Jetpack 9.0 also provides a seamless transition necessary to ensure Instagram and Facebook embeds will continue working after Facebook drops unauthenticated oEmbed support on October 24. The Jetpack team reports that it “partnered with Facebook” to make sure these embeds continue to work with the WordPress.com REST API.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 23:28:38 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:25;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:51:\"Post Status: Joost de Valk on WordPress marketshare\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=79914\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:62:\"https://poststatus.com/joost-de-valk-on-wordpress-marketshare/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1193:\"

David Bisset makes his podcast debut for Post Status, as he interviews Joost de Valk, Founder and Chief Product Officer of Yoast, and discusses all things WordPress marketshare related.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Jilt

\n\n\n\n

Jilt offers powerful email marketing built for eCommerce. From newsletters to highly segmented automations, Jilt is your one-stop show for eCommerce email. Join thousands of stores that have already earned tens of millions of dollars in extra sales using Jilt. Try Jilt for free

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 22:28:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:26;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"WPTavern: iThemes Buys WPComplete, Complementing Its Recent Restrict Content Pro Acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105631\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition?utm_source=rss&utm_medium=rss&utm_campaign=ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4395:\"

Just one month after publicly announcing its acquisition of Restrict Content Pro (RCP), iThemes purchased WPComplete for an undisclosed amount. The acquisition is for the product, website, and customers only.

\n\n\n\n

Paul Jarvis and Zack Gilbert created the WPComplete plugin in 2016. However, it has outgrown what the duo could maintain and support alone. After the transition period in which the new owners take over, the two will step away from the project.

\n\n\n\n

In essence, WPComplete is a “course completion” plugin. Site owners can create online courses while allowing students/users to mark their work as completed. It also gives students a way to track their progress through courses, which can often boost the potential for them to finish.

\n\n\n\n

“Paul and Jack believe a key to their success has been their ability to keep their team small and manageable,” wrote Matt Danner, the COO at iThemes, in the announcement. “The growth of WPComplete has presented a number of challenges for a team of two people, so the decision was made to start looking towards alternative ownership solutions that could continue to grow WPComplete and provide it with a stable team. iThemes is a perfect fit.”

\n\n\n\n

iThemes customers who have a Plugin Suite or Toolkit membership will get automatic access to the pro version of the WPComplete plugin. For current WPComplete users, Danner said everything should be “business as usual.” However, iThemes has assigned a few of its team members to work on the product and site, so customers should see some new faces.

\n\n\n\n

RCP and WPComplete are obviously complementary products. RCP is a membership plugin that allows site owners to restrict content based on that membership. WPComplete allows site members to mark lessons or coursework as completed. “We’ll be rolling out a new bundle later this month that combines both RCP and WPComplete for course and membership creators to take advantage of these two plugins,” said AJ Morris, the Product Innovation and Marketing Manager at iThemes.

\n\n\n\n

WPComplete is still a young product. The free version of the plugin currently has 2,000+ active installs and a solid 4.7 rating on WordPress.org. If marketed as an extension of the RCP plugin, it automatically puts it in front of the eyes of 1,000s of more potential customers. It should be much easier to grow the plugin as part of a membership bundle.

\n\n\n\n

iThemes is making some bold moves in the membership space. It will be interesting to see if the company makes any other acquisitions that could strengthen its product line and help it become more dominant. There is still a ton of room for growth in the membership segment of the market. There is also the potential for integrations with other major plugins.

\n\n\n\n

“Adding WPComplete to the iThemes product lineup also allows us to move more quickly on some plans we have for Restrict Content Pro,” said Danner in the initial announcement. He also vaguely mentioned a couple of ideas the team had in the works but did not go into detail.

\n\n\n\n

With a little prodding, Morris provided some insight into what they are planning for the immediate future. The biggest first step is tackling integration with the block editor. Currently, WPComplete uses shortcodes. The team’s next step is likely to begin with creating block equivalents for those shortcodes.

\n\n\n\n

“After that, we’ve touched on a few deeper integrations with Restrict Content Pro, like the possibility to restrict courses to memberships,” said Morris.

\n\n\n\n

The iThemes team does not plan to stop with WPComplete as part of its product lineup. One of the goals is to use the plugin for the iThemes website itself.

\n\n\n\n

“We always try to eat our own dogfood when we can,” said Morris. “You’ll see that with RCP and WPComplete early next year as we look to integrate them into our iThemes Training membership.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 20:59:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:27;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: Exploring Full-Site Editing With the Q WordPress Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105676\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/exploring-full-site-editing-with-the-q-wordpress-theme?utm_source=rss&utm_medium=rss&utm_campaign=exploring-full-site-editing-with-the-q-wordpress-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7492:\"

I have been eagerly awaiting the moment when I could install a theme and truly test Gutenberg’s full-site editing feature. By and large, each time I have tested it over the past few months, the experience has felt utterly broken. This is why I have remained skeptical of seeing the feature land in WordPress 5.6 this December.

\n\n\n\n

The Q theme by Ari Stathopoulos is the first theme that seems to be a decent working example. Whether that is a stroke of luck with timing or that this particular theme is simply built correctly is hard to tell — Stathopoulos is a team rep for the Themes Team. Gutenberg 9.1 dropped last week with continued work toward site editing.

\n\n\n\n

Q is as experimental as it gets. The Themes Team put out an open call for experimental, block-based themes as far back as March this year. However, not many have taken the team up on this offer. If approved, Q stands to be the first block-based theme to go live in the official WordPress directory. It still has to work its way through the standard review process, awaiting its turn in the coming weeks.

\n\n\n\n

On the whole, full-site editing remains a frustrating and confusing experience. I still remain skeptical about its readiness, even in beta form, to show off to the world in WordPress 5.6.

\n\n\n\n

However, Q is an interesting theme to explore at this point for both end-users and theme developers. Users can install it and start tinkering with the site editing screen via the Gutenberg plugin. Developers can learn how global styles, templates, and template parts fit together from a working theme.

\n\n\n\n

Using the Site Editor

\n\n\n\nEditing a single post in the site editor.\n\n\n\n

The Q theme requires the Gutenberg plugin and its full-site editing mode to be enabled. Generally, requiring a plugin is not allowed for themes in the directory. However, experimental Gutenberg themes are allowed to bypass this guideline.

\n\n\n\n

Stathopoulos pointed out that the theme is highly experimental and should not be used on a production site. However, he is hopeful that it will get more eyes focused on full-site editing.

\n\n\n\n

He mentioned that several items are broken, such as category archives not showing the correct posts. This is a current limitation of the Query block in Gutenberg. However, one of the best ways to find and recognize these types of issues is to have a theme that stays up with the pace of development.

\n\n\n\n

Currently, the site editor feels like it is biting off more than it can chew. Not only can users edit the layout and design of the page, but they can also directly edit existing post content — don’t try this at home unless you are willing for your post titles to get switched to the hyphenated slug. Should the site editor be handling the double-duty of design and content editing? If so, should design and content editing be handled in separate locations in the long term or be merged into one feature?

\n\n\n\n

It feels raw. It is not geared toward users at this point.

\n\n\n\n

The bright spot with the site editor is the current progress on template parts in the editor. Template parts are essentially “modules” that handle one part of the page. For example, the typical theme will have a header and footer template part. Currently, end-users can insert custom template parts or switch one template part for another. This opens a world of possibilities, such as users choosing between multiple header designs (template parts) for their sites.

\n\n\n\nSwitching the header template part.\n\n\n\n

The downside to the entire template system is that it seems so divorced from the site editor that it is hard to believe the average user would understand what is going on. Templates and template parts reside under the Appearance menu in the admin. The Site Editor is a separate, top-level menu item. Without any preexisting knowledge of how these pieces work together, it can be confusing.

\n\n\n\n

Template parts worked for me in the site editor from the outset. However, they did not work on the front end at first. I continually received the “template part not found” message for hours. Then, at some point — whether through magic or a random save that pulled everything together — the feature began to output the previously-missing header and footer template parts.

\n\n\n\n

Glimpse Into the Future of Theme Development

\n\n\n\n

The Q theme has a scant few style rules, which it loads directly in the <head> section of the site in lieu of adding an extra stylesheet. It relies on the stock Gutenberg block styles on the front end with a few minor overrides. Most other custom styles are handled via the global styles system, which pulls from the theme’s experimental-theme.json config file (will be theme.json in the future).

\n\n\n\n

It begs the question of whether themes will necessarily need much in the way of CSS when full-site editing lands.

\n\n\n\n

If WordPress allows users to configure most styles via block options and global styles overrides, themes may not need much more than their config files. After that, it would come down to registering custom block styles and patterns.

\n\n\n\n

If this is the future that we are headed toward, anyone could essentially create a WordPress theme. And, those pieces, such as template parts and patterns, could all be shared between any site. In that future, themes may simply not matter anymore.

\n\n\n\n

Last year, Mike Schinkel proposed deprecating the theme system altogether and replacing it with web components.

\n\n\n\n

“Rather than look for a theme that has all the features one needs — which I have found always limits the choices to zero — a site owner could look for the components and modules they need and then assemble their site from those modules,” he said. “They could pick a header, a footer, a home-page hero, a set of article cards, a pricing module, and so on.”

\n\n\n\n

The more I tinker with full-site editing, the more it feels like that is the lane that it will ultimately merge into. Imagine a future where end-users could pick and choose the pieces they wanted and simply have it look right on the front end.

\n\n\n\n

It is exciting to think about that possibility. Both Schinkel and I have more of a background in programming than we do in design. It makes sense from that sort of analytical mindset to put everything into neat, reusable boxes because reuse is a cornerstone of smart programming.

\n\n\n\n

However, I worry about the state of design in such a system with so many replaceable parts. Will designers be able to take holistic approaches to theme development, creating truly intricate pieces of art? Will that system essentially create a web of cookie-cutter sites? Or, will designers simply find ways to think outside the box while within the constraints of the block system?

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 21:21:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:28;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Virtual Jamstack Conf to Feature Fireside Chat with Matt Mullenweg and Matt Biilmann, October 6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105680\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6?utm_source=rss&utm_medium=rss&utm_campaign=virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2618:\"
image credit: Jamstack Conf
\n\n\n\n

The greater Jamstack community is coming together on October 6-7, 2020, for a virtual conference. Organizers expect more than 15,000 attendees from around the globe over a two-day span that includes keynotes, sessions, interactive topic tables, workshops, speaker Q&As, and networking opportunities.

\n\n\n\n

Matt Mullenweg will be joining Netlify CEO Matt Biilmann on day 1 at 12PM PDT for a fireside chat moderated by CSS-Tricks Creator Chris Coyier. The chat will go deeper on recent topics of contention, including developer sentiment, complexity, security, and performance. Coyier also plans to discuss how the Jamstack and WordPress communities intersect through headless implementations of the CMS.

\n\n\n\n

A provocative post from TheNewStack at the end of August quoted Mullenweg as saying that “JAMstack is a regression for the vast majority of the people adopting it.” This sparked multiple heated exchanges across blogs and social media. Biilimann, who originally coined the term “Jamstack,” wrote a response to Mullenweg’s remarks, hailing “the end of the WordPress era.”

\n\n\n\n

Live conversations tend to be more cordial than shots fired across the blogosphere. It will be interesting to see if Biilimann cares to join Stackbit CEO Ohad Eder-Pressman in his wager that Jamstack will become the predominant architecture for the web by 2025. The fireside chat should be recorded, in case you cannot catch the live session. Recordings of talks from the previous virtual Jamstack event held in May are available on YouTube.

\n\n\n\n

Today is the last call for registration. Many of the workshops have already sold out, but tickets to the regular sessions on October 6 are still available. Sign up on the event website to get your free ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 20:12:50 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:29;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Gutenberg 9.1 Adds Patterns Category Dropdown and Reverts Block-Based Widgets in the Customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105629\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5615:\"

Gutenberg 9.1 was released to the public on Wednesday. The team announced over 200 commits from 77 contributors in its release post yesterday. One of the biggest changes to the interface was the addition of a new dropdown selector for block pattern categories. The team also reverted the block-based widgets section in the customizer and added an image size control to the Media & Text block.

\n\n\n\n

One of the main focuses of this release was improving the block-based widgets editor. The feature was taken out of the experimental stage in Gutenberg 8.9 and continues to improve. The widgets screen now uses the same inserter UI as the post-editing screen. However, users can currently only insert regular blocks. Patterns and reusable blocks are still not included.

\n\n\n\n

Theme authors can now control aspects of the block editor via a custom theme.json file. This is part of the ongoing Global Styles project, which will allow theme authors to configure features for their users.

\n\n\n\n

The development team has also added an explicit box-sizing style rule to the Cover and Group blocks. This is to avoid any potential issues with the new padding/spacing options. Theme authors who rely on the block editor styles should test their themes to make sure this change does not break anything.

\n\n\n\n

Better Pattern Organization

\n\n\n\nNew block patterns UI in the inserter.\n\n\n\n

I have been calling for the return of the tabbed pattern categories since Gutenberg 8.0, which was a regression from previous versions. For 11 versions, users have had to scroll and scroll and scroll through every block pattern just to find the one they wanted. The development team has sought to address this issue by using a category dropdown selector. When selecting a specific category, its patterns will appear.

\n\n\n\n

At first, I was unsure about this method over the old tabbed method. However, after some use, it feels like the right direction.

\n\n\n\n

As more and more theme and plugin authors add block pattern categories to users’ sites, the dropdown is a more sensible route. Even tabs could become unwieldy over time. The dropdown better organizes the list of categories and makes the UI cleaner. More than anything, I am enjoying the experience and look forward to this eventually landing in WordPress 5.6 later this year.

\n\n\n\n

Customizer Widgets Reverted

\n\n\n\nReverted widgets panel in the customizer.\n\n\n\n

On the subject of WordPress 5.6, one of its flagship features has been hitting some roadblocks. Block-based widgets are expected to land in core with the December release, but the team just reverted part of the feature. They had to remove the widgets block editor from the customizer they added just two major releases ago.

\n\n\n\n

It was for the best. The customizer’s block-based widgets editor was fundamentally broken. It was not ready for primetime and should have remained in the experimental stage until it was somewhat usable.

\n\n\n\n

“I will approve this since the current state of the customizer in the Gutenberg plugin is broken, and there is no clear path forward about how to fix that,” wrote Andrei Draganescu in the reversion ticket. “With this patch, the normal widgets can still be edited in the customizer and the block ones don’t break it anymore. This is NOT to mean that we won’t proceed with fixing the block editor in the customizer, that is still an ongoing discussion.”

\n\n\n\n

The current state of editing widgets via the customizer is at least workable with this change. If end-users add a block via the admin-side widgets editor, it will merely appear as an uneditable, faux widget named “Block” in the customizer. They will need to edit blocks via the normal widgets screen.

\n\n\n\n

There is no way that WordPress can ship the current solution when 5.6 rolls out. However, we are still two months out. This leaves plenty of time for a fix, but Draganescu’s note that “there is no clear path forward” may make some people a bit uneasy at this stage of development.

\n\n\n\n

Control Image Size for Media & Text

\n\n\n\nImage size dropdown selector for the Media & Text block.\n\n\n\n

One of the bright spots in this update is the addition of an image size control to the Media & Text block. Like the normal Image block, end-users can choose from any registered image size created for their uploaded image.

\n\n\n\n

This is a feature I have been looking forward to in particular. Previously, using the full-sized image often made the page weight a bit heftier than necessary. It is also nice to go along with themes that register sizes for both landscape and portrait orientations, giving users more options.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 20:56:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:30;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"WordPress.org blog: The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8711:\"

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:31;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches New Web Analytics Product Focusing on Privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105446\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-new-web-analytics-product-focusing-on-privacy?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-new-web-analytics-product-focusing-on-privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2448:\"

In pursuit of “democratizing web analytics,” Cloudflare announced it is launching privacy-first analytics as a new standalone product. The company is entering a market that has been dominated by Google Analytics for years but with a major differentiating feature – it will not track individual users by a cookie or IP address to show unique visits.

\n\n\n\n

Cloudflare Web Analytics defines a visit as “a successful page view that has an HTTP referer that doesn’t match the hostname of the request.” It’s not the same as Google’s “unique” metric, and Cloudflare says it may differ from other reporting tools. Weeding out bots from the total traffic numbers is a nascent feature that Cloudflare is improving as part of its Bot Management product.

\n\n\n\n
\n\n\n\n

Cloudflare Web Analytics is launching with features that are largely similar to Google Analytics but with some unique ways of zooming into different traffic segments and time ranges to see where traffic is originating from.

\n\n\n\n

“The most popular analytics services available were built to help ad-supported sites sell more ads,” Cloudflare product manager Jon Levine said. “But, a lot of websites don’t have ads. So if you use those services, you’re giving up the privacy of your users in order to understand how what you’ve put online is performing.

\n\n\n\n

“Cloudflare’s business has never been built around tracking users or selling advertising. We don’t want to know what you do on the Internet — it’s not our business.”

\n\n\n\n

Paying customers on the Pro, Biz, and Enterprise plans can access their analytics from their dashboards immediately. Cloudflare is also offering the product for free as JavaScript-based analytics for users who are not currently customers. Those who want access to the free plan can sign up for the waitlist.

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 04:03:01 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:32;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"WPTavern: Virtual WordPress Page Builder Summit Kicks Off October 5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105570\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:179:\"https://wptavern.com/virtual-wordpress-page-builder-summit-kicks-off-october-5?utm_source=rss&utm_medium=rss&utm_campaign=virtual-wordpress-page-builder-summit-kicks-off-october-5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6348:\"

From October 5 through October 9, the first Page Builder Summit will open its virtual doors to all attendees for free. Nathan Wrigley, the podcaster behind WP Builds, and Anchen le Roux, the founder and lead developer of Simply Digital Design, are hosting the five-day online event that focuses on the vast ecosystem of page builders for WordPress.

\n\n\n\n

The summit will include 35 sessions spread out over the event schedule. Each session will last around 30 minutes, so it will be easy to pop in and watch one in your downtime. Sessions will cover a range of builders, including the default WordPress block editor, Elementor, Beaver Builder, Oxygen, Brizy, and Divi.

\n\n\n\n

“It’s an event specifically for users of WordPress page builders, or those curious about what they can do,” said Wrigley. “I feel like a page builder style interface for creating websites is the future for our industry. WordPress itself is moving in this direction with the block editor (a.k.a. Gutenberg). With that in mind, it seemed like a good idea to create a dedicated event to share knowledge about this side of WordPress. We’ve tried to include presentations from as many page builders as we could.”

\n\n\n\n

Wrigley made sure to point out that it is not all geared toward developers, discussing the inner-workings of builders. Some of the sessions focus on marketing, optimization, and conversion, which provides a wider range of topics for potential attendees.

\n\n\n\n

The summit hosts created an online quiz for those who are unsure about which sessions to watch.

\n\n\n\n

There is a small catch. The sessions will be freely available only from the time they begin and the following 24 hours. After that, accessing the videos will come at a premium. Attendees can gain lifetime access to the PowerPack for $47 if they purchase within 15 minutes of signing up. Then, prices will rise to $97 until the event kicks off on October 5. Beyond, the price jumps to $147. The lifetime access includes access to the presentations, transcripts, a workbook, and other bonuses from the speakers.

\n\n\n\n

For those unsure about forking over the cash, they can still watch the sessions during the 24-hour window.

\n\n\n\n

The proceeds from the event will go out to paying affiliate commissions to speakers and partners. Some of it will go into planning and investing in a second summit down the road.

\n\n\n\n

“Both myself and Nathan have specific charities that we want to donate to after the event,” said le Roux. “It was part of our goals to be able to do this, but we didn’t want to make this an official contribution.”

\n\n\n\n

Why a Page Builder Summit?

\n\n\n\n

Both Wrigley and le Roux have their preferred builders. But, the goal of the summit is to offer a wide look at the tools available and help freelancers and agencies better streamline their businesses and create happier clients.

\n\n\n\n

“I’ve been a user of page builders for many years, but only at the point where they truly showed in the editing interface something that almost perfectly reflected what the end-user would see did I get really immersed,” said Wrigley. “Having come from a background in which I built entire websites from a collection of text files (HTML, CSS, PHP, etc.), I was fascinated that we’d reached a point where the learning curve for building a good website was significantly reduced.”

\n\n\n\n

He pointed out that it is not always so simple though. While the same level of coding skills may not be necessary, people must figure out how to navigate their preferred page builder, which can come with its own learning curve.

\n\n\n\n

“You need to learn their way of doing things and how to achieve your design choices,” he said. “It’s always going to work out better if you know the code, but the WordPress mission of democratizing publishing certainly seems to align quite nicely with the adoption of tools, like page builders, which mean that once-difficult tasks are now easier.”

\n\n\n\n

For le Roux, her interest in hosting the Page Builder Summit falls back to her design studio.

\n\n\n\n

“As a developer, my main reason for switching to page builders was around streamlining and creating more efficient but quality websites in the shortest amount of time,” she said. “Especially now that we focus on day rates, creating the best possible website that clients would love fast would not have been possible without page builders.”

\n\n\n\n

The Hosts’ Go-To Builders

\n\n\n\n

“We prefer using Beaver Builder with Themer at Simply Digital Design,” said le Roux. “We use Gutenberg for blog posts or where possible with custom post types or LMS software. However, we’ve also taken on a few Elementor projects where that’s the client’s preferred option.”

\n\n\n\n

Wrigley uses some of the same tools. His main work is on the WP Builds website where he hosts podcasts.

\n\n\n\n

“I have used Beaver Builder’s Themer to create templates for specific layouts, but for content creation within those layouts I’m using the block editor,” said Wrigley. “My content is mainly text and the WordPress editor is utterly remarkable in this situation. I kept the classic editor installed for a few months after WordPress 5.0 came about, but I soon realized that this was folly and that the editing interface of Gutenberg is superior. The ability to insert and move text, buttons, etc. is such a joy to work with, and the iterations that have been made in the last two years make it, in my opinion, the best text editing experience on the web.”

\n\n\n\n

Wrigley sees a future in which the WordPress block editor takes over much of the work that page builders are currently handling. However, that future is “still over the horizon.”

\n\n\n\n

“I’m excited about this future though, and we’ve got a few crystal ball-gazing presentations; trying to work out what that future might look like,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 20:31:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:33;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"WPTavern: Jetpack 9.0 to Introduce New Feature for Publishing WordPress Posts to Twitter as Threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105448\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3318:\"

Jetpack 9.0, coming on October 6, will debut a new feature that allows users to share blog posts as Twitter threads in multiples tweets. A recent version of Jetpack introduced the ability to import and unroll tweetstorms for publishing inside a post. The 9.0 release will run it back the other way so the content originates in WordPress, yet still reaps all the same benefits of circulation on Twitter as a thread.

\n\n\n\n

The new Twitter threads feature is being added as part of Jetpack’s Publicize module under the Twitter settings. After linking up a Twitter account, the Jetpack sidebar options for Publicize allow users to publish to Twitter as a link to the blog or a set of threaded tweets. It’s not just limited to text content – the threads feature will also upload and attach any images and videos included in the post.

\n\n\n\n\n\n\n\n

When first introduced to the idea of publishing a Twitter thread from WordPress, I wondered if threads might lose their trademark pithy punch, since users aren’t forced to keep each segment to the standard length of a tweet. Would each tweet be separated in an odd, unreadable way? The Jetpack team anticipated this, so the thread option adds more information to the block editor to show where the paragraphs will be split into multiple tweets.

\n\n\n\n

“Threads are wildly underused on Twitter,” Gary Pendergast said in a post introducing the feature. “I think a big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.” The tool Pendergast has been working on for Jetpack gives users the best of both worlds.

\n\n\n\n

In response to a comment requesting Automattic “concentrate on tools to get people off social media,” Pendergast said, “If we’re also able to improve the quality of conversations on social media, I think it’d be remiss of us to not do so.” He also credits IndieWeb discussions on Tweetstorms and POSSE (Publish (on your) Own Site, Syndicate Elsewhere) as inspirations for the feature.

\n\n\n\n

For years, blogging advocates have tried to convince those who post lengthy tweetstorms to switch to a publishing medium that is more suitable to the length of their thoughts. The problem is that Twitter users lose so much of the immediate feedback and momentum that their thoughts would have generated when composed as a tweetstorm.

\n\n\n\n

Instead of lecturing people about how they should really be blogging instead of tweetstorming, Jetpack is taking a fresh approach by enabling full content ownership with effortless social syndication. You can test out the experience for yourself by adding the Jetpack Beta Testers plugin and running the 9.0 RC version on your site.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 02:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:34;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: Ask the Bartender: How To WordPress in a Block World?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105491\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:167:\"https://wptavern.com/ask-the-bartender-how-to-wordpress-in-a-block-world?utm_source=rss&utm_medium=rss&utm_campaign=ask-the-bartender-how-to-wordpress-in-a-block-world\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9755:\"

I love your articles. And now, in the middle of the WordPress revolution, I realized I’m constantly searching for an answer regarding WP these days.

So many things are being said, so many previsions of the future, problems, etc., but, right now, I think I, as a designer, just want to understand one thing that seemed answered already but it’s never clear:

Is WordPress a good choice to build a client’s template where he just has to insert the info that will show in the frontend where I want to? And he doesn’t have to worry about formatting blocks? I love blocks, don’t get me wrong, but will normal templating end?

I just think that having a super CMS, HTML, CSS, and being able to play with a database with ACF is so powerful, that I’m wondering if it’s lost. After so much reading, I still don’t understand if this paradigm is going to disappear.

Right now, I don’t know if it’s best to stop making websites as I used to and adopt block patterns instead.

Ricardo
\n\n\n\n

WordPress is definitely changing. Over the past two years, we have seen much of it reshaped into something different from the previous decade and more. However, this is not new. WordPress has always been a constantly-changing platform. It just feels far too different this time around, almost foreign to many. The platform had to make a leap. Otherwise, it would have started falling behind.

\n\n\n\n

And, it is a big ask of the existing community to come along with it, to take that leap together.

\n\n\n\n

It can be scary as a developer whose livelihood has depended on things working a certain way or who has built tools and systems around pre-block WordPress. Many freelancers and agencies had their world turned upside down with the launch of the block editor. It is perfectly OK to feel a bit lost.

\n\n\n\n

Now, it is time for a little tough love. It has been two years. As a professional, you need to have a plan in place already. Whether that is an educational plan for yourself or a transitional plan for your clients, you should already be tackling projects that leverage the block editor. If you are at a point where you have not been building with blocks, you are now behind. However, you can still catch up and continue advancing in your WordPress career.

\n\n\n\n

There are so many changes coming down the pipeline that anyone who plans to develop for WordPress will be in continual education mode for years to come.

\n\n\n\n

When building for clients, the biggest thing to remember is that it is not about you. It is about getting something into the hands of your clients that addresses their specific needs. Freelancers and agencies need to often be the Jacks and Jills of all trades. Sometimes, this even means having a backup CMS or two that you can use that are not named WordPress. It helps to be well-rounded enough to jump around when needed, especially if you are not at a point in your career where you can demand specific work and pass on jobs that would put food on the table.

\n\n\n\n

It is also easy to look at every job as a nail and WordPress as the hammer. Or, even specific plugins as the tool that will always get the job done. I have seen developers in the past rely on tools like ACF, CMB2, or Meta Box but could not code a custom metadata solution when necessary to save their life. Sometimes a bigger toolbox is necessary.

\n\n\n\n

Every WordPress developer needs a solid, foundational understanding of the languages that WordPress uses. Gone are the days of skating by on HTML, CSS, and PHP knowledge. You need to learn JavaScript deeply. Matt Mullenweg, the co-founder of WordPress, was not joking around when he said this back in 2015. It holds true more and more each day. In another five years, it will tough to be a developer in the WordPress world without knowing JavaScript, at least for backend work.

\n\n\n\n

It also depends on what types of sites you are building. If you are primarily handling front-end design, you will likely be able to get by with a lower skill level. You will just need to know the “WordPress way” of building themes.

\n\n\n\n

Within the next year, you should be able to build just about any theme design with decent CSS and HTML knowledge along with an understanding of how the block system works. Full-site editing and block-based themes will change how we build the front end of the web. It is going to be a challenging transition at first, especially for those of us who are steeped in traditional theme development, but client sites will often be far easier to build. I highly recommend the twice-monthly block-based themes meetings if your focus is on the front end.

\n\n\n\n

Block Templates

\n\n\n\n

Based on your question, I am going to make some assumptions. You have a history of essentially building out meta boxes via ACF where the client just pops in their data. Then, you format that data on the front end. You are likely mixing this with custom post types (CPTs). This is a fairly common scenario.

\n\n\n\n

One of the great things about the block system is that you can lock the post editor for individual CPTs. WordPress already has you covered with its block templates feature, which allows you to define just what a post should look like. You can set up which blocks you want to appear and have the client drop their content in. At the moment, this feature is limited to the post type level. However, it should grow more robust over time, particularly when it works alongside the traditional “page templates” system.

\n\n\n\n

Block templates are a powerful tool in the ol’ toolbox that will come in handy when building client sites.

\n\n\n\n

Block Patterns

\n\n\n\n

You do not have to stop making websites as you are accustomed to at the moment. However, you should start leveraging new block features as they become available and make sense for a specific project. I am a fanatic when it comes to block patterns, so my bias will definitely show.

\n\n\n\n

The biggest thing with block patterns and clients is education. For the uninitiated, you will need to spend some time teaching them how to insert a pattern and how it can be used to their advantage. That is the hurdle you must jump.

\n\n\n\n

For many of the users that I have seen introduced to well-designed patterns, they have fallen in love with the feature. Even many who were reluctant to switch to the block editor became far more comfortable working with it after learning how patterns worked. This is not the case for every user or client, but it has been a good introduction point to the block editor for many.

\n\n\n\n

To answer your question regarding patterns: yes, you should absolutely begin to adopt them.

\n\n\n\n

ACF Is Evolving

\n\n\n\n

Because you are accustomed to ACF, you should be aware that the framework is evolving to keep up with the block editor. Version 5.8.0 introduced a PHP framework for creating custom blocks over a year ago. And, it has been improving ever since. There are even projects like ACF Blocks, which will provide even more tools for your arsenal.

\n\n\n\n

It is important to learn from what some of the larger agencies are doing. Read up on how WebDevStudios is tackling block development. The company also has an open-source block library for ACF.

\n\n\n\n

Solving Problems

\n\n\n\n

Your job as a developer is to be a problem solver. Whatever system you are building with is merely a part of your toolset. You need to be able to solve issues regardless of what tool you are using. At the end of the day, it is just code. If you can learn HTML, you can learn CSS. If you can learn those, you can learn PHP. And, if you can manage PHP, you can certainly pick up JavaScript.

\n\n\n\n

A decade or two from now, you will need to learn something else to stay relevant in your career. Web technology changes. You must change with it. Always consider yourself a student and continue your education. Surround yourself and learn from those who are more advanced than you. Emulate, borrow, and steal good ideas. Use what you have learned to make them great.

\n\n\n\n

There is no answer I can give that will be perfect for every scenario. Each client is unique, and you will need to decide the best direction for each.

\n\n\n\n

However, yes, you should already be on the path to building with a block-first mindset if you plan to continue working with WordPress for the long haul. Immerse yourself in the system. Read, study, and build something any chance you get.

\n\n\n\n

This is the first post in the Ask the Bartender series. Have a question of your own? Shoot it over.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 30 Sep 2020 20:35:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:35;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: Supercharge the Default WordPress Theme With Twentig, a Toolbox for Twenty Twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105344\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:225:\"https://wptavern.com/supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty?utm_source=rss&utm_medium=rss&utm_campaign=supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6455:\"Custom page pattern from the Twentig plugin.\n\n\n\n

I am often on the hunt for those hidden gems when it comes to block-related plugins. I like to see the interesting places that plugin authors venture. That is why it came as a surprise when someone recommended I check out the Twentig plugin a few days ago. Somehow, it has flown under my radar for months. And, it has managed to do this while being one of the more interesting plugins for WordPress I have seen in the past year.

\n\n\n\n

Twentig is a plugin that essentially gives superpowers to the default Twenty Twenty theme. Diane and Yann Collet are the sibling co-founders and brains behind the plugin.

\n\n\n\n

While I have been generally a fan of Twenty Twenty since it was first bundled in core, it was almost a bit of a letdown in some ways. It was supposed to be the theme that truly showcased what the block editor could do — and it does a fine job of styling the default blocks — but there was a lot of potential left on the table. The Twentig plugin turns Twenty Twenty into something worthier of a showcase for the block editor. It is that missing piece, that extra mile in which WordPress should be marching its default themes.

\n\n\n\n

While the new Twenty Twenty-One default theme is just around the corner, Twentig is breathing new life into the past year’s theme. The developers behind the plugin are still fixing bugs and bringing new features users.

\n\n\n\n

Of its 34 reviews on WordPress.org, Twentig has earned a solid five-star rating. That is a nice score for a plugin with only 4,000 active installations. As I said, it has flown under the radar a bit, but the users who have found it have obviously discovered something that adds those extra touches to their sites they need.

\n\n\n\n

What Does Twentig Do?

\n\n\n\n

It is a toolbox for Twenty Twenty. The headline feature is its block editor features, such as custom patterns and page layouts. It also offers a slew of customizer options that allow end-users to put their own design spin on the default theme. However, my interest is primarily in how it extends the block editor.

\n\n\n\n

Let’s get this out of the way up front. Twentig’s one downside is that it adds a significant amount of additional CSS on top of the already-heavy Twenty Twenty and block editor styles. I will blame the current lack of a full design system from WordPress on most of this. Styling for the block editor can easily bloat a stylesheet. Adding an extra 100+ kb per page load might be a blocker for some who would like to try the plugin. Users will need to weigh the trade-offs between the additional features and the added page size.

\n\n\n\n

The thing that makes Twentig special is its extensive patterns and pages library, which offers one-click access to hundreds of layouts specifically catered to the Twenty Twenty theme.

\n\n\n\nInserting one of the hero patterns.\n\n\n\n

It took me a few minutes to figure out how to access the patterns — mainly because I did not read the manual. I expected to find them mixed in with the core patterns inserter. However, the plugin adds a new sidebar panel to the editor, which users can access by clicking the “tw” icon. After seeing the list of options, I can understand why they probably would not fit into WordPress’s limited block and patterns inserter UI.

\n\n\n\n

It would be easier to list what the plugin does not have than to go through each of the custom patterns and pages.

\n\n\n\n

The one thing that truly sets this plugin apart from the dozens of other block-library types of plugins is that there are no hiccups with the design. Almost every similar plugin or tool I have tested has had CSS conflicts with themes because they are trying to be a tool for every user. Twentig specifically targets the Twenty Twenty theme, which means it does not have to worry about whether it looks good with the other thousands of themes out there. It has one job, which is to extend its preferred theme, and it does it with well-designed block output.

\n\n\n\n

The other aspect of this is that it does not introduce new blocks. Every pattern and page layout option uses the core WordPress blocks, which includes everything from hero sections to testimonials to pricing tables to event listings. And more.

\n\n\n\n

Twentig does not stop adding features to the block editor with custom patterns. The useful and sometimes fun bits are on the individual block level, and I have yet to explore everything. I continue to discover new settings each time I open my editor.

\n\n\n\n

Whether it is custom pullquote styles, a photo image frame, or an inner border tweak to the Cover block (shown below), the plugin adds little extras that push what users can do with their content.

\n\n\n\nInner border style for the Cover block.\n\n\n\n

Each block also gets some basic top and bottom margin options, which comes in handy when laying out a page. At this point, I am simply looking forward to discovering features I have yet to find.

\n\n\n\n

Areas Themes Should Explore

\n\n\n\n

One of the things I dislike about many of these features being within the Twentig plugin is that I would like to see them within the Twenty Twenty theme instead. Obviously not every feature belongs in the theme — some features firmly land in plugin territory. The default WordPress themes should also leave some room for plugin authors to explore. But, shipping some of the more prominent patterns and styles with Twenty Twenty would make a more robust experience for the average end-user looking to get the most out of blocks.

\n\n\n\n

Block patterns were not a core WordPress feature when Twenty Twenty landed. However, for the upcoming Twenty Twenty-One theme, which is expected to bundle some unique patterns, the design team should explore what the Twentig plugin has brought to the current default. That is the direction that theme development should be heading, and theme developers can learn a lot by stealing borrowing from this plugin.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 22:00:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:36;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Coming in Jetpack 9.0: Shortcode Embeds Module Updated to Handle Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105381\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2938:\"

Facebook and Instagram are dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers in an upcoming release. After evaluating third-party solutions, WordPress VIP is recommending its partners enable Jetpack’s Shortcode Embeds module. Jetpack will be shipping the update in its 9.0 release, which is anticipated to land prior to the October 24th deadline.

\n\n\n\n

The module is being updated to provide a seamless transition for users who might otherwise be negatively impacted by Facebook’s upcoming API change. WordPress contributors have run some simulations but are not yet sure what will happen to the display for previously embedded content.

\n\n\n\n

“It is possible that they change the contents of the JS file to manipulate cached embeds, perhaps to display a warning that the site is using an old method to embed content or that the request is not properly authenticated,” Jonathan Desrosiers commented on the trac ticket for removing the oEmbed providers.

\n\n\n\n

WordPress.com VIP roughly outlined what users can expect if they do not enable a solution to begin authenticating oEmbeds:

\n\n\n\n

By default, WordPress caches oEmbed contents in post metadata. These embeds will continue to display in previously-published content. If you edit older posts in the Block Editor, regardless of whether you update the post by saving changes, the embeds in the post will no longer be cached and will stop displaying. If you view these older posts using the Classic Editor, so long as the post is not re-saved, the embeds will continue to function and display properly. If you update the post content, the embed will cease functioning unless you have a mitigation installed.

\n\n\n\n

Although WordPress VIP recommends using the Jetpack module as the best solution, self-hosted WordPress users may want to investigate other options if they are not already using Jetpack. oEmbed Plus is a free plugin created specifically for solving the problem of WordPress dropping Facebook and Instagram as oEmbed providers but it is more work to set up and configure. It requires users to register as a Facebook developer and create an app to get API credentials.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 21:18:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:37;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:52:\"WPTavern: W3C Selects Craft CMS for Redesign Project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105265\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:149:\"https://wptavern.com/w3c-selects-craft-cms-for-redesign-project?utm_source=rss&utm_medium=rss&utm_campaign=w3c-selects-craft-cms-for-redesign-project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9407:\"

W3C has selected Craft CMS over Statamic for its redesign project, after dropping WordPress from consideration in an earlier round of elimination:

\n\n\n\n

In the end, our decision mostly came down to available resources. Craft had already committed to reach AA compliance in Craft 4 (it is currently on version 3.5, the release of version 4 is planned for April 2021). They had also arranged for an external agency to provide them with accessibility issues to tackle weekly. In the end, they decided instead to hire an in-house accessibility specialist to perform assessments and assist the development team in adopting accessibility patterns in the long run.

W3C CMS Selection Report
\n\n\n\n

Last week we published a post urging W3C to revisit Gutenberg for a fair shake against the proprietary CMS’s or consider adopting another open source option. During the selection process, Studio 24, the agency contracted for the redesign, cited its extensive experience with WordPress as the reason for not performing any accessibility testing on more recent versions of Gutenberg.

\n\n\n\n

When asked if the team contacted anyone from WordPress’ Accessibility Team during the process or put Gutenberg through the same tests as the proprietary CMS’s, Studio 24 founder Simon Jones confirmed they had not.

\n\n\n\n

“No, we only reached out to the two shortlisted CMS’s” Jones said. “I’m afraid we didn’t have time to do more. We did test GB a few months ago based on editing content – though it wasn’t the only factor in our choice. As an agency we do plan to keep reviewing GB in the future.”

\n\n\n\n

In response to our concerns regarding licensing, Jones penned an update titled “On not choosing WordPress,” which further elaborated on the reasons why the agency was not inclined towards using or evaluating the new editor:

\n\n\n\n

From a business perspective I also believe Gutenberg creates a complexity issue that makes it challenging for use by many agencies who create custom websites for clients; where we have a need to create lots of bespoke blocks and page elements for individual client projects.

The use of React complicates front-end build. We have very talented front-end developers, however, they are not React experts – nor should they need to be. I believe front-end should be built as standards-compliant HTML/CSS with JavaScript used to enrich functionality where necessary and appropriate.

As of yet, we have not found a satisfactory (and profitable) way to build custom Gutenberg blocks for commercial projects. 

\n\n\n\n

The CMS selection report also stated that W3C needs the CMS to be “usable by non-sighted users” by the launch date, since some members of the staff who contribute to the website are non-sighted.

\n\n\n\n

Since the most recent version of WordPress was not tested in comparison with the proprietary CMS’s, it’s unclear how much better they handle accessibility. Ultimately, W3C and Studio 24 were more comfortable moving forward with a proprietary vendor that was able to make certain assurances about the future accessibility of its authoring tool, despite having a smaller pool of contributors.

\n\n\n\n

“[I’m] also deeply curious since the cursory notes on accessibility for both of the reviewed CMSes seem to highlight a ton of issues like ‘Buttons and Checkboxes are built using div elements’ or most inputs lacking clear focus styles,” Gutenberg technical lead Matías Ventura said. “An element like the Calendar for choosing a post date seems entirely inoperable with keyboard on Craft, for example, while WordPress’ has had significant effort and rounds of feedback poured into that element alone to make it fully operable.”

\n\n\n\n

WordPress developer Anthony Burchell commented on how using a relatively new proprietary CMS seemed counter to W3C’s stated goal to select an option on the basis of longevity. Craft CMS’s continued success is contingent upon its business model and the company’s ability to remain profitable.

\n\n\n\n

“FOSS have the same opportunity of direct access to developers,” Burchell said. “I recognize there are many accessibility shortcomings in popular software, but I think it’s more constructive to rally behind and contribute, not use a proprietary CMS that boasts beer budget in their guidelines.”

\n\n\n\n

On the other side of the issue, accessibility advocates took the W3C’s decision as a referendum on Gutenberg’s continued struggles to meet WCAG AA standards. WordPress accessibility specialist Amanda Rush said it was “nice to see the W3C flip tables over this.”

\n\n\n\n

“Gutenberg is not mature software,” accessibility consultant and WordPress contributor Joe Dolson said in a post elaborating on his comments at WPCampus 2020 Online. He emphasized the lack of stability in the project that Studio 24 alluded to when documenting the reasons against using WordPress.

\n\n\n\n

“It is still undergoing rapid changes, and has grand goals to add a full-site editing experience for WordPress that almost guarantees that it will continue to undergo rapid changes for the next few years,” Dolson said. “Why would any organization that is investing a large amount into a site that they presumably hope will last another 10 years want to invest in something this uncertain?”

\n\n\n\n

Dolson also said the accessibility improvements he referenced regarding the audit were only a small part of the whole picture.

\n\n\n\n

“They only encompass issues that existed in the spring of 2019,” he said. “Since then, many features have been added and changed, and those features both resolve issues and have created new ones. The accessibility team is constantly playing catch up to try and provide enough support to improve Gutenberg. And even now, while it is more or less accessible, there are critical features that are not yet implemented. There are entirely new interface patterns introduced on a regular basis that break prior accessibility expectations.”

\n\n\n\n

WordPress is also being used by millions of people who are constantly reporting issues to fuel the software’s continued refinement, which increases the backlog of issues. Unfortunately, Studio 24 did not properly evaluate Gutenberg against the proprietary CMS’s in order to determine if these software projects are in any better shape.

\n\n\n\n

Instead, they decided that Craft CMS’s community was more receptive to collaborating on issues without reaching out to WordPress. Given the W3C’s stated preference for open source software, WordPress, as the only CMS under consideration with an OSD-compliant license, should have received the same accessibility evaluation.

\n\n\n\n

“I can’t make any statements that would be meaningful about the other content management systems under consideration; but if WordPress wants to be taken seriously in environments where accessibility is a legal, ethical, and mission imperative, there’s still a lot of work to be done,” Dolson said.

\n\n\n\n

Studio 24’s evaluation may not have been equitable to the only open source CMS under consideration, but the situation serves to highlight a unique quandary: when using open source software becomes the impractical choice for organizations requiring a high level of accessibility in their authoring tools.

\n\n\n\n

“Studio 24 ultimately determined that working with a CMS to make it better was more possible with a smaller, proprietary vendor than with a large open-source project,” accessibility advocate Brian DeConinck said. “Project leadership would be more receptive, and the smaller community means changes can be made more quickly. That should prompt a lot of soul-searching for…well, everyone. What does that say about the future of open source?”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 04:56:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:38;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"Gary: More than 280 characters\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:25:\"https://pento.net/?p=5405\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://pento.net/2020/09/29/more-than-280-characters/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5187:\"

It’s hard to be nuanced in 280 characters.

\n\n\n\n

The Twitter character limit is a major factor of what can make it so much fun to use: you can read, publish, and interact, in extremely short, digestible chunks. But, it doesn’t fit every topic, ever time. Sometimes you want to talk about complex topics, having honest, thoughtful discussions. In an environment that encourages hot takes, however, it’s often easier to just avoid having those discussions. I can’t blame people for doing that, either: I find myself taking extended breaks from Twitter, as it can easily become overwhelming.

\n\n\n\n

For me, the exception is Twitter threads.

\n\n\n\n

Twitter threads encourage nuance and creativity.

\n\n\n\n

Creative masterpieces like this Choose Your Own Adventure are not just possible, they rely on Twitter threads being the way they are.

\n\n\n\n
\n

Being Beyoncé’s assistant for the day: DONT GET FIRED THREAD pic.twitter.com/26ix05Hkhp

— green chyna (@CORNYASSBITCH) June 23, 2019
\n
\n\n\n\n

Publishing a short essay about your experiences in your job can bring attention to inequality.

\n\n\n\n
\n

DOWNTOWN BROOKLYN: I\'m working arraignments tonight, representing poor New Yorkers who were arrested yesterday on Thanksgiving.

It was the coldest Thanksgiving in more than a century. Tonight\'s also bitterly cold, even in the courtroom. I\'m wearing my scarf & coat.

— Rebecca Kavanagh (@DrRJKavanagh) November 24, 2018
\n
\n\n\n\n

And Tumblr screenshot threads are always fun to read, even when they take a turn for the epic (over 4000 tweets in this thread, and it isn’t slowing down!)

\n\n\n\n
\n

Tumblr textposts thread, probably?

— we are a family forged in bureaucracy (@ex_aItiora) August 26, 2019
\n
\n\n\n\n

Everyone can think of threads that they’ve loved reading.

\n\n\n\n

My point is, threads are wildly underused on Twitter. I think I big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.

\n\n\n\n

To help make this easier, I’ve been working on a tool that will help you publish an entire post to Twitter from your WordPress site, as a thread. It takes care of transforming your post into Twitter-friendly content, you can just… write. \"?\"

\n\n\n\n

It doesn’t just handle the tweet embeds from earlier in the thread: it handles handle uploading and attaching any images and videos you’ve included in your post.

\n\n\n\n\n\n\n\n

All sorts of embeds work, too. \"?\"

\n\n\n\n
\n
\n
\n\n\n\n

It’ll be coming in Jetpack 9.0 (due out October 6), but you can try it now in the latest Jetpack Beta! Check it out and tell me what you think. \"?\"

\n\n\n\n

This might not fix all of Twitter’s problems, but I hope it’ll help you enjoy reading and writing on Twitter a little more. \"?\"

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 02:33:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Gary\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:39;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Themes Team Releases a Web Fonts Loader, Likely To Prohibit Hotlinking Any Off-Site Assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105363\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets?utm_source=rss&utm_medium=rss&utm_campaign=themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5815:\"

Last Friday, the WordPress Themes Team announced the release of its new Webfonts Loader project. It is a drop-in script that allows theme authors to load web fonts from the user’s site instead of a third-party CDN. The secondary message included in the team’s announcement is that it no longer plans to allow themes to hotlink Google Fonts in the future.

\n\n\n\n

Throughout most of the team’s history, it has not allowed themes to hotlink or use CDNs for hosting theme assets, such as CSS, JavaScript, and fonts. The one exception to this rule was the use of Google Fonts. This allowed themes to have richer typography options at their disposal from what the team has generally declared a reliable source.

\n\n\n\n

“The exception was made because there was no practical way to not have the exception at the time,” said Aria Stathopoulos, a Themes Team representative and developer behind the Webfonts Loader project. “The exception for Google Fonts was made out of necessity. Now that there is another way, the exception will not be necessary.”

\n\n\n\n

In effect, disallowing the Google Fonts CDN would not be a new ban. It would be a removal of an exception to the existing ban.

\n\n\n\n

Google Fonts has become so embedded into the theme developer toolset over the years, there was no way the team could simply pull the plug and prohibit the use of the CDN overnight. If the Themes Team members wanted to focus more on privacy, they would need to build a tool that made it dead simple for theme authors to use.

\n\n\n\n

There is no hard deadline for when the team will remove the exception for Google Fonts, and it is not set in stone at this point. Stathopoulos said removing it has been the goal from the beginning, disallowing all CDNs. However, it took a while to find an efficient way to handle this. With a viable alternative in place, they can discuss moving forward.

\n\n\n\n

Webfonts Loader for Themes

\n\n\n\n

The Webfonts Loader project keeps it simple for theme authors. It introduces a new wptt_get_webfont_styles() function that developers can plug in a stylesheet URL. Once a page is loaded with that function call, it will download the fonts locally to a /fonts folder in the user’s /wp-content directory. This way, fonts will always be served from the user’s site.

\n\n\n\n

The system is not limited to Google Fonts either. Any URL that serves CSS with an @font-face {} rule will work. It does not currently include authentication for CDNs that require API keys, such as Adobe Fonts. However, that is something the team might add in the future.

\n\n\n\n

“For end-users, moving away from CDNs and locally hosting web fonts will improve performance (fewer handshake roundtrips for SSL), and is the privacy-conscious choice,” said Stathopoulos. “The only ‘valid privacy concern’ is that the web fonts’ CDN does not disclose information that is fundamental to the GDPR: what information gets logged, for how long these logs remain, how they are processed, if there is any cross-referencing with all the other wealth of information the company has from users, etc. The concern is a lack of disclosure and information. If a site owner doesn’t know what kind of information a third-party logs for its visitors, then they should ethically not enforce that on their visitors. With this package, the CDN is removed from the equation and the font still gets served fast — if not faster.”

\n\n\n\n

A Path to Core WordPress

\n\n\n\n

Today, there is now a broader focus on privacy concerns related to third-party resources, particularly with tech giants like Google. Such concerns extend to whether third parties are tracking users or collecting data. Additional concerns are around whether sites are disclosing the use of third-party resources, which may be required in some jurisdictions. Site owners who are often unable to work through the web of potential issues are stuck in the middle.

\n\n\n\n

Jono Alderson opened a ticket to create an API for loading web fonts locally in core WordPress in February 2019. It is a lengthy and detailed proposal, but it has yet to see much buy-in outside of a handful of developers.

\n\n\n\n

“If such a script is standardized and included in WordPress core, one of the main benefits would be more respect for the end-user’s privacy,” said Stathopoulos. “In the end, that’s all privacy is about: respecting users.”

\n\n\n\n

A standard API like Alderson proposes could solve some issues. Namely, it would virtually eliminate any privacy concerns. However, loading fonts locally could allow WordPress to optimize font loading and would create a shared system where plugins and themes do not load duplicate assets because of the current limitations of the enqueuing system. A standard API would also put the responsibility of efficiently loading fonts on WordPress’s shoulders instead of theme and plugin developers.

\n\n\n\n

The Themes Team’s new project is a solid start and strengthens the current proposal.

\n\n\n\n

“If we’re serious about WordPress becoming a fast, privacy-friendly platform, we can’t rely on theme developers to add and manage fonts without providing a framework to support them,” wrote Alderson in the ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 28 Sep 2020 20:58:48 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:40;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: Fuxia Scholz First to Pass 100K Reputation Points on WordPress Stack Exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105282\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange?utm_source=rss&utm_medium=rss&utm_campaign=fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5096:\"

Fuxia Scholz, a prolific WordPress Stack Exchange (WPSE) contributor, is the first member to reach 100,000 reputation points. The popular Q&A community site rewards expert advice by floating the highest quality answers to the top, allowing users to earn reputation points. The gamified help community has proven to be more motivating for developers than many traditional forums, since the upvotes communicate how useful their answers are to others.

\n\n\n\n
\n\n\n\n

Scholz started on Stack Overflow a few months before WordPress had its own site. She wrote around 50 answers and made connections with other WordPress developers ahead of the site’s beta phase in June 2010. Once the site graduated and got its own logo and design, Scholz started writing more.

\n\n\n\n

“One core idea for all Stack Exchange sites is gamification: You earn reputation, and you get access to certain privileges,” Scholz said.

\n\n\n\n

“You can say I got a bit addicted. My favorite questions were about problems for which I didn’t know the answer, and couldn’t find one with a search engine, because no one else had solved that before. I used my answers to teach myself, and I learned a lot this way! In May 2011 my reputation on WPSE was already higher than on Stack Overflow, and for the next years it went up in a steep curve.” Ten years after WPSE launched, Scholz has become the first to reach 100,000 reputation points.

\n\n\n\n

“What reputation and karma do is send a message that this is a community with norms, it’s not just a place to type words onto the internet. (That would be 4chan.)” Stack Overflow co-creator Joel Spolsky said. “We don’t really exist for the purpose of letting you exercise your freedom of speech. You can get your freedom of speech somewhere else. Our goal is to get the best answers to questions. All the voting makes it clear that we have standards, that some posts are better than others, and that the community itself has some norms about what’s good and bad that they express through the vote.”

\n\n\n\n

The reputation points were originally inspired by Reddit Karma. Spolsky admits that the points not a perfect system but they do tend to “drive a tremendous amount of good behavior.” Gamification can shape and encourage certain behaviors but Spolsky said it’s a weak force that cannot motivate people to do things they are not already interested in doing. For Scholz, it was the community aspect and an earned sense of ownership and responsibility that kept her hooked.

\n\n\n\n

“In 2012, the community elected me as a moderator, and that changed a lot,” she said. “Now it wasn’t just a game anymore, it was a duty. I felt responsible for the site. I still do. I also found some friends on there. We met at WordCamps and in private, and worked together on different projects.”

\n\n\n\n

Scholz no longer works in development and said she doesn’t care about WordPress anymore, but she is still a regular contributor on the WPSE.

\n\n\n\n

“I switched careers and work as a writer, translator, and community manager for Chess24.com now,” she said. “But I still care about the site WordPress Stack Exchange! I keep an eye on new tags, handle flagged posts and comments, try to make every new user feel welcome, and I search for people who are abusing the system — vote fraud and spam. And, very rarely, I even write an answer, because I still know all this stuff.

\n\n\n\n

“Checking the site has become a part of my daily routine, like feeding the cat.”

\n\n\n\n

This daily habit has snowballed into Scholz racking up more than 2,000 answers. She is getting upvotes on many of her old answers nearly every day, which is what pushed her over the 100k milestone.

\n\n\n\n

“There is a lot to say about the way our site developed over the years,” Scholz said. “I’m not happy about some things. The enthusiasm of the early days is gone. We don’t have enough regulars, there is no discussion about the site on WordPress Development Meta Stack Exchange, and our chat, once very active, funny, and friendly, is now almost dead.

\n\n\n\n

“Maybe that’s normal, I don’t know. But it’s still ‘my’ site. Reputation and badges don’t really mean anything for a long time now, but keeping the site working, useful and friendly is more important.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 26 Sep 2020 15:27:03 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:41;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:82:\"WPTavern: PhotoPress Plugin Seeks to Revolutionize Photography for WordPress Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=104770\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:209:\"https://wptavern.com/photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users?utm_source=rss&utm_medium=rss&utm_campaign=photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5638:\"

Peter Adams, the owner of the PhotoPress plugin, announced a couple of weeks ago that now is the time for his project to take center stage. “It’s Time for PhotoPress,” read the title of his post in which he laid out a four-phase plan for the future of his project.

\n\n\n\n

Adams is no stranger to manipulating WordPress to suit the needs of photographers. He described photography as his first love and second career. He initially found the art of taking photos in high school and set off to college to become a professional photographer in the early ’90s.

\n\n\n\n

As his university graduation loomed, he was recruited to run web development for an internet ad agency that built websites for Netscape, Bill Clinton’s White House, and dozens of Fortune 500 companies. He spent the next 15 years starting or running tech companies before returning to his roots as a photographer.

\n\n\n\n

Today, he photographs for various magazines and companies. And, that’s where his PhotoPress project comes in.

\n\n\n\n

“As far as WordPress has come, it is at risk of losing an entire generation of photographers to photo website services such as Photoshelter, SmugMug, Squarespace, and PhotoFolio,” he said. Adams wants to change that, making WordPress the go-to platform for photographers around the world.

\n\n\n\n

The Jetpack of Photography Plugins

\n\n\n\n

If you dig into the history of the PhotoPress plugin on WordPress.org, it seems to have a 15-year history. However, this is not the same plugin that was published a decade and a half ago by a different developer. The original plugin is now defunct, and Adams took over when the name was freed up on the directory.

\n\n\n\n

Adams wrote in his announcement post that WordPress has done a great job of delivering several media features over the years. “Yet despite that, there are still many rough edges and missing features that keep WordPress from being the first choice for a photographer that needs to publish a beautiful portfolio of their work, put their image catalog/archive online, or showcase a photo editorial/project.”

\n\n\n\n

He outlined a list of 10 specific problem areas that he wants to address in a “Jetpack-like” plugin for photographers. This is the bread and butter of the first of the planned four phases, which he said is about 80% finished. He had originally planned to develop PhotoPress as a series of separate plugins, each addressing a specific problem. Now, it is a single plugin with modules than can be enabled or disabled.

\n\n\n\n

When asked why the “right time” is now, Adams explained it is because the Gutenberg (block editor) project is a giant leap forward in usability in terms of creating photography blogs.

\n\n\n\nPhotoPress Gallery block in the editor.\n\n\n\n

“Photogs are a rare breed of non-technical users with high design sense,” he said. “Things that I used to have to teach photographers to do using shortcode syntax and custom CSS can now be simple controls with live feedback inside a Gutenberg block. It’s really a game-changer for getting people comfortable with customizing things like gallery styling — which is the number one thing photographers need to do.”

\n\n\n\n

The primary piece of the PhotoPress plugin is its custom PhotoPress Gallery block. It allows users to choose between a range of gallery styles, such as columns, masonry, justified, and mosaic. Each style has its own options. Images can also be launched into a slideshow when one is clicked.

\n\n\n\n

Based on some quick tests, the block’s front-end output will go farther with some themes than others. This is mainly because of conflicting CSS and issues which can be solved by testing against more themes.

\n\n\n\n

Aside from the block, the plugin can automatically extract image metadata and group that data through custom taxonomies, such as cameras, lenses, locations, keywords, and more. WordPress stores this information out of the box, but it is hidden away as post meta. The plugin uses the taxonomy system to make it manageable for end-users.

\n\n\n\n

Ultimately, Adams set out to create a photography plugin that fits in with the WordPress admin user interface and experience, which he has accomplished.

\n\n\n\n

The Future of PhotoPress

\n\n\n\n

The project is still a work in progress. Adams is still moving through Phase I of the four-phase plan. Once it is complete, he can move on to the next steps in the process.

\n\n\n\n

Phase II is to create themes that are designed specifically to work with the PhotoPress plugin. He has three planned thus far. One for handling portfolio sites. Another for creating a stock photo archive. And the last for photojournalism and exhibits. Each will be built on top of his photography theme framework.

\n\n\n\n

The themes in Phase II will likely be commercial products. Adams said he needs a way to fund the next phases of the project. He hopes to have this step underway by the end of the year.

\n\n\n\n

For 2021, he wants to begin tackling Phases III and IV. The former will be a website-as-a-service (WaaS) similar to WordPress.com but for photographers. It will begin as a paid project but could have some free options for emerging photographers and students. The final phase is to build an onboarding system.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 25 Sep 2020 19:08:15 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:42;a:6:{s:4:\"data\";s:13:\"\n\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"WPTavern: Google Officially Releases Its Web Stories for WordPress Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105227\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:191:\"https://wptavern.com/google-officially-releases-its-web-stories-for-wordpress-plugin?utm_source=rss&utm_medium=rss&utm_campaign=google-officially-releases-its-web-stories-for-wordpress-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5593:\"Web Stories for WordPress dashboard.\n\n\n\n

Two and a half months after the launch of its public beta, Google released its Web Stories for WordPress plugin. So far, the plugin has over 10,000 active installations and has garnered a solid five-star rating from four reviews.

\n\n\n\n

Google created the Web Stories format through its AMP Project to allow publishers to create visually-rich stories. It is primarily geared toward mobile site visitors, allowing them to quickly jump through story pages with small chunks of content.

\n\n\n\n

The Web Stories plugin creates a visual interface within WordPress for creating Stories. It breaks away from the traditional WordPress interface and introduces users to an almost Photoshop-like experience for building out individual Stories. The Stories editor is completely drag-and-drop.

\n\n\n\n

The plugin also offers eight predesigned templates out of the box that cover a small range of niches. However, according to Google’s announcement, the company plans to add more templates in future updates.

\n\n\n\n

Web Stories Are for Storytelling

\n\n\n\n

“Firstly…the power of Stories,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Stories are how we (humans) see the world and share our experiences. Up to now the platforms that we have to tell stories have been limited to books/films/tv/websites/blogs/instagram stories etc.”

\n\n\n\n

“Websites are ok for telling stories but in many ways the format doesn’t really fit the linear arc of storytelling. When Marshall McLuhan said ‘the medium is the message’ in 1964 he was talking about how the medium itself has a social impact, and change the communication itself…and the possibilities for what is communicated and how it is perceived. But we should keep coming back to Stories. Stories are the key here imo. Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Marsland finished his thread by saying that using Stories as a replacement for a brochure or website is a missed opportunity. He said that it was a platform for storytelling and should be used as such.

\n\n\n\n

It is far too early to tell if Web Stories will simply be a fad or still in wide use years from now. The technology certainly lends itself well to telling stories, particularly in mobile format, but I doubt we have seen the best of what is possible on the web. The format feels too limited to be the end-all-be-all of storytelling. It is merely one medium that will live and die by its popularity with users.

\n\n\n\n

With the right design skills, some people will craft beautiful Web Stories. And, that is just what Marsland has done with the first Story he shared:

\n\n\n\nPage from the Wilson and Pootle Web Story by Jamie Marsland.\n\n\n\n

I agree with his conclusion. Web Stories should be about storytelling. When you move outside of that zone, the technology feels out of place.

\n\n\n\n

Where I disagree is that websites are not ideal for storytelling. Ultimately, the WordPress block editor will allow artistic end-users to craft intricate stories, mixing content and design in ways that we have not seen. We are just now scratching the surface. I expect our community of developers to build more intricate tools than what the Web Stories plugin currently allows, and we can do so in a way that revolutionizes storytelling on the web.

\n\n\n\n

New Features

\n\n\n\nStory editor with Unsplash photo integration.\n\n\n\n

The Web Stories plugin now adds support for Unsplash images and Coverr videos out of the box. The plugin adds a new tab with a “media” icon. For users of the first beta version of the plugin, this may be a bit confusing. The previous media icon was for a tab that displayed the user’s media. Now, the user’s media is under the tab with the “upload” icon.

\n\n\n\n

It is also not immediately clear that the Unsplash images and Coverr videos are not hosted on the site itself. There is a “powered by” notice at the bottom of the tab, but it can be easy to miss because it blends in with the media in the background.

\n\n\n\n

Media from Unsplash and Coverr is hosted off-site and not downloaded to the user’s WordPress media library. I could find no mention of this in the plugin’s documentation. Such hotlinking was a cause for debate over the recent official release of the Unsplash plugin.

\n\n\n\n

Google also announced it planned to add more “stock media integrations” in the near future. According to a document shared via a GitHub ticket, such future integrations may include Google Photos and GIF-sharing site Tenor.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 21:13:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:43;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"WPTavern: W3C Drops WordPress from Consideration for Redesign, Narrows CMS Shortlist to Statamic and Craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105108\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft?utm_source=rss&utm_medium=rss&utm_campaign=w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:11563:\"

The World Wide Web Consortium (W3C), the international standards organization for the web, is redesigning its website and will soon be selecting a new CMS. Although WordPress is already used to manage W3C’s blog and news sections of the website, the organization is open to adopting a new CMS to meet its list of preferences and requirements.

\n\n\n\n

Studio 24, the digital agency selected for the redesign project, narrowed their consideration to three CMS candidates:

\n\n\n\n
  1. Statamic
  2. Craft CMS
  3. WordPress
\n\n\n\n

Studio 24 was aiming to finalize their recommendations in July but found that none of them complied with the W3C’s authoring tool accessibility guidelines. The CMS’s that were better at compliance with the guidelines were not as well suited to the other project requirements.

\n\n\n\n

In the most recent project update posted to the site, Studio 24 reported they have shortlisted two CMS platforms. Coralie Mercier, Head of Marketing and Communications at W3C, confirmed that these include Statamic and Craft CMS.

\n\n\n\n

WordPress was not submitted to the same review process as the Studio 24 team claims to have extensive experience working with it. In the summary of their concerns, Studio 24 cited Gutenberg, accessibility issues, and the fact that the Classic Editor plugin will stop being officially maintained on December 31st, 2021:

\n\n\n\n

First of all, we have concerns about the longevity of WordPress as we use it. WordPress released a new version of their editor in 2018: Gutenberg. We have already rejected the use of Gutenberg in the context of this project due to accessibility issues.

If we choose to do away with Gutenberg now, we cannot go back to it at a later date. This would amount to starting from scratch with the whole CMS setup and theming.

Gutenberg is the future of WordPress. The WordPress core development team keeps pushing it forward and wants to roll it out to all areas of the content management system (navigation, sidebar, options etc.) as opposed to limiting its use to the main content editor as is currently the case.

This means that if we want to use WordPress long term, we will need to circumvent Gutenberg and keep circumventing it for a long time and in more areas of the CMS as time goes by. 

\n\n\n\n

Another major factor in the decision to remove WordPress from consideration was that they found “no elegant solution to content localization and translation.”

\n\n\n\n

Studio 24 also expressed concerns that tools like ACF, Fewbricks, and other plugins might not being maintained for the Classic Editor experience “in the context of a widespread adoption of Gutenberg by users and developers.”

\n\n\n\n

“More generally, we think this push to expand Gutenberg is an indication of WordPress focusing on the requirements of their non-technical user base as opposed to their audience of web developers building custom solutions for their clients.”

\n\n\n\n

It seems that the digital agency W3C selected for the project is less optimistic about the future of Gutenberg and may not have reviewed recent improvements to the overall editing experience since 2018, including those related to accessibility.

\n\n\n\n

Accessibility consultant and WordPress contributor Joe Dolson recently gave an update on Gutenberg accessibility audit at WPCampus 2020 Online. He reported that while there are still challenges remaining, many issues raised in the audit have been addressed across the whole interface and 2/3 of them have been solved. “Overall accessibility of Gutenberg is vastly improved today over what it was at release,” Dolson said.

\n\n\n\n

Unfortunately, Studio 24 didn’t put WordPress through the same content creation and accessibility tests that it used for Statamic and Craft CMS. This may be because they had already planned to use a Classic Editor implementation and didn’t see the necessity of putting Gutenberg through the paces.

\n\n\n\n

These tests involved creating pages with “flexible components” which they referred to as “blocks of layout,” for things like titles, WYSIWYG text input, and videos. It also involved creating a template for news items where all the content input by the user would be displayed (without formatting).

\n\n\n\n

Gutenberg would lend itself well to these uses cases but was not formally tested with the other candidates, due to the team citing their “extensive experience” with WordPress. I would like to see the W3C team revisit Gutenberg for a fair shake against the proprietary CMS’s.

\n\n\n\n

W3C Is Prioritizing Accessibility Over Its Open Source Licensing Preferences

\n\n\n\n

The document outlining the CMS requirements for the project states that “W3C has a strong preference for an open-source license for the CMS platform” as well as “a CMS that is long-lived and easy to maintain.” This preference may be due to the economic benefits of using a stable, widely adopted CMS, or it may be inspired by the undeniable symbiosis between open source and open standards.

\n\n\n\n

“The industry has learned by experience that the only software-related standards to fully achieve [their] goals are those which not only permit but encourage open source implementations. Open source implementations are a quality and honesty check for any open standard that might be implemented in software…”

Open Source Initiative
\n\n\n\n

WordPress is the only one of the three original candidates to be distributed under an OSD-compliant license. (CMS code available on GitHub isn’t the same.)

\n\n\n\n

Using proprietary software to publish the open standards that underpin the web isn’t a good look. While proprietary software makers are certainly capable of implementing open standards, regardless of licensing, there are a myriad of benefits for open standards in the context of open source usage:

\n\n\n\n

“The community of participants working with OSS may promote open debate resulting in an increased recognition of the benefits of various solutions and such debate may accelerate the adoption of solutions that are popular among the OSS participants. These characteristics of OSS support evolution of robust solutions are often a significant boost to the market adoption of open standards, in addition to the customer-driven incentives for interoperability and open standards.”

International Journal of Software Engineering & Applications
\n\n\n\n

Although both Craft CMS and Statamic have their code bases available on GitHub, they share similarly restrictive licensing models. The Craft CMS contributing document states:

\n\n\n\n

Craft isn’t FOSS
Let’s get one thing out of the way: Craft CMS is proprietary software. Everything in this repo, including community-contributed code, is the property of Pixel & Tonic.

That comes with some limitations on what you can do with the code:

– You can’t change anything related to licensing, purchasing, edition/feature-targeting, or anything else that could mess with our alcohol budget.
– You can’t publicly maintain a long-term fork of Craft. There is only One True Craft.

\n\n\n\n

Statamic’s contributing docs have similar restrictions:

\n\n\n\n

Statamic is not Free Open Source Software. It is proprietary. Everything in this and our other repos on Github — including community-contributed code — is the property of Wilderborn. For that reason there are a few limitations on how you can use the code:

\n\n\n\n

Projects with this kind of restrictive licensing often fail to attract much contribution or adoption, because the freedoms are not clear.

\n\n\n\n

In a GitHub issue requesting Craft CMS go open source, Craft founder and CEO Brandon Kelly said, “Craft isn’t closed source – all the source code is right here on GitHub,” and claims the license is relatively unrestrictive as far as proprietary software goes, that contributing functions in a similar way to FOSS projects. This rationale is not convincing enough for some developers commenting on the thread.

\n\n\n\n

“I am a little hesitant to recommend Craft with a custom open source license,” Frank Anderson said. “Even if this was a MIT+ license that added the license and payment, much like React used to have. I am hesitant because the standard open source licenses have been tested.”

\n\n\n\n

When asked about the licensing concerns of Studio 24 narrowing its candidates to two proprietary software options, Coralie Mercier told me, “we are prioritizing accessibility.” A recent project update also reports that both CMS suppliers W3C is reviewing “have engaged positively with authoring tool accessibility needs and have made progress in this area.”

\n\n\n\n

Even if you have cooperative teams at proprietary CMS’s that are working on accessibility improvements as the result of this high profile client, it cannot compare to the massive community of contributors that OSD-compliant licensing enables.

\n\n\n\n

It’s unfortunate that the state of open source CMS accessibility has forced the organization to narrow its selections to proprietary software options for its first redesign in more than a decade.

\n\n\n\n

Open standards go hand in hand with open source. There is a mutually beneficial connection between the two that has caused the web to flourish. I don’t see using a proprietary CMS as an extension of W3C values, and it’s not clear how much more benefit to accessibility the proprietary options offer in comparison. W3C may be neutral on licensing debates, but in the spirit of openness, I think the organization should adopt an open source CMS, even if it is not WordPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 20:13:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:44;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:79:\"WPTavern: First Look at Twenty Twenty-One, WordPress’s Upcoming Default Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105166\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme?utm_source=rss&utm_medium=rss&utm_campaign=first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6907:\"

Fashion is ephemeral. Art is eternal. Indeed what is a fashion really? A fashion is merely a form of ugliness so absolutely unbearable that we have to alter it every six months!

\n\n\n\n

Thus wrote Oscar Wilde on Victorian-era fashion in an article titled “The Philosophy of Dress” for the New-York Tribune in 1885.

\n\n\n\n

In many ways, WordPress theming is the same as the ever-changing landscape of fashion. Rounded corners are in one day and out the next. Box shadows are in one year after being frowned up just months earlier. Perhaps web design is so intolerable that we must change it every six months. Or, at least freshen it up every year in the case of WordPress.

\n\n\n\n

If art is eternal, there are only two default, Twenty* themes that I can truly recall from past years: Twenty Ten and Twenty Fourteen — yes, Twenty Twenty is memorable, but it is also still the current default. Twenty Ten was a classic that paid homage to WordPress’s past. Twenty Fourteen was such a leap away from tradition that it is hard to forget. Everything else has seemed to fade to varying degrees.

\n\n\n\n

With WordPress 5.6 and the end of the year looming, it is time to look forward to the latest trend. As Mel Choyce-Dwan noted in the announcement of Twenty Twenty-One, the next default theme, “Pastels and muted colors are pretty in right now.”

\n\n\n\n

She is not wrong. The colors are a refreshing change of pace. Now that we are into the second day of autumn, I am getting the good kind of vibes from some of the more earthy-tones from a couple of the color palettes expected to ship with the theme.

\n\n\n\nPotential color palette options for Twenty Twenty-One.\n\n\n\n

Whether Twenty Twenty-One will be a fashionable theme for the year or art that we can remember a decade from now, only history will be able to judge. For now, let’s enjoy the creation and take a look at what we should expect from the next default WordPress theme.

\n\n\n\n

The Current Twenty Twenty-One

\n\n\n\n

The new default theme is a fork of Automattic’s Seedlet, a project in which I lauded as the next step in the evolution of theming. It is a theme that is focused on WordPress’s future of being completely comprised of blocks. It gives us an ideal insight into where theme development is heading. It makes sense as the foundation for the new default. Few other themes would make for a good starting point right now. With WordPress theme development in flux, Seedlet is simply ahead of the pack in terms of foundational elements.

\n\n\n\nSeedlet WordPress theme screenshot.\n\n\n\n

“This provides us with a thorough system of nested CSS variables to make child theming easier, and to help integrate with the global styles functionality that’s under development for full-site editing,” wrote Choyce-Dwan of using Seedlet as a starting point.

\n\n\n\n

There are no plans to spin up a Google Web Font for this theme. The design team is going native and sticking with the default system font stack. Choyce-Dwan listed several reasons for the choice, such as keeping a neutral font that allows broad use, speed, and customizability via a child theme.

\n\n\n\n

Despite the neutral font, the default pastel green is a fairly opinionated design decision. It will not be used broadly across industries. However, the team plans to create multiple color palettes that will give it more range. Presumably, these palettes can also be overwritten.

\n\n\n\nPastel green color scheme on single post view.\n\n\n\n

Other than the colors, the design is relatively simple. Choyce-Dwan said that the theme’s block patterns support is where it will be truly unique.

\n\n\n\n

I was initially unhappy with the patterns that were going to ship with WordPress 5.5. However, an 11th-hour update improved the situation so that they did not feel entirely experimental. The foundational Seedlet theme for Twenty Twenty-One has some unique patterns that begin to scratch the surface of what’s possible with this WordPress feature. My hope is that the new default theme steps it up a notch.

\n\n\n\n

Currently, the theme does not register any custom patterns. However, it has a placeholder file and a note that they are a work in progress. Choyce-Dwan shared some patterns the team has already designed in the announcement.

\n\n\n\nCurrently-designed block patterns.\n\n\n\n

“We’ll be relying on our talented community designers for more ideas,” she wrote. The team has also created a GitHub template for anyone to contribute pattern design ideas.

\n\n\n\n

Currently, the theme does not support the upcoming full-site editing feature of WordPress. After the Beta 1 release of WordPress 5.6, the team plans to begin exploring the addition of this support. WordPress is expected to ship a public beta of full-site editing in its next major release, but it is unclear whether it will be far enough along to be a headline feature for the Twenty Twenty-One theme.

\n\n\n\n

The team and volunteers have less than a month before the October 20th deadline for committing the new theme to trunk, the core WordPress development branch. At that stage, the theme should be nearly complete and ready for production. Of course, there will be several rounds of patches, bug fixes, and updates before WordPress 5.6 lands in December. Right now is the best time for anyone who wants to get involved with Twenty Twenty-One to do so.

\n\n\n\n

Useful links with more information:

\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 20:01:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:45;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:37:\"HeroPress: Hello World – Hevo Nyika\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=3308\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:176:\"https://heropress.com/essays/hello-world-discovering-the-world-through-wordpress/#utm_source=rss&utm_medium=rss&utm_campaign=hello-world-discovering-the-world-through-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14438:\"\"Pull

Unokwanisa kuverenga rondedzero iyi muChiShona

\n

So I chose a career in Web Development!!

\n

To be honest it’s kind of funny when I think about it and quite surreal to be here talking about my story. It has been a journey and I would like to share my story with you.

\n

I have been lucky in the Dad department. My Dad encouraged me to work hard and dream big from a very young age. I remember occasionally having ‘when I grow up’ talks.

\n

For quite some time I wanted to be a Judge, however awesome this dream sounds it was not very inspired. After binge-watching Judge Judy for a whole weekend, I started calling myself Judge Thelma. Though I don’t remember much of this my sister says that I used to say I would arrest all the men in the World if I ever became a Judge. HAHAHA! (clearly I didn’t understand how the World works)

\n

I did not understand what being a Judge meant or what was required for me to start banging that gavel to my heart’s desire. Eventually, I learnt that I had to become a lawyer first then magistrate before I could be nominated to be a Judge and let us just say that is how I sentenced that dream to a lifetime down the drain.

\n

See what I did there? hahaha!

\nWith Daddy Dearest\n

A few years later, I was in High School and that is when I decided to pursue a career in Computer Science. I did not know what I would be doing or how I would get there but I just knew that I was going to pursue a career in ICT. I wrote my first line of code when I was 16 years old.

\n

This was after I had joined the school’s computer class, initially, I thought I would be learning about Excel Sheets and Word Documents until I was assigned to write my first program in C (talk about a double-take!!). It was not easy but it was very exciting, l remember writing up simple code for a Video Club – a simple check-in/out for VHS tapes and CDs. Dear World, thus began my fascination with computers.

\n

Seven years later, I was now in university studying ICT as I had always wanted. I was doing a Bachelors in Business Management & Information Technology. In my third year, I was interning at a local Webdesign and hosting company. This was never my plan, I only took on that job after I had failed to get a job with local banks or telecommunications companies. Before I was introduced to Website Design I envisioned myself suiting up and working in IT Audit or offering IT support. Even though things did not go as I had planned, I am glad they did not exactly go my way in that aspect. So in 2017, I was designing websites using HTML, CSS, PHP, JavaScripts and Joomla which was the prefered content management system at that company. I knew about WordPress but I was not using it for anything. People have this misconception that WordPress is not for real developers and it is not secure and at that time I was one of those people.

\n

Finding my tribe

\n

One day when I was working at the front desk Thabo Tswana came to give a colleague of mine a purple WooCommerce pen. I did not know what WooCommerce was at that time but I was taken by the purple shirt and pen he was carrying. I asked him about it and he explained what WooCommerce was and that what he was carrying was called ‘swag’. So the love of freebies led me to the WordCamp Harare website, instead of buying a ticket I applied to volunteer. I learnt more about WordPress, I was a volunteer, without any knowledge on WordPress.org or WordPress.com. I only started using WordPress because of the awesome people that l had met at that Wordcamp.

\n

Everyone was so welcoming, a week later with help from Thabo I designed my first ever WP website.

\n

Soon after I was part of the community and a bit more involved in the meetups. We had our first-ever Women Who WordPress meetup in 2018. So many ladies came on board bloggers and developers alike. We were free to talk and discuss a lot of things. We had more time to discuss the difference between WordPress.com and WordPress.org we shared views on how to handle discrimination at work, how to promote your website and a whole lot of other things.

\n

\n

Establishing roots

\n

In 2018, Harare had its first-ever female Lead Organiser Tapiwanashe Manhobo whoop whoop! I was also part of the organising team that year, I was assigned to handle Harare’s first Kids Camp. The planning process was stressful because the economic crisis in Zimbabwe was getting worse, luckily we had over 8 months to plan and with help from sponsors, we managed to pull through. In the end, everything turned out great. I wrote an article about the Kids Camp here.

\n

After the first Kids Camp, we had several WordPressors that were enthusiasts about encouraging kids to embrace ICT. In 2019 we had not planned to have a Kids Camp because of financial constraints but to our surprise, we had some anonymous donations and we managed to have a WordPress Community outreach to a youth centre a week after our WordCamp. We had the outreach at the Centre for Total Transformation which is a non-formal school that caters for underprivileged and vulnerable children. We taught them about WordPress, Computer Hardware and Software.

\n

Here is a small video I took with Ellen when we were about to leave. Did l mention that I am terrible on camera? hahaha!

\n\n

Kids Camp 2019 – Centre for Total Transformation

\n

I have fallen deeply for WordPress because of the Community, I enjoy attending WordCamps, meeting new people and just learning new stuff. I have a huge list of WordCamps I need to attend before l kick the bucket, hopefully. Last year I managed to cross WordCamp

\n

Johannesburg off my bucket list. This year I was going to attend WordCamp Capetown but unfortunately, 2020 had other plans for the whole world. Anyway when everything is back to normal my plan to travel to WordCamps will proceed. (fingers crossed)

\n

Reaping Fruits

\n

Meanwhile, my plan to improve my developing skills has not been on hold. Even though I can still cook up code in C and Java, for now, I have also included WordPress PHP functions to the mix. It was not easy to get to this point, daring myself got me to this slightly better stage. My IQ is not way up there, however, I try to do my best where I can and I am happy to say it has paid off so far.

\n

Around November last year, I was designing as a freelancer while job hunting. Out of the blue l got a call for a job offer from Trust Nhokovenzo who is big on Digital marketing and also part of the WordPress Community. He had asked someone in the community about developers and my name happened to come up. So since February, I have been part of his team at Calmlock Digital Marketing Agency.

\n

There is so much more in the world of WordPress that l am yet to tap into so even though I am ending my write up here, for now, my story is going to continue …

\n

Until next time…

\n

Hevo Nyika

\n

Saka ini ndakasarudza kugadzira mawebhusayiti.

\n

Ndakaita rombo rakanaka pana baba vandakapihwa naMwari. Baba vangu vaindikurudzira kuti ndishande nesimba. Ndinoyeuka pano neapo tichiita hurukuro dzedu dzekuti ‘kana ndakura ndoda kuveyi’.

\n

Kwenguva yakati rebei ndaida kuve Mutongi. Kunyangwe ini ndisingazvirangariri mukoma wangu anotaura kuti ndaiti ndaizosunga varume vese vari pasi rino kana ndikangoita mutongi HAHAHA zveshuwa handaiziva kuti mitemo yenyika inofambiswa seyi.
\nNdanga ndisinga nzwisisi kuti kuva mutongi kwairevei kana zvaidikanwa kwandiri kuti nditange kurova iro ghavheu kuchishuwo chemoyo wangu. Pakupedzisira, ndakadzidza kuti ndaifanirwa kuzoita gweta ipapo magistrate ndisati ndasarudzwa kuita Mutongi naizvozvo ndokupera kwakaita chiroto chekuva Mutongi.

\nNa Baba Vangu\n

Gare gare papfura makore mashoma pandakanga ndave kuHigh School ndakanga ndakuda kuita basa rema kombiyuta. Ndakanyora mutsara wekutanga wekodhi pandaive nemakore gumi nematanhatu. Izvi zvakaitika mushure mekunge ndapinda mukirasi yemakombiyuta, pakutanga ndaifunga kuti ndinenge ndichidzidza nezveExcel Sheets neWord zvisineyi ndakaona ndakunyora kodhi yangu yekutanga muC. Zvaisave nyore kunyora kodhi asi zvainakidza kwazvo, ndorangarira ndichinyora kodhi yeVhidhiyo Kirabhu.

\n

Makore manomwe apfura, ndakanga ndava kuyunivhesiti ndichidzidza ICT zvandakagara ndakaronga. Ndaiita Bachelors muBusiness Management & Information Technology. Mugore rangu rechitatu ndainge ndave kushanda kune imwe kambani yaita zvekugadzira mawebhusaiti. Ndakawana basa iri mushure mekunge ndatadza kuwana basa kumabhanga. Kunyangwe hazvo zvinhu zvisina kuenda sezvandaive ndakaronga, ndinofara kuti hazvina kunyatso enda nenzira yangu. Saka muna 2017 ndaigadzira mawebhusaiti ndichishandisa HTML, CSS, PHP, JavaScript uye Joomla iyo yaive iyo inokurudzirwa kukambani kwandaive. Panguva iyi ndaiziva nezve WordPress asi ndakanga ndisingaishandisi.

\n

Kuwanana neWordPress

\n

Rimwe zuva pandakanga ndichishanda ndakaona Thabo Tswana akauya kuzopa mumwe mukomana wandayishanda naye chinyoreso cheWooCommerce. Ndakanga ndisingazive kuti WooCommerce yaive chii asi ndakafarira chinyoreso nehembe ye WooCommerce yaanga akapfeka. Ndakamubvunza nezvazvo akatsanangura kuti WooCommerce yaive chii. Saka nekudawo zvakanaka, zvemahara ndakaenda pawebhusaiti yeWordCamp Harare ndikabata zvimbo zvegore iroro. Ndakazvipira kubatsirawo vamwe vekuWordPress kuWordCamp Harare. Nerubatsiro kubva kunaThabo ndakagadzira webhusaiti yangu yekutanga yeWordPress vhiki rakatevera .

\n

Mushure mekunge ndaitawo chipato cheavo vanoshandisa WordPress ndakanga ndakuenda kumisangano yeWordPress yaitwa muHarare. Takaita musangano wevakadzi chete muna 2018. Vakadzi vazhinji vakauya kumusangano uyu. Tainga takasununguka kukurukura zvinhu zvakawanda. Takakurukura pamusoro pemutsauko uripo pakati peWordPress.com neWordPress.org takagovana maonero ekugadzirisa rusarura kubasa nezvimwewo.

\n

\n

Nguva yandakatanga kushandisa WordPress

\n

Muna 2018, kurongwa kweWordCamp Harare kwakatungamirwa kekutanga nemusikana ainzi Tapiwanashe Manhobo (waiva mufaro mukuru). Ndakanga ndiri mumwe wevairongawo naye. Hurongwa hwekuronga WordCamp Harare mugore iri hwainetsa pamusaka pekuoma kwehupfumi wemuZimbabwe, zvisineyi takaita rombo rakanaka nokuti takawana rubatsiro kubva kunevamwewo vanhu vakatiwedzera mari. Pakupedzisira, zvese zvakabudirira zvakanaka. Takarongawo WordCamp yevana varipasi pemakore gumi nechishanu, munokwanisa kuverenga pamusoro pezuva iri pawebhisaiti yangu apa.

\n

Mushure mekuita WordCamp yevana, takave nevamwe vanhu veWordPress aifarira kukurudzira vana kuti vagamuchire ICT. Muna 2019 takanga tisina kuronga kuve neWordCamo yeVana nekuda kwezvimhingamupinyi zvemari asi chakatishamisa ndechekuti takawana mari kubvawo kune vamwe. Takaita Camp iyi paCentre for Total Transformation chinova chikoro chisiri chepamutemo chinodzidzisa vana vanotambura. Tadzidzisa vana ava pamusoro peWordPress, Computer Hardware uye Software.

\n\n

Ndofarira WordPress zvakanyanya nekuda kweavo varimu nharaunda yacho, ini ndinonakidzwa nekuenda kumaWordCampi, kusangana nevanhu vatsva uye kungo dzidza zvinhu zvitsva. Gore rakapera ndakakwanisa kuyambuka muganhu weZimbabwe ndichienda kuWordCamp Johannesburg, dai pasina kuti 2020 nyika dzepasi rino dzakawirwa nedenda reCOVID 19 zvimwe ndingadayi ndakaenda kuWordCamp Capetown. Zvisinei hazvo kana denda ranani zvimwe ndichakwanisa kufamba ndichienda kumaWordCamp edzimwe nyika.

\n

Kukowa zvandakadyara

\n

Zvichakadaro, chirongwa changu chekuvandudza hunyanzvi hwangu hachina kumira. Kunyangwe ini ndichiri kukwanisa kubika kodhi muC uye Java, ikozvino, ndasanganisirawo WordPress PHP. Zvaive zvisiri nyore kusvika apa, zvakatora kuzvishingisa nekushanda nesimba. Ndinofara mwari aiva neni pamufambo wangu uyu.

\n

Muna Mbudzi gore rakapera, ndaive ndichigadzira mawebhusayiti apo nditsvaga basa. Pasina nguva ndakataura naTrust Nhokovenzo uyo akaandipa basa mukambani yake, kambani iyi inonzi Calmlock Digital Marketing Agency.

\n

Pane zvimwe zvakawanda kuWordPress zvandisati ndapinda mazviri. Nhaizvozvo kunyangwe ndiri kupedzisa kunyora kwangu apa, nyaya yehupenyu wangu ichaenderera mberi…

\n

Kusvikira nguva inotevera …

\n

…. tsvaga chinangwa chako, chiite mushe mushe ..

\n

The post Hello World – Hevo Nyika appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 06:00:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Thelma Mutete\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:46;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: WordPress Contributors Debate Dashboard Notice for Upcoming Facebook oEmbed Provider Removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105132\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5885:\"

WordPress contributors are discussing different strategies for responding to Facebook and Instagram dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers. When a user attempts to embed content by pasting a URL as they have in the past, they may not understand why it no longer works. They may assume that WordPress broke embeds, causing an increase in the support burden for this change.

\n\n\n\n

A few participants on the trac ticket for this issue have suggested WordPress detect users who will be impacted and attempt to warn them with a notice.

\n\n\n\n

“Since this may impact users unknowingly, it is possible to push a dashboard notice to users who have Facebook/Instagram embeds in their content, showing for site admins, as a one-off that can be dismissed,” Marius Jensen said.

\n\n\n\n

“We’ve previously done post-update-processing to clean up comments, so the idea of looking over content for an embed isn’t completely outlandish, and would help with those who don’t follow WordPress’ usual channels to learn of this.”

\n\n\n\n

Others don’t see the necessity. “Why should we make exception here?” Milan Dinić said. “It’s not the first time oEmbed support was discontinued for a provider, and I don’t remember anything specific was done then.”

\n\n\n\n

There is still some uncertainty about what will happen with existing oEmbeds after Facebook updates its API. During a recent core developer meeting, Helen Helen Hou-Sandí confirmed that WordPress does not clear oEmbed caches regularly. “Technically oEmbed caches are cleared if you save and a valid response is returned, we do not do cron-based garbage collection,” Hou-Sandí said.

\n\n\n\n

In a post today on the core development blog, Jake Spurlock assured users and developers that the existing embeds added before Facebook’s API change should still work:

\n\n\n\n

Because oEmbed responses are cached in the database using the hidden oembed_cache post type, any embed added prior to the October 24th deadline will be preserved past the deprecation date. These posts are not purged by default in WordPress Core, so the contents of the embed will persist unless manually deleted.

\n\n\n\n

Marius Jensen cautioned that there is still the possibility that existing embeds may not work going, depending on what Facebook does.

\n\n\n\n

“We don’t know how they plan on implementing the use of unauthorized embed attempts,” Jensen said. “It could not return an embed code and your link would remain a plain link, or maybe they decide to return some kind of embedded ‘unauthorized’ content. I don’t think anyone has heard any specifics on how Facebook plans on doing this, so we’re all just kinda waiting to either hear more, or see what happens.”

\n\n\n\n

Jensen said WordPress doesn’t re-check the cached results except when something changes with the post, but there may be plugins that clean up temporary data that may create an unpredictable outcome.

\n\n\n\n

“The reliability of the caches are hard to determine (and being caches, it’s sort of in the term that it’s not guaranteed to always be there, but rather fetched and saved for a while when needed),” Jensen said.

\n\n\n\n

Ideally WordPress’ oEmbed caches will prevent millions of embeds from breaking, but it’s still unknown how Facebook and third party plugins could change things.

\n\n\n\n

Coming off a rocky 5.5 core update that deprecated jQuery Migrate and flooded official support forums with reports of broken sites, some contributors are wary of having another situation where users are left in the dark.

\n\n\n\n

“I think a dashboard notice is desirable,” Jon Brown said. “Otherwise we’re not preemptively warning people in a way they can prepare and transition to another solution. We’re letting them know the same instant it’s going to break (when editing a specific post). I don’t think we can safely assume cached data is going to persist forever either, plenty of routines out there purge transient data before its stated expiration date.

\n\n\n\n

“I see this as potentially being similar to the problems seen in dropping JQM. It’ll cause avoidable and silent breakage client side without even any error logging for a site developer to pick up on. In hindsight, what ideally would have happened with JQM would have been incorporating the detection code from Enable jQuery Migrate Helper into core temporarily, or simply installing that plugin automatically on behalf of users.”

\n\n\n\n

Brown suggested WordPress detect calls to the cached embeds and warn users before the calls have the chance to fail so they can consider enabling a plugin to keep their embeds working more reliably.

\n\n\n\n

The discussion remains open in the make.wordpress.org/core post and the corresponding trac ticket. Spurlock said WordPress will likely remove Facebook and Instagram oEmbed providers in the upcoming 5.6 release (scheduled for December 8) but it could also be shipped in a 5.x minor release that happens after October 24.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 04:28:56 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:47;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Gutenberg Hub Launches Landing Page Templates Directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105009\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:175:\"https://wptavern.com/gutenberg-hub-launches-landing-page-templates-directory?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-hub-launches-landing-page-templates-directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7657:\"\n\n\n\n

Munir Kamal has created copy-and-paste blocks. He has built sections or “patterns” from those blocks. He has created a plugin that allows users to completely customize the two features via block options. Yesterday, he released an initial offering of 22 landing page templates that build upon his earlier work.

\n\n\n\n

Gutenberg Hub can almost be called his magnum opus, at least at this stage of his career. It is a continually growing library of free tools for WordPress’s block editor.

\n\n\n\n

Like previous projects, Gutenberg Hub’s landing templates require the EditorPlus plugin. This plugin is essentially a suite of design controls for the core WordPress blocks. The templates make use of these options by default. Given the limitations of the block editor’s current design controls, the use of such a plugin is necessary. Otherwise, there would be few other ways to realistically create a template system like this.

\n\n\n\n

Currently, users must copy the block code — via a convenient “copy” button — from the Gutenberg Hub website and then paste it in the editor. It is not an ideal situation, and I have been asking Kamal whether he would consider building a template inserter for months now.

\n\n\n\n

This time around, he preemptively said, “And, by the way, I am already working on adding a Template Inserter in my EditorPlus plugin. That will allow users to browse and insert these templates directly from Gutenberg without leaving the website.”

\n\n\n\n

He knew the question was coming. No need for me to ask again. He was unable to share a current screenshot of what the inserter looks like, but he is asking for feedback on what people expect of the user experience and interface.

\n\n\n\n

“Earlier, I created a template inserter similar to other blocks plugins, but later I changed my mind and thought that I should integrate with the Gutenberg Patterns API and load the templates into the ‘patterns’ panel in the block inserter,” he said. “But, I am having a few issues and thinking about going back to the original idea to have a Templates button on the top toolbar that opens a popup window to browse and filter templates that users can insert on a click.”

\n\n\n\n

For now, it is still early. However, at least it is on the long-term roadmap and being worked on.

\n\n\n\n

The Landing Page Templates

\n\n\n\nTesting the photography template (with minor adjustments).\n\n\n\n

At the moment, Gutenberg Hub offers 22 landing page templates. The “page” terminology may not mean “full page.” It simply depends on the active theme. Some themes have an open-canvas type of template that allows users to create the entire page via the editor. However, that is not a common feature, so these page templates will be confined to the post content area in most cases.

\n\n\n\n

The templates also work better with themes that have at least a full-width or no-sidebar option. End-users will want a lot of breathing room to use the templates and tinker with their designs.

\n\n\n\n

Kamal has built templates that stretch across a variety of industries. From restaurants to gyms to education to fashion, there is a lot to choose from right now. He promises more are on the way and at least a 23rd template in the next few days.

\n\n\n\n

“For the niches, I did some research from the top WordPress and HTML marketplaces and found the following most common or popular niches,” he said. “I think I will stick with these niches unless I get some more recommendations.”

\n\n\n\n

In comparison, Redux Templates offers access to over 1,000 sections and templates. Of course, there are trade-offs, such as some of those being commercial and the plugin typically requiring other third-party plugins. While quantity is not the only thing to look at, it proves there are miles of landscape that Gutenberg Hub’s templates have not yet explored. But, it is merely the beginning.

\n\n\n\n

Gutenberg Hub’s full-page templates are not quite as plug-and-play as its blocks and section templates. This is not so much a fault from the developer’s end. It is an issue of the platform, which is constantly being updated, and the range of support from current themes. End-users will start seeing some of the current limitations of the system when a layout does not quite look right with one theme but does with another. Or, if their theme has not been updated to support a new feature, such as the Social Links block, the typical horizontal menu design will likely be a normal vertical list of links instead.

\n\n\n\n

These are not insurmountable issues. Gutenberg and themes need more time to mature before projects like Gutenberg Hub’s landing templates are perfect or at least as close to perfect as can be expected.

\n\n\n\n

There are some things that Gutenberg Hub could improve with its templates. With several that I tested, I needed to switch specific blocks to be full width. This should be set up as the default with templates that are clearly meant to be full width in the example screenshots available on the site. It is a minor issue, but correcting this in the editor fixed several layout issues I was having when using the templates.

\n\n\n\n

Monetization Plans

\n\n\n\n

The second question that Kamal has not been prepared to answer fully over the past several months is how he will monetize Gutenberg Hub. Eventually, developers need some return on their investment when building tons of free tools. Many would do it all for free as long as their bills somehow got paid, but the reality is that there will come a tipping point where their projects need funding for long-haul maintenance.

\n\n\n\n

Kamal said he has laid the groundwork for funding but has not finalized anything yet. Currently, he is working on three ideas:

\n\n\n\n
  • Creating a pro version of his EditorPlus plugin.
  • Offering premium templates and blocks but is looking for a talented designer to work with.
  • Using ads specific to Gutenberg users, but he is not a fan of going this route or ads in general.
\n\n\n\n

He is open to feedback on how to best monetize the website and its projects. However, he said he is unwilling to compromise on giving away current and future free templates and tools.

\n\n\n\n

Future Gutenberg Projects

\n\n\n\n

Kamal said he does not have any new Gutenberg-related projects in the pipeline. The current plan is to work on what he has already created, which is a large ecosystem of Gutenberg tools that somehow work together.

\n\n\n\n

Outside of blocks, templates, and plugins, he is beginning to write more free tutorials on the Gutenberg Hub blog and focusing on creating videos around the project, including a new tutorial series for beginners.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 22 Sep 2020 21:05:19 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:48;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"WPTavern: WordPress Mobile Engineers Propose Dual Licensing Gutenberg under GPL v2.0 and MPL v2.0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105025\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:239:\"https://wptavern.com/wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6556:\"

During a Q&A session at WordCamp Europe 2020 online, Matt Mullenweg mentioned that Gutenberg contributors were considering dual licensing for embedding Gutenberg in mobile apps, along with the requirement that they would need to get an agreement from all contributors. WordPress mobile engineer Maxime Biais has just published a proposal for discussion, recommending dual licensing the editor under GPL v2.0 and MPL v2.0.

\n\n\n\n

“The GPL v2.0 license is a blocker for distributing the Gutenberg library in proprietary mobile apps,” Biais said in the corresponding GitHub issue. “Currently the only known users of Gutenberg on mobile are the WordPress mobile apps which are under GPL v2.0 (WordPress for AndroidWordPress for iOS). Mobile apps under the GPL v2.0 are not common and this limits Gutenberg usage in many apps.

\n\n\n\n

“Rich text editor libraries in the mobile space are lacking. There is no well known open source rich text editor for Android or iOS. We believe that Gutenberg could be a key library for many mobile apps, but that will never happen with the GPL v2.”

\n\n\n\n

Mobile app developers are limited by the GPL, because it requires the entire app to be distributed under the same license. The team is proposing dual licensing under MPL v2.0, a weaker copyleft license that is often considered to be more “business-friendly.” It allows users to combine the software with proprietary code. MPL v2.0 requires the source code for any changes to be available under the MPL, ensuring improvements are shared back to the community. The rest of the app can be distributed under any terms with the MPL v2.0 code included as part of a “larger work.”

\n\n\n\n

“The idea here is to keep some of the WordPress-specific modules under the GPL v2.0 only; some of them are not needed and not relevant for using Gutenberg in another software. Ideally, there would be a different way of bundling the project for being used in WordPress or in a non-GPL software,” Biais said.

\n\n\n\n

The GitHub ticket has several comments from developers who hope to be able to use the editor in their own projects. Radek Pietruszewski, tech lead for a collaborative todo app called Nozbe Teams, has been requesting a relicensing of Gutenberg since October 2019.

\n\n\n\n

“Our tech stack is essentially React on web and React Native on iOS and Android,” Pietruszewski said. “We’re a tiny company, and so we share >80% of app’s codebase between these 3 platforms.

\n\n\n\n

“Our app sorely lacks a WYSIWYG editor. We had a working implementation on web, but we decided to scrap it, because there was no way to port it on iOS and Android. There are pretty much no viable rich text editors for iOS or Android, yet alone both. But even then, shipping three completely separate, but somehow compatible editors would be a vast amount of work.”

\n\n\n\n

When Peitruszewski originally made his case to the mobile team, he identified Gutenberg/Aztec as a basic infrastructure that has the potential to enable many different apps:

\n\n\n\n

And that infrastructure is sorely lacking. There are very few rich text editor libraries on both iOS and Android — and most of them suck. And if you want an editor that has a shared API for both platforms… you’re stuck. There are no options – Gutenberg is the only game in town (and it’s really good).

And it’s very hard to create this infrastructure. WYSIWYG editors are very hard, and it takes entire teams years to develop them (and they still usually suck). Almost no-one has the resources to develop it just for themselves, and if they do, they’re unwilling to open-source it.

\n\n\n\n

Automattic’s mobile app engineers have struggled to get regular contributions to the apps, despite them being open source. Dual licensing Gutenberg could open up a new world of contributors with the editor being used more widely across the industry.

\n\n\n\n

“While we might not be big enough to be able to tackle a challenge of developing a rich text editor from scratch, we’re big enough to contribute features and bug fixes to open source projects,” Pietruszewski said.

\n\n\n\n

Matt Mullenweg was the first comment on Biais’ post in favor of the change:

\n\n\n\n

I think Gutenberg has a chance to become a cross-CMS standard, giving users a familiar interface any place they currently have a rich text box. There are hundreds and hundreds of engineers at other companies solving similar problems in a proprietary way, it would be amazing to get them working together but a huge barrier now is supporting Gutenberg in mobile apps, which every modern web service or CMS has. (Hypothetically, think of Mailchimp as a possible consumer and collaborator here, but it could be any company, SaaS, or other open source CMS.)

\n\n\n\n

Unless any major blockers come up in further discussion, this dual licensing change appears to be on track to move forward. Biais noted that a similar license change has already happened on Aztec-Android and Aztec-iOS. The last hurdle is gaining the approval of all the original code contributors or rewriting the code for those who decline to give approval.

\n\n\n\n

Once Gutenberg can be used under the MPL v2.0, the editor will gain a broader reach, with people already on deck wanting to use it. Other companies and projects that are normally outside WordPress’ open source orbit will also have the opportunity to enrich Gutenberg’s ecosystem with contributions back to the project. At the same time, the MPL 2.0 protects Gutenberg from companies that would try to re-release the code as a closed-source project.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 22:59:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:49;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:124:\"WPTavern: GitHub to Use ‘Main’ Instead of ‘Master’ as the Default Branch on All New Repositories Starting Next Month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105014\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:269:\"https://wptavern.com/github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month?utm_source=rss&utm_medium=rss&utm_campaign=github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4844:\"

In August, GitHub announced that it would change the “master” branch name for all new repositories created on the platform to “main” starting October 1. The date is less than two weeks away, and WordPress developers need to be prepared for the change if they use the service for version control or project management.

\n\n\n\n

The larger tech and web development community began conversations through various venues in June, a time in which the Black Lives Matter was gaining more traction in the U.S. and worldwide. The discussion centered on removing any terminology that could be discriminatory or oppressive to specific groups of people. This ongoing discussion has shown that there is a deep division over whether such changes are necessary or even helpful.

\n\n\n\n

The WordPress community is dealing with this division itself. Aaron Jorbin proposed a change at the same time to rename the default branch name on WordPress-owned repositories. Through discussion on his post and elsewhere, the community landed on “trunk,” which keeps WordPress projects in line with its SVN roots.

\n\n\n\n

“To close the circle on this, a decision was made in June and earlier today (August 19),” wrote Helen Hou-Sandí, a lead WordPress developer, in the comments of the original proposal. “I updated the default branch name for new GitHub repositories under the WordPress organization to be trunk after GitHub enabled early access to that feature.”

\n\n\n\n

As evidenced by the comments on the Tavern’s coverage of the proposal and those on the original post, the WordPress development community as a whole did not support this decision.

\n\n\n\n

Jorbin has updated several of WordPress’s repositories and switched them to use trunk instead of master. However, there are still some lingering projects yet to be updated, including the primary WordPress and WordPress Develop repositories. He left a comment with an updated list in June. There is no public word on whether the existing, leftover projects will be changed.

\n\n\n\n

WordPress Developer Preparations

\n\n\n\nCustomizing the default branch for a user’s GitHub repositories.\n\n\n\n

GitHub is merely changing the default branch name for new repositories starting on October 1. This change does not affect existing repositories. Individual users, organization owners, and enterprise administrators can customize the default branch via their account settings now before the switch is made. Owners can also change the default branch name for individual repositories.

\n\n\n\n

The biggest thing that developers need to watch out for is their tooling or other integrations that might still require the master branch. There may be cases where an alternative default branch name will break workflows. If planning to use a different branch name, the best thing to do right now is to spin up the tools you use on a test repository. If something breaks, check to see whether the particular tool you are using will be getting an update. In most cases, this should not be a problem because customized default branch names will be an industry standard.

\n\n\n\n

The great thing about how GitHub is rolling out this feature is that it offers a choice. Those who believe that “master” is oppressive can change the branch name to something they feel is more inclusive. For those who believe otherwise, they can keep their master branch. But, everyone can use the branch name they prefer.

\n\n\n\n

For existing repositories, GitHub is asking that developers be patient for now. The company is investing in tools to make this a seamless experience later this year. There are a few technical hurdles to clear first.

\n\n\n\n

Developers should read the full GitHub guide on setting the default branch for more information.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 20:39:55 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:8:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:31 GMT\";s:12:\"content-type\";s:8:\"text/xml\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:13:\"last-modified\";s:29:\"Thu, 22 Oct 2020 16:15:08 GMT\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 2\";s:16:\"content-encoding\";s:4:\"gzip\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(133,'_transient_timeout_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(134,'_transient_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603384291','no'),(135,'_transient_timeout_dash_v2_88ae138922fe95674369b1cb3d215a2b','1603427491','no'),(136,'_transient_dash_v2_88ae138922fe95674369b1cb3d215a2b','','no'),(139,'theme_mods_twentytwenty','a:1:{s:18:\"custom_css_post_id\";i:-1;}','yes'),(140,'recently_activated','a:0:{}','yes'); +INSERT INTO `wp55_options` VALUES (1,'siteurl','http://localhost','yes'),(2,'home','http://localhost','yes'),(3,'blogname','Datadog Test Application','yes'),(4,'blogdescription','Just another WordPress site','yes'),(5,'users_can_register','0','yes'),(6,'admin_email','test@gmail.com','yes'),(7,'start_of_week','1','yes'),(8,'use_balanceTags','0','yes'),(9,'use_smilies','1','yes'),(10,'require_name_email','1','yes'),(11,'comments_notify','1','yes'),(12,'posts_per_rss','10','yes'),(13,'rss_use_excerpt','0','yes'),(14,'mailserver_url','mail.example.com','yes'),(15,'mailserver_login','login@example.com','yes'),(16,'mailserver_pass','password','yes'),(17,'mailserver_port','110','yes'),(18,'default_category','1','yes'),(19,'default_comment_status','open','yes'),(20,'default_ping_status','open','yes'),(21,'default_pingback_flag','0','yes'),(22,'posts_per_page','10','yes'),(23,'date_format','F j, Y','yes'),(24,'time_format','g:i a','yes'),(25,'links_updated_date_format','F j, Y g:i a','yes'),(26,'comment_moderation','0','yes'),(27,'moderation_notify','1','yes'),(28,'permalink_structure','/%postname%','yes'),(29,'rewrite_rules','a:93:{s:11:\"^wp-json/?$\";s:22:\"index.php?rest_route=/\";s:14:\"^wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:21:\"^index.php/wp-json/?$\";s:22:\"index.php?rest_route=/\";s:24:\"^index.php/wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:17:\"^wp-sitemap\\.xml$\";s:23:\"index.php?sitemap=index\";s:17:\"^wp-sitemap\\.xsl$\";s:36:\"index.php?sitemap-stylesheet=sitemap\";s:23:\"^wp-sitemap-index\\.xsl$\";s:34:\"index.php?sitemap-stylesheet=index\";s:48:\"^wp-sitemap-([a-z]+?)-([a-z\\d_-]+?)-(\\d+?)\\.xml$\";s:75:\"index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]\";s:34:\"^wp-sitemap-([a-z]+?)-(\\d+?)\\.xml$\";s:47:\"index.php?sitemap=$matches[1]&paged=$matches[2]\";s:47:\"category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:42:\"category/(.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:23:\"category/(.+?)/embed/?$\";s:46:\"index.php?category_name=$matches[1]&embed=true\";s:35:\"category/(.+?)/page/?([0-9]{1,})/?$\";s:53:\"index.php?category_name=$matches[1]&paged=$matches[2]\";s:17:\"category/(.+?)/?$\";s:35:\"index.php?category_name=$matches[1]\";s:44:\"tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:39:\"tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:20:\"tag/([^/]+)/embed/?$\";s:36:\"index.php?tag=$matches[1]&embed=true\";s:32:\"tag/([^/]+)/page/?([0-9]{1,})/?$\";s:43:\"index.php?tag=$matches[1]&paged=$matches[2]\";s:14:\"tag/([^/]+)/?$\";s:25:\"index.php?tag=$matches[1]\";s:45:\"type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:40:\"type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:21:\"type/([^/]+)/embed/?$\";s:44:\"index.php?post_format=$matches[1]&embed=true\";s:33:\"type/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?post_format=$matches[1]&paged=$matches[2]\";s:15:\"type/([^/]+)/?$\";s:33:\"index.php?post_format=$matches[1]\";s:12:\"robots\\.txt$\";s:18:\"index.php?robots=1\";s:13:\"favicon\\.ico$\";s:19:\"index.php?favicon=1\";s:48:\".*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\\.php$\";s:18:\"index.php?feed=old\";s:20:\".*wp-app\\.php(/.*)?$\";s:19:\"index.php?error=403\";s:18:\".*wp-register.php$\";s:23:\"index.php?register=true\";s:32:\"feed/(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:27:\"(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:8:\"embed/?$\";s:21:\"index.php?&embed=true\";s:20:\"page/?([0-9]{1,})/?$\";s:28:\"index.php?&paged=$matches[1]\";s:41:\"comments/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:36:\"comments/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:17:\"comments/embed/?$\";s:21:\"index.php?&embed=true\";s:44:\"search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:39:\"search/(.+)/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:20:\"search/(.+)/embed/?$\";s:34:\"index.php?s=$matches[1]&embed=true\";s:32:\"search/(.+)/page/?([0-9]{1,})/?$\";s:41:\"index.php?s=$matches[1]&paged=$matches[2]\";s:14:\"search/(.+)/?$\";s:23:\"index.php?s=$matches[1]\";s:47:\"author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:42:\"author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:23:\"author/([^/]+)/embed/?$\";s:44:\"index.php?author_name=$matches[1]&embed=true\";s:35:\"author/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?author_name=$matches[1]&paged=$matches[2]\";s:17:\"author/([^/]+)/?$\";s:33:\"index.php?author_name=$matches[1]\";s:69:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:64:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:45:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$\";s:74:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true\";s:57:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:81:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]\";s:39:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$\";s:63:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]\";s:56:\"([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:51:\"([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:32:\"([0-9]{4})/([0-9]{1,2})/embed/?$\";s:58:\"index.php?year=$matches[1]&monthnum=$matches[2]&embed=true\";s:44:\"([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:65:\"index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]\";s:26:\"([0-9]{4})/([0-9]{1,2})/?$\";s:47:\"index.php?year=$matches[1]&monthnum=$matches[2]\";s:43:\"([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:38:\"([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:19:\"([0-9]{4})/embed/?$\";s:37:\"index.php?year=$matches[1]&embed=true\";s:31:\"([0-9]{4})/page/?([0-9]{1,})/?$\";s:44:\"index.php?year=$matches[1]&paged=$matches[2]\";s:13:\"([0-9]{4})/?$\";s:26:\"index.php?year=$matches[1]\";s:27:\".?.+?/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\".?.+?/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\".?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\".?.+?/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"(.?.+?)/embed/?$\";s:41:\"index.php?pagename=$matches[1]&embed=true\";s:20:\"(.?.+?)/trackback/?$\";s:35:\"index.php?pagename=$matches[1]&tb=1\";s:40:\"(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:35:\"(.?.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:28:\"(.?.+?)/page/?([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&paged=$matches[2]\";s:35:\"(.?.+?)/comment-page-([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&cpage=$matches[2]\";s:24:\"(.?.+?)(?:/([0-9]+))?/?$\";s:47:\"index.php?pagename=$matches[1]&page=$matches[2]\";s:27:\"[^/]+/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\"[^/]+/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\"[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\"[^/]+/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"([^/]+)/embed/?$\";s:37:\"index.php?name=$matches[1]&embed=true\";s:20:\"([^/]+)/trackback/?$\";s:31:\"index.php?name=$matches[1]&tb=1\";s:40:\"([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:35:\"([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:28:\"([^/]+)/page/?([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&paged=$matches[2]\";s:35:\"([^/]+)/comment-page-([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&cpage=$matches[2]\";s:24:\"([^/]+)(?:/([0-9]+))?/?$\";s:43:\"index.php?name=$matches[1]&page=$matches[2]\";s:16:\"[^/]+/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:26:\"[^/]+/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:46:\"[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:22:\"[^/]+/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";}','yes'),(30,'hack_file','0','yes'),(31,'blog_charset','UTF-8','yes'),(32,'moderation_keys','','no'),(33,'active_plugins','a:1:{i:0;s:19:\"datadog/datadog.php\";}','yes'),(34,'category_base','','yes'),(35,'ping_sites','http://rpc.pingomatic.com/','yes'),(36,'comment_max_links','2','yes'),(37,'gmt_offset','0','yes'),(38,'default_email_category','1','yes'),(39,'recently_edited','','no'),(40,'template','twentytwenty','yes'),(41,'stylesheet','twentytwenty','yes'),(42,'comment_registration','0','yes'),(43,'html_type','text/html','yes'),(44,'use_trackback','0','yes'),(45,'default_role','subscriber','yes'),(46,'db_version','48748','yes'),(47,'uploads_use_yearmonth_folders','1','yes'),(48,'upload_path','','yes'),(49,'blog_public','0','yes'),(50,'default_link_category','2','yes'),(51,'show_on_front','posts','yes'),(52,'tag_base','','yes'),(53,'show_avatars','1','yes'),(54,'avatar_rating','G','yes'),(55,'upload_url_path','','yes'),(56,'thumbnail_size_w','150','yes'),(57,'thumbnail_size_h','150','yes'),(58,'thumbnail_crop','1','yes'),(59,'medium_size_w','300','yes'),(60,'medium_size_h','300','yes'),(61,'avatar_default','mystery','yes'),(62,'large_size_w','1024','yes'),(63,'large_size_h','1024','yes'),(64,'image_default_link_type','none','yes'),(65,'image_default_size','','yes'),(66,'image_default_align','','yes'),(67,'close_comments_for_old_posts','0','yes'),(68,'close_comments_days_old','14','yes'),(69,'thread_comments','1','yes'),(70,'thread_comments_depth','5','yes'),(71,'page_comments','0','yes'),(72,'comments_per_page','50','yes'),(73,'default_comments_page','newest','yes'),(74,'comment_order','asc','yes'),(75,'sticky_posts','a:0:{}','yes'),(76,'widget_categories','a:2:{i:2;a:4:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:12:\"hierarchical\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(77,'widget_text','a:0:{}','yes'),(78,'widget_rss','a:0:{}','yes'),(79,'uninstall_plugins','a:0:{}','no'),(80,'timezone_string','','yes'),(81,'page_for_posts','0','yes'),(82,'page_on_front','0','yes'),(83,'default_post_format','0','yes'),(84,'link_manager_enabled','0','yes'),(85,'finished_splitting_shared_terms','1','yes'),(86,'site_icon','0','yes'),(87,'medium_large_size_w','768','yes'),(88,'medium_large_size_h','0','yes'),(89,'wp_page_for_privacy_policy','3','yes'),(90,'show_comments_cookies_opt_in','1','yes'),(91,'admin_email_lifespan','1618936275','yes'),(92,'disallowed_keys','','no'),(93,'comment_previously_approved','1','yes'),(94,'auto_plugin_theme_update_emails','a:0:{}','no'),(95,'initial_db_version','48748','yes'),(96,'wp55_user_roles','a:5:{s:13:\"administrator\";a:2:{s:4:\"name\";s:13:\"Administrator\";s:12:\"capabilities\";a:61:{s:13:\"switch_themes\";b:1;s:11:\"edit_themes\";b:1;s:16:\"activate_plugins\";b:1;s:12:\"edit_plugins\";b:1;s:10:\"edit_users\";b:1;s:10:\"edit_files\";b:1;s:14:\"manage_options\";b:1;s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:6:\"import\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:8:\"level_10\";b:1;s:7:\"level_9\";b:1;s:7:\"level_8\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;s:12:\"delete_users\";b:1;s:12:\"create_users\";b:1;s:17:\"unfiltered_upload\";b:1;s:14:\"edit_dashboard\";b:1;s:14:\"update_plugins\";b:1;s:14:\"delete_plugins\";b:1;s:15:\"install_plugins\";b:1;s:13:\"update_themes\";b:1;s:14:\"install_themes\";b:1;s:11:\"update_core\";b:1;s:10:\"list_users\";b:1;s:12:\"remove_users\";b:1;s:13:\"promote_users\";b:1;s:18:\"edit_theme_options\";b:1;s:13:\"delete_themes\";b:1;s:6:\"export\";b:1;}}s:6:\"editor\";a:2:{s:4:\"name\";s:6:\"Editor\";s:12:\"capabilities\";a:34:{s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;}}s:6:\"author\";a:2:{s:4:\"name\";s:6:\"Author\";s:12:\"capabilities\";a:10:{s:12:\"upload_files\";b:1;s:10:\"edit_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;s:22:\"delete_published_posts\";b:1;}}s:11:\"contributor\";a:2:{s:4:\"name\";s:11:\"Contributor\";s:12:\"capabilities\";a:5:{s:10:\"edit_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;}}s:10:\"subscriber\";a:2:{s:4:\"name\";s:10:\"Subscriber\";s:12:\"capabilities\";a:2:{s:4:\"read\";b:1;s:7:\"level_0\";b:1;}}}','yes'),(97,'fresh_site','0','yes'),(98,'widget_search','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(99,'widget_recent-posts','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(100,'widget_recent-comments','a:2:{i:2;a:2:{s:5:\"title\";s:0:\"\";s:6:\"number\";i:5;}s:12:\"_multiwidget\";i:1;}','yes'),(101,'widget_archives','a:2:{i:2;a:3:{s:5:\"title\";s:0:\"\";s:5:\"count\";i:0;s:8:\"dropdown\";i:0;}s:12:\"_multiwidget\";i:1;}','yes'),(102,'widget_meta','a:2:{i:2;a:1:{s:5:\"title\";s:0:\"\";}s:12:\"_multiwidget\";i:1;}','yes'),(103,'sidebars_widgets','a:4:{s:19:\"wp_inactive_widgets\";a:0:{}s:9:\"sidebar-1\";a:3:{i:0;s:8:\"search-2\";i:1;s:14:\"recent-posts-2\";i:2;s:17:\"recent-comments-2\";}s:9:\"sidebar-2\";a:3:{i:0;s:10:\"archives-2\";i:1;s:12:\"categories-2\";i:2;s:6:\"meta-2\";}s:13:\"array_version\";i:3;}','yes'),(104,'cron','a:7:{i:1603384279;a:5:{s:32:\"recovery_mode_clean_expired_keys\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:34:\"wp_privacy_delete_old_export_files\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"hourly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:3600;}}s:16:\"wp_version_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:17:\"wp_update_plugins\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_update_themes\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1603384285;a:2:{s:19:\"wp_scheduled_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:25:\"delete_expired_transients\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1603384288;a:1:{s:30:\"wp_scheduled_auto_draft_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1603384345;a:1:{s:28:\"wp_update_comment_type_batch\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:2:{s:8:\"schedule\";b:0;s:4:\"args\";a:0:{}}}}i:1603384346;a:1:{s:8:\"do_pings\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:2:{s:8:\"schedule\";b:0;s:4:\"args\";a:0:{}}}}i:1603470679;a:1:{s:30:\"wp_site_health_scheduled_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"weekly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:604800;}}}s:7:\"version\";i:2;}','yes'),(105,'widget_pages','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(106,'widget_calendar','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(107,'widget_media_audio','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(108,'widget_media_image','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(109,'widget_media_gallery','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(110,'widget_media_video','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(111,'widget_tag_cloud','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(112,'widget_nav_menu','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(113,'widget_custom_html','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(114,'_transient_doing_cron','1603384688.0394918918609619140625','yes'),(115,'_site_transient_update_core','O:8:\"stdClass\":4:{s:7:\"updates\";a:1:{i:0;O:8:\"stdClass\":10:{s:8:\"response\";s:6:\"latest\";s:8:\"download\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:6:\"locale\";s:5:\"en_US\";s:8:\"packages\";O:8:\"stdClass\":5:{s:4:\"full\";s:59:\"https://downloads.wordpress.org/release/wordpress-5.5.1.zip\";s:10:\"no_content\";s:70:\"https://downloads.wordpress.org/release/wordpress-5.5.1-no-content.zip\";s:11:\"new_bundled\";s:71:\"https://downloads.wordpress.org/release/wordpress-5.5.1-new-bundled.zip\";s:7:\"partial\";s:0:\"\";s:8:\"rollback\";s:0:\"\";}s:7:\"current\";s:5:\"5.5.1\";s:7:\"version\";s:5:\"5.5.1\";s:11:\"php_version\";s:6:\"5.6.20\";s:13:\"mysql_version\";s:3:\"5.0\";s:11:\"new_bundled\";s:3:\"5.3\";s:15:\"partial_version\";s:0:\"\";}}s:12:\"last_checked\";i:1603384286;s:15:\"version_checked\";s:5:\"5.5.1\";s:12:\"translations\";a:0:{}}','no'),(116,'_site_transient_update_plugins','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384412;s:7:\"checked\";a:3:{s:19:\"akismet/akismet.php\";s:5:\"4.1.6\";s:19:\"datadog/datadog.php\";s:5:\"0.0.0\";s:9:\"hello.php\";s:5:\"1.7.2\";}s:8:\"response\";a:0:{}s:12:\"translations\";a:0:{}s:9:\"no_update\";a:2:{s:19:\"akismet/akismet.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:21:\"w.org/plugins/akismet\";s:4:\"slug\";s:7:\"akismet\";s:6:\"plugin\";s:19:\"akismet/akismet.php\";s:11:\"new_version\";s:5:\"4.1.6\";s:3:\"url\";s:38:\"https://wordpress.org/plugins/akismet/\";s:7:\"package\";s:56:\"https://downloads.wordpress.org/plugin/akismet.4.1.6.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:59:\"https://ps.w.org/akismet/assets/icon-256x256.png?rev=969272\";s:2:\"1x\";s:59:\"https://ps.w.org/akismet/assets/icon-128x128.png?rev=969272\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:61:\"https://ps.w.org/akismet/assets/banner-772x250.jpg?rev=479904\";}s:11:\"banners_rtl\";a:0:{}}s:9:\"hello.php\";O:8:\"stdClass\":9:{s:2:\"id\";s:25:\"w.org/plugins/hello-dolly\";s:4:\"slug\";s:11:\"hello-dolly\";s:6:\"plugin\";s:9:\"hello.php\";s:11:\"new_version\";s:5:\"1.7.2\";s:3:\"url\";s:42:\"https://wordpress.org/plugins/hello-dolly/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/plugin/hello-dolly.1.7.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-256x256.jpg?rev=2052855\";s:2:\"1x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-128x128.jpg?rev=2052855\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:66:\"https://ps.w.org/hello-dolly/assets/banner-772x250.jpg?rev=2052855\";}s:11:\"banners_rtl\";a:0:{}}}}','no'),(117,'_site_transient_timeout_theme_roots','1603386087','no'),(118,'_site_transient_theme_roots','a:3:{s:14:\"twentynineteen\";s:7:\"/themes\";s:15:\"twentyseventeen\";s:7:\"/themes\";s:12:\"twentytwenty\";s:7:\"/themes\";}','no'),(119,'_site_transient_update_themes','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1603384287;s:7:\"checked\";a:3:{s:14:\"twentynineteen\";s:3:\"1.7\";s:15:\"twentyseventeen\";s:3:\"2.4\";s:12:\"twentytwenty\";s:3:\"1.5\";}s:8:\"response\";a:0:{}s:9:\"no_update\";a:3:{s:14:\"twentynineteen\";a:6:{s:5:\"theme\";s:14:\"twentynineteen\";s:11:\"new_version\";s:3:\"1.7\";s:3:\"url\";s:44:\"https://wordpress.org/themes/twentynineteen/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/theme/twentynineteen.1.7.zip\";s:8:\"requires\";s:5:\"4.9.6\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:15:\"twentyseventeen\";a:6:{s:5:\"theme\";s:15:\"twentyseventeen\";s:11:\"new_version\";s:3:\"2.4\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentyseventeen/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentyseventeen.2.4.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}s:12:\"twentytwenty\";a:6:{s:5:\"theme\";s:12:\"twentytwenty\";s:11:\"new_version\";s:3:\"1.5\";s:3:\"url\";s:42:\"https://wordpress.org/themes/twentytwenty/\";s:7:\"package\";s:58:\"https://downloads.wordpress.org/theme/twentytwenty.1.5.zip\";s:8:\"requires\";s:3:\"4.7\";s:12:\"requires_php\";s:5:\"5.2.4\";}}s:12:\"translations\";a:0:{}}','no'),(120,'_site_transient_timeout_browser_6daa110c3e56e442b403473c9591e946','1603989088','no'),(121,'_site_transient_browser_6daa110c3e56e442b403473c9591e946','a:10:{s:4:\"name\";s:6:\"Chrome\";s:7:\"version\";s:12:\"86.0.4240.80\";s:8:\"platform\";s:9:\"Macintosh\";s:10:\"update_url\";s:29:\"https://www.google.com/chrome\";s:7:\"img_src\";s:43:\"http://s.w.org/images/browsers/chrome.png?1\";s:11:\"img_src_ssl\";s:44:\"https://s.w.org/images/browsers/chrome.png?1\";s:15:\"current_version\";s:2:\"18\";s:7:\"upgrade\";b:0;s:8:\"insecure\";b:0;s:6:\"mobile\";b:0;}','no'),(122,'_site_transient_timeout_php_check_56babb1797dd31750a342dc4c8a11025','1603989088','no'),(123,'_site_transient_php_check_56babb1797dd31750a342dc4c8a11025','a:5:{s:19:\"recommended_version\";s:3:\"7.4\";s:15:\"minimum_version\";s:6:\"5.6.20\";s:12:\"is_supported\";b:1;s:9:\"is_secure\";b:1;s:13:\"is_acceptable\";b:1;}','no'),(124,'_site_transient_timeout_community-events-e0e4f94be3c2d577e126ec3b012627f2','1603427490','no'),(125,'_site_transient_community-events-e0e4f94be3c2d577e126ec3b012627f2','a:4:{s:9:\"sandboxed\";b:0;s:5:\"error\";N;s:8:\"location\";a:1:{s:2:\"ip\";s:12:\"192.168.16.0\";}s:6:\"events\";a:2:{i:0;a:10:{s:4:\"type\";s:6:\"meetup\";s:5:\"title\";s:58:\"Discussion Group: WordPress Troubleshooting Basics: Part 1\";s:3:\"url\";s:68:\"https://www.meetup.com/learn-wordpress-discussions/events/273993927/\";s:6:\"meetup\";s:27:\"Learn WordPress Discussions\";s:10:\"meetup_url\";s:51:\"https://www.meetup.com/learn-wordpress-discussions/\";s:4:\"date\";s:19:\"2020-10-23 06:00:00\";s:8:\"end_date\";s:19:\"2020-10-23 07:00:00\";s:20:\"start_unix_timestamp\";i:1603458000;s:18:\"end_unix_timestamp\";i:1603461600;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"US\";s:8:\"latitude\";d:37.779998779297;s:9:\"longitude\";d:-122.41999816895;}}i:1;a:10:{s:4:\"type\";s:8:\"wordcamp\";s:5:\"title\";s:17:\"WordCamp Bulgaria\";s:3:\"url\";s:35:\"https://bulgaria.wordcamp.org/2020/\";s:6:\"meetup\";N;s:10:\"meetup_url\";N;s:4:\"date\";s:19:\"2020-10-24 10:00:00\";s:8:\"end_date\";s:19:\"2020-10-24 10:00:00\";s:20:\"start_unix_timestamp\";i:1603522800;s:18:\"end_unix_timestamp\";i:1603522800;s:8:\"location\";a:4:{s:8:\"location\";s:6:\"Online\";s:7:\"country\";s:2:\"BG\";s:8:\"latitude\";d:42.733883;s:9:\"longitude\";d:25.48583;}}}}','no'),(126,'can_compress_scripts','0','no'),(127,'_transient_timeout_feed_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(128,'_transient_feed_9bbd59226dc36b9b26cd43f15694c5c3','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:49:\"\n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:27:\"News – – WordPress.org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:13:\"lastBuildDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 20:10:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"en-US\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"generator\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"https://wordpress.org/?v=5.6-beta1-49274\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:10:{i:0;a:6:{s:4:\"data\";s:60:\"\n \n\n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WordPress 5.6 Beta 1 is now available for testing!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8236:\"\n

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:38:\"The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:363:\"This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September.  WordPress 5.5.1 Launch On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8713:\"\n

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"WordPress 5.5.1 Maintenance Release\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"https://wordpress.org/news/2020/09/wordpress-5-5-1-maintenance-release/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 19:13:53 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8979\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:460:\"WordPress 5.5.1 is now available! This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade. You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process. […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9020:\"\n

WordPress 5.5.1 is now available!

\n\n\n\n

This maintenance release features 34 bug fixes, 5 enhancements, and 5 bug fixes for the block editor. These bugs affect WordPress version 5.5, so you’ll want to upgrade.

\n\n\n\n

You can download WordPress 5.5.1 directly, or visit the Dashboard → Updates screen and click Update Now. If your sites support automatic background updates, they’ve already started the update process.

\n\n\n\n

WordPress 5.5.1 is a short-cycle maintenance release. The next major release will be version 5.6.

\n\n\n\n

To see a full list of changes, you can browse the list on Trac, read the 5.5.1 RC1 and 5.5.1 RC2 posts, or visit the 5.5.1 documentation page.

\n\n\n\n

Thanks and props!

\n\n\n\n

The 5.5.1 release was led by @audrasjb, @azhiyadev, @davidbaumwald, @desrosj, @johnbillion, @planningwrite, @sergeybiryukov and @whyisjake.

\n\n\n\n

Thank you to everyone who helped make WordPress 5.5.1 happen:

\n\n\n\nAmit Dudhat, Andrea Fercia, Andrey “Rarst” Savchenko, Andy Fragen, Angel Hess, avixansa, bobbingwide, Brian Hogg, chunkysteveo, Clayton Collie, David Baumwald, David Herrera, dd32, demetris, Dominik Schilling, dushakov, Earle Davies, Enrique Sánchez, Frankie Jarrett, fullofcaffeine, Garrett Hyder, Gary Jones, gchtr, Hauwa, Herre Groen, Howdy_McGee, Ipstenu (Mika Epstein), Jb Audras, Jeremy Felt, Jeroen Rotty, Joen A., Johanna de Vos, John Blackbourn, John James Jacoby, Jonathan Bossenger, Jonathan Desrosiers, Jonathan Stegall, Joost de Valk, Jorge Costa, Justin Ahinon, Kalpesh Akabari, Kevin Hagerty, Knut Sparhell, Kyle B. Johnson, landau, Laxman Prajapati, Lester Chan, mailnew2ster, Marius L. J., Mark Jaquith, Mark Uraine, Matt Gibson, Michael Beckwith, Mikey Arce, Mohammad Jangda, Mukesh Panchal, Nabil Moqbel, net, oakesjosh, O André, Omar Reiss, Ov3rfly, Paddy, Pascal Casier, Paul Biron, Peter Wilson, rajeshsingh520, Rami Yushuvaev, rebasaurus, riaanlom, Riad Benguella, Rodrigo Arias, rtagliento, salvoaranzulla, Sanjeev Aryal, sarahricker, Sergey Biryukov, Stephen Bernhardt, Steven Stern (sterndata), Thomas M, Timothy Jacobs, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), TwentyZeroTwo, Winstina, wittich, and Yoav Farhi.\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8979\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"The Month in WordPress: August 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wordpress.org/news/2020/09/the-month-in-wordpress-august-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 01 Sep 2020 09:32:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8983\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:362:\"August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world. WordPress 5.5 Launch […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9605:\"\n

August was special for WordPress lovers, as one of the most anticipated releases, WordPress 5.5, was launched. The month also saw several updates from various contributor teams, including the soft-launch of the Learn WordPress project and updates to Gutenberg. Read on to find out about the latest updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Launch

\n\n\n\n

The team launched WordPress 5.5 on August 11. The major release comes with a host of features like automatic updates for plugins and themes, enabling updates over uploaded ZIP files, a block directory, XML sitemaps, block patterns, inline image editing, and lazy-loading images, to name a few. WordPress 5.5 is now available in 50 languages too! You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. Subsequent to the 5.5 release, the 5.5.1 release candidate came out on August 28, which will be followed by its official launch of the minor release on September 1.

\n\n\n\n

A record 805 people contributed to WordPress 5.5, hailing from 58 different countries. @audrasjb has compiled many more stats like that and they’re well worth a read!

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.7 and 8.8

\n\n\n\n

The core team launched Gutenberg 8.7 and 8.8. Version 8.7 saw many improvements to the Post Block suite, along with other changes like adding a block example to the Buttons block, consistently autosaving edits, and updating the group block description. Version 8.8 offers updates to Global Styles, the Post Block suite, and Template management. The release significantly improves the back-compatibility of the new Widget Screen, and also includes other important accessibility and mobile improvements to user interfaces like the Toolbar, navigation menus, and Popovers. For full details on the latest versions of these Gutenberg releases, visit these posts about 8.7 and 8.8.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Check out the brand new Learn WordPress platform!

\n\n\n\n

Learn WordPress is a brand new cross-team initiative led by the WordPress Community team, with support from the training team, the TV team, and the meta team. This platform is a learning repository on learn.wordpress.org, where WordPress learning content will be made available. Video workshops published on the site will be followed up by supplementary discussion groups based on workshop content. The first of these discussion groups have been scheduled, and you can join an upcoming discussion on the dedicated meetup group. The community team invites members to contribute to the project. You can apply to present a workshop, assist with reviewing submitted workshops, and add ideas for workshops that you would like to see on the site. You can also apply to be a discussion group leader to organize discussions directly through the learn.wordpress.org platform. We are also creating a dedicated Learn WordPress working group and have posted a call for volunteers. Meetup organizers can use Learn WordPress content for their meetup events (without applying as a discussion group leader). Simply ask your meetup group to watch one of the workshops in the weeks leading up to your scheduled event, and then host a discussion group for that content as your event.

\n\n\n\n

Want to get involved with the Community team? Follow the Community blog, or join them in the #community-events channel in the Making WordPress Slack group. To organize a local WordPress community event, visit the handbook page

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8983\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n\n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"WordPress 5.5 “Eckstine”\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"https://wordpress.org/news/2020/08/eckstine/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 11 Aug 2020 19:03:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8799\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:354:\"Version 5.5 \"Eckstine\" of WordPress is available for download or update in your WordPress dashboard. With this release, your site gets new power in three major areas: \nspeed (lazy-loading images), search (sitemaps included by default), and security (auto-updates for plugins and themes), along with many new features and improvements to the block editor.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:3:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:48:\"https://s.w.org/images/core/5.5/auto-updates.mp4\";s:6:\"length\";s:6:\"238264\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:50:\"https://s.w.org/images/core/5.5/block-patterns.mp4\";s:6:\"length\";s:7:\"3518792\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:56:\"https://s.w.org/images/core/5.5/inline-image-editing.mp4\";s:6:\"length\";s:7:\"3145140\";s:4:\"type\";s:9:\"video/mp4\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Matt Mullenweg\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:71062:\"\n

Here it is! Named “Eckstine” in honor of Billy Eckstine, this latest and greatest version of WordPress is available for download or update in your dashboard.

\n\n\n\n
\"\"
\n\n\n\n
\n

Welcome to WordPress 5.5.

\n\n\n\n

In WordPress 5.5, your site gets new power in three major areas:
speed, search, and security.

\n
\n\n\n\n
\n
\n\n\n\n
\n

Speed

\n\n\n\n

Posts and pages feel faster, thanks to lazy-loaded images.

\n\n\n\n

Images give your story a lot of impact, but they can sometimes make your site seem slow.

\n\n\n\n

In WordPress 5.5, images wait to load until they’re just about to scroll into view. The technical term is ‘lazy loading.’

\n\n\n\n

On mobile, lazy loading can also keep browsers from loading files meant for other devices. That can save your readers money on data — and help preserve battery life.

\n\n\n\n

Search

\n\n\n\n

Say hello to your new sitemap.

\n\n\n\n

WordPress sites work well with search engines.

\n\n\n\n

Now, by default, WordPress 5.5 includes an XML sitemap that helps search engines discover your most important pages from the very minute you go live.

\n\n\n\n

So more people will find your site sooner, giving you more time to engage, retain and convert them to subscribers, customers or whatever fits your definition of success.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Security

\n\n\n\n
Now you can choose to update plugins and themes automatically–or pick just a few–from the screens you’ve always used.
\n\n\n\n

Auto-updates for Plugins and Themes

\n\n\n\n

Now you can set plugins and themes to update automatically — or not! — in the WordPress admin. So you always know your site is running the latest code available.

\n\n\n\n

You can also turn auto-updates on or off for each plugin or theme you have installed — all on the same screens you’ve always used.

\n\n\n\n

Update by uploading ZIP files

\n\n\n\n

If updating plugins and themes manually is your thing, now that’s easier too — just upload a ZIP file.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Highlights from the block editor

\n\n\n\n

Once again, the latest WordPress release packs a long list of exciting new features for the block editor. For example:

\n\n\n\n
\n\n\n\n
\n
\n

Block patterns

\n\n\n\n

New block patterns make it simple and fun to create complex, beautiful layouts, using combinations of text and media that you can mix and match to fit your story.

\n\n\n\n

You will also find block patterns in a wide variety of plugins and themes, with more added all the time. Pick any of them from a single place — just click and go!

\n
\n\n\n\n
\n

The new block directory

\n\n\n\n

Now it’s easier than ever to find the block you need. The new block directory is built right into the block editor, so you can install new block types to your site without ever leaving the editor.

\n\n\n\n

Inline image editing

\n\n\n\n

Crop, rotate, and zoom your photos right from the image block. If you spend a lot of time on images, this could save you hours!

\n
\n
\n\n\n\n
\n\n\n\n

And so much more.

\n\n\n\n

The highlights above are a tiny fraction of the new block editor features you’ve just installed. Open the block editor and enjoy!

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

Accessibility

\n\n\n\n

Every release adds improvements to the accessible publishing experience, and that remains true for WordPress 5.5.

\n\n\n\n

Now you can copy links in media screens and modal dialogs with a button, instead of trying to highlight a line of text.

\n\n\n\n

You can also move meta boxes with the keyboard, and edit images in WordPress with your assistive device, as it can read you the instructions in the image editor.

\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

For developers

\n\n\n\n

5.5 also brings a big box of changes just for developers.

\n\n\n\n
\n
\n

Server-side registered blocks in the REST API

\n\n\n\n

The addition of block types endpoints means that JavaScript apps (like the block editor) can retrieve definitions for any blocks registered on the server.

\n\n\n\n

Defining environments

\n\n\n\n

WordPress now has a standardized way to define a site’s environment type (staging, production, etc). Retrieve that type with wp_get_environment_type() and execute only the appropriate code.

\n\n\n\n

Dashicons

\n\n\n\n

The Dashicons library has received its final update in 5.5. It adds 39 block editor icons along with 26 others.

\n\n\n\n

Passing data to template files

\n\n\n\n

The template loading functions (get_header()get_template_part(), etc.) have a new $args argument. So now you can pass an entire array’s worth of data to those templates.

\n
\n\n\n\n
\n

More changes for developers

\n\n\n\n
  • The PHPMailer library just got a major update, going from version 5.2.27 to 6.1.6.
  • Now get more fine-grained control of redirect_guess_404_permalink().
  • Sites that use PHP’s OPcache will see more reliable cache invalidation, thanks to the new wp_opcache_invalidate() function during updates (including to plugins and themes).
  • Custom post types associated with the category taxonomy can now opt-in to supporting the default term.
  • Default terms can now be specified for custom taxonomies in register_taxonomy().
  • The REST API now officially supports specifying default metadata values through register_meta().
  • You will find updated versions of these bundled libraries: SimplePie, Twemoji, Masonry, imagesLoaded, getID3, Moment.js, and clipboard.js.
\n
\n
\n
\n\n\n\n
\n
\n\n\n\n
\n
\n\n\n\n
\n

The Squad

\n\n\n\n

Leading this release were Matt MullenwegJake Spurlock, and David Baumwald. Supporting them was this highly enthusiastic release squad:

\n\n\n\n\n\n\n\n

Joining the squad throughout the release cycle were 805 generous volunteer contributors who collectively worked on over 523 tickets on Trac and over 1660 pull requests on GitHub.

\n\n\n\n

Put on a Billy Eckstine playlist, click that update button (or download it directly), and check the profiles of the fine folks that helped:

\n\n\nA2 Hosting, a4jp . com, a6software, Aaron D. Campbell, Aaron Jorbin, abderrahman, Abha Thakor, Achal Jain, achbed, Achyuth Ajoy, acosmin, acsnaterse, Adam Silverstein, Addie, addyosmani, adnan.limdi, adrian, airamerica, Ajay Ghaghretiya, Ajit Bohra, akbarhusen, akbarhusen429, Akhilesh Sabharwal, Akira Tachibana, Alain Schlesser, Albert Juhé Lluveras, Alex Concha, Alex Kirk, Alex Lende, Alex Shiels, Ali Shan, ali11007, Allen Snook, amaschas, Amit Dudhat, anbumz, andfinally, Andrea Fercia, Andrea Middleton, Andrea Tarantini, Andrei Draganescu, Andrew Duthie, Andrew Nacin, Andrew Nevins, Andrew Ozz, Andrey \"Rarst\" Savchenko, Andrés Maneiro, Andy Fragen, Andy Meerwaldt, Andy Peatling, Angel Hess, Angela Jin, Angelika Reisiger, Anh Tran, Ankit Gade, Ankit K Gupta, Ankit Panchal, Anne McCarthy, Anthony Burchell, Anthony Hortin, Anton Timmermans, Antonis Lilis, apedog, archon810, argentite, Arpit G Shah, Arslan Ahmed, asalce, ashiagr, ashour, Atharva Dhekne, Aurélien Joahny, aussi, automaton, avixansa, Ayesh Karunaratne, BackuPs, Barry, Barry Ceelen, Bart Czyz, bartekcholewa, bartkalisz, Bastien Ho, Bastien Martinent, bcworkz, bdbch, bdcstr, Ben Dunkle, Bence Szalai, bencroskery, Benjamin Gosset, Benoit Chantre, Bernhard Reiter, BettyJJ, bgermann, bigcloudmedia, bigdawggi, Bill Erickson, Birgir Erlendsson (birgire), Birgit Pauli-Haack, BjornW, bobbingwide, bonger, Boone Gorges, Boris Brdarić, Boy Witthaya, Brandon Kraft, Brandon Payton, Brent Swisher, Brian Hogg, Brian Krogsgard, bruandet, Bunty, Burhan Nasir, caiocrcosta, Cameron Voell, cameronamcintyre, Carike, Carl Wuensche, Carlos Galarza, Carolina Nymark, Caroline Moore, Carrigan, ceyhun, Chad, Chad Butler, Charles Fulton, Chetan Prajapati, Chintan hingrajiya, Chip Snyder, Chloé Bringmann, Chouby, Chris Van Patten, chriscct7, Christian Chung, Christian Jongeneel, Christian Sabo, Christian Wach, Christoph Herr, Christopher Churchill, chunkysteveo, cklee, clayray, Clayton Collie, Clifford Paulick, codeforest, Commeuneimage, Copons, Corey McKrill, cpasqualini, Cristovao Verstraeten, Csaba (LittleBigThings), Curtis Belt, Cyrus Collier, D.PERONNE, d6, Daniel Bachhuber, Daniel Hüsken, Daniel James, Daniel Llewellyn, Daniel Richards, Daniel Roch, Daniele Scasciafratte, Danny, Darko G., Darren Ethier (nerrad), Dave McHale, Dave Whitley, David A. Kennedy, David Aguilera, David Anderson, David Artiss, David Baumwald, David Brumbaugh, David E. Smith, David Herrera, David Ryan, David Shanske, David Smith, david.binda, davidvee, dchymko, Debabrata Karfa, Deepak Lalwani, dekervit, Delowar Hossain, demetris, Denis Yanchevskiy, derekakelly, Derrick Hammer, Derrick Tennant, Diane Co, Dilip Bheda, Dimitris Mitsis, dingo-d, Dion Hulse, Dixita Dusara, djennez, dmenard, dmethvin, doc987, Dominik Schilling, donmhico, Dono12, Doobeedoo, Dossy Shiobara, dpacks, dratwas, Drew Jaynes, DrLightman, DrProtocols, dsifford, dudo, dushakov, Dustin Bolton, dvershinin, Dylan Kuhn, Earle Davies, ecotechie, Eddie Moya, Eddy, Edi Amin, ehtis, Eileen Violini, Ekaterina, Ella van Durpe, elmastudio, Emanuel Blagonic, Emilie LEBRUN, Emmanuel Hesry, Enej Bajgoric, Enrico Sorcinelli, Enrique Piqueras, Enrique Sánchez, Eric, Eric Andrew Lewis, Eric Binnion, Erik Betshammar, Erin \'Folletto\' Casali, esemlabel, esoj, espiat, Estela Rueda, etoledom, etruel, Ev3rywh3re, Evan Mullins, Fabian Kägy, Fabian Todt, Faisal Ahmed, Felix Arntz, Felix Edelmann, ferdiesletering, finomeno, Florian Brinkmann, Florian TIAR, Florian Truchot, florianatwhodunit, FolioVision, Francesca Marano, Francois Thibaud, Frank Goossens, Frank Klein, Frank.Prendergast, Frankie Jarrett, Franz Armas, fullofcaffeine, Gabriel Koen, Gabriel Maldonado, Gabriel Mays, gadgetroid, Gal Baras, Garavani, garethgillman, Garrett Hyder, Gary Cao, Gary Jones, Gary Pendergast, gchtr, Geert De Deckere, Gemini Labs, Gennady Kovshenin, geriux, Giorgio25b, gisselfeldt, glendaviesnz, goldsounds, Goto Hayato, Govind Kumar, Grégory Viguier, gradina, Greg Ziółkowski, gregmulhauser, grierson, Grzegorz.Janoszka, gsmumbo, Guido Scialfa, guidobras, Gunther Pilz, gwwar, H-var, hakre, Halacious, hankthetank, Hapiuc Robert, Hareesh, haukep, Hauwa, Haz, Hector Farahani, Helen Hou-Sandi, Henry Wright, Herre Groen, hlanggo, hommealone, Hoover, Howdy_McGee, Hronak Nahar, huntlyc, Ian Belanger, Ian Dunn, Ian Stewart, ianjvr, ifrins, infinum, Ipstenu (Mika Epstein), Isabel Brison, ishitaka, J.D. Grimes, jackfungi, jacklinkers, Jadon N, jadpm, jagirbahesh, Jake Spurlock, Jake Whiteley, James Koster, James Nylen, Jan Koch, Jan Reilink, Jan Thiel, Janvo Aldred, Jarret, Jason Adams, Jason Coleman, Jason Cosper, Jason Crouse, Jason LeMahieu (MadtownLems), Jason Rouet, JasWSInc, Javier Casares, Jayson Basanes, jbinda, jbouganim, Jean-Baptiste Audras, Jean-David Daviet, Jeff Chandler, Jeff Farthing, Jeff Ong, Jeff Paul, Jen, Jenil Kanani, Jeremy Felt, Jeremy Herve, Jeremy Yip, Jeroen Rotty, jeryj, Jesin A, Jignesh Nakrani, Jim_Panse, Jip Moors, jivanpal, Joe Dolson, Joe Hoyle, Joe McGill, Joen Asmussen, Johanna de Vos, John Blackbourn, John Dorner, John James Jacoby, John P. Green, John Richards II, John Watkins, johnnyb, Jon Quach, Jon Surrell, Jonathan Bossenger, Jonathan Champ, Jonathan Christopher, Jonathan Desrosiers, Jonathan Stegall, jonkolbert, Jonny Harris, jonnybot, Jono Alderson, Joost de Valk, Jorge Bernal, Jorge Costa, Joseph Dickson, Josepha Haden, Josh Smith, JoshuaWold, Joy, Juanfra Aldasoro, juanlopez4691, Jules Colle, julianm, Juliette Reinders Folmer, Julio Potier, Julka Grodel, Justin Ahinon, Justin de Vesine, Justin Tadlock, justlevine, justnorris, K. Adam White, kaggdesign, Kailey (trepmal), Kaira, Kaitlin Bolling, Kalpesh Akabari, KamataRyo, Kantari Samy, Kaspars, Kavya Gokul, keesiemeijer, Kelly Dwan, kennethroberson5556, Kevin Hagerty, Kharis Sulistiyono, Khokan Sardar, kinjaldalwadi, Kiril Zhelyazkov, Kirsty Burgoine, Kishan Jasani, kitchin, Kite, Kjell Reigstad, Knut Sparhell, Konstantin Obenland, Konstantinos Xenos, ksoares, KT Cheung, Kukhyeon Heo, Kyle B. Johnson, lalitpendhare, landau, Laterna Studio, laurelfulford, Laurens Offereins, Laxman Prajapati, Lester Chan, Levdbas, Lew Ayotte, Lex Robinson, linyows, lipathor, Lisa Schuyler, liuhaibin, ljharb, logig, lucasbustamante, luiswill, Luke Cavanagh, Luke Walczak, lukestramasonder, M Asif Rahman, M.K. Safi, Maarten de Boer, Mahfoudh Arous, mailnew2ster, manojlovic, Manuel Schmalstieg, maraki, Marcin Pietrzak, Marcio Zebedeu, Marco Pereirinha, MarcoZ, Marcus, Marcus Kazmierczak, Marek Dědič, Marek Hrabe, Mario Valney, Marius Jensen, Mark Chouinard, Mark Jaquith, Mark Parnell, Mark Uraine, markdubois, markgoho, Marko Andrijasevic, Marko Heijnen, MarkRH, markshep, markusthiel, Martijn van der Kooij, martychc23, Mary Baum, Matheus Martins, Mathieu Viet, Matias Ventura, matjack1, Matt Cromwell, Matt Gibson, Matt Mullenweg, Matt Radford, Matt van Andel, mattchowning, Matthew Boynes, Matthew Eppelsheimer, Matthew Gerring, Matthias Kittsteiner, Matthias Pfefferle, Matthieu Mota, mattyrob, Maxime Culea, Maxime Pertici, maxme, Mayank Majeji, mcshane, Mel Choyce-Dwan, Menaka S., mensmaximus, metalandcoffee, Michael, Michael Arestad, Michael Arestad, Michael Beckwith, Michael Fields, Michael Nelson, Michele Butcher-Jones, Michelle, Miguel Fonseca, mihdan, Miina Sikk, Mikael Korpela, mikaumoto, Mike Crantea, Mike Glendinning, Mike Haydon, Mike Schinkel [WPLib Box project lead], Mike Schroder, Mikey Arce, Milana Cap, Milind More, mimi, mislavjuric, Mohammad Jangda, Mohammad Rockeybul Alam, Mohsin Rasool, Monika Rao, Morgan Kay, Morten Rand-Hendriksen, Morteza Geransayeh, moto hachi ( mt8.biz ), mrgrt, mrmist, mrTall, msaggiorato, Muhammad Usama Masood, Mukesh Panchal, munyagu, Nabil Moqbel, Nadir Seghir, Nahid Ferdous Mohit, Nalini Thakor, Naoko Takano, narwen, Nate Gay, Nathan Rice, Navid, neonkowy, net, netpassprodsr, Nextendweb, Ngan Tengyuen, Nick Daugherty, Nicky Lim, nicolad, Nicolas Juen, NicolasKulka, Nidhi Jain, Niels de Blaauw, Niels Lange, nigro.simone, Nik Tsekouras, Nikhil Bhansi, Nikolay Bachiyski, Nilo Velez, Niresh, nmenescardi, Noah Allen, NumidWasNotAvailable, oakesjosh, obliviousharmony, ockham, Olga Gleckler, Omar Alshaker, Omar Reiss, onokazu, Optimizing Matters, Ov3rfly, ovann86, overclokk, p_enrique, Paal Joachim Romdahl, Pablo Honey, Paddy, palmiak, Paresh Shinde, Parvand, Pascal Birchler, Pascal Casier, Paul Bearne, Paul Biron, Paul Fernhout, Paul Gibbs, Paul Ryan, Paul Schreiber, Paul Stonier, Paul Von Schrottky, pavelevap, Pedro Mendonça, pentatonicfunk, pepe, Peter \"Pessoft\" Kolínek, Peter Westwood, Peter Wilson, Phil Derksen, Phil Johnston, Philip Jackson, Pierre Gordon, pigdog234, pikamander2, pingram, Pionect, Piyush Patel, pkarjala, pkvillanueva, Prashant Baldha, pratik028, Pravin Parmar, Presskopp, Presslabs, Priyank Patel, Priyo Mukul, ProGrafika, programmin, Puneet Sahalot, pvogel2, r-a-y, Raaj Trambadia, Rachel Peter, raine, rajeshsingh520, Ramanan, Rami Yushuvaev, RavanH, Ravat Parmar, ravenswd, rawrly, rebasaurus, Red Sand Media Group, Remy Perona, Remzi Cavdar, Renatho, renggo888, retlehs, retrofox, riaanlom, Riad Benguella, Rian Rietveld, riasat, Rich Tabor, Ringisha, ritterml, Rnaby, Rob Cutmore, Rob Migchels, rob006, Robert Anderson, Robert Chapin, Robert Peake, Robert Windisch, Rodrigo Arias, Ronald Huereca, Rostislav Wolný, Roy Tanck, rtagliento, ruxandra, Ryan Boren, Ryan Fredlund, Ryan Kienstra, Ryan McCue, Ryan Welcher, Ryota Sakamoto, ryotsun, Sören Wrede, Søren Brønsted, Sachit Tandukar, Sagar Jadhav, Sajjad Hossain Sagor, Sal Ferrarello, Salvatore Formisano, salvoaranzulla, Sam Fullalove, Sam Webster, Samir Shah, Samuel Wood (Otto), samueljseay, Sander van Dragt, Sanjeev Aryal, Sanket Mehta, sarahricker, Sathiyamoorthy V, Sayed Taqui, scarolan, scholdstrom, Scott Kingsley Clark, Scott Reilly, Scott Smith, Scott Taylor, scribu, scruffian, Sean Hayes, seanpaulrasmussen, seayou, senatorman, Sergey Biryukov, Sergey Predvoditelev, Sergio de Falco, sergiomdgomes, Shannon Smith, Shantanu Desai, shaunandrews, Shawn Hooper, shawnz, Shital Marakana, shulard, siliconforks, Simon Wheatley, simonjanin, sinatrateam, sjmur, skarabeq, skorasaurus, skoskie, slushman, snapfractalpop, SpearsMarketing, sphakka, squarecandy, sreedoap, Stanimir Stoyanov, Stefano Minoia, Stefanos Togoulidis, Steph Wells, Stephen Bernhardt, Stephen Cronin, Stephen Edgar, Steve Dufresne, stevegibson12, Steven Stern (sterndata), Steven Word, stevenkussmaul, stevenlinx, Stiofan, Subrata Sarkar, SUM1, Sunny, Sunny Ratilal, Sushyant Zavarzadeh, suzylah, Sybre Waaijer, Synchro, Sérgio Estêvão, Takayuki Miyauchi, Tammie Lister, Tang Rufus, TeBenachi, Tessa Watkins LLC, Tetsuaki Hamano, theMikeD, theolg, Thierry Muller, Thimal Wickremage, Thomas M, Thorsten Frommen, Thrijith Thankachan, Tiago Hillebrandt, Till Krüss, Timothy Jacobs, Tkama, tmdesigned, tmoore41, TobiasBg, tobifjellner (Tor-Bjorn Fjellner), Tofandel, tomdude, Tommy Ferry, Tony G, Toro_Unit (Hiroshi Urabe), torres126, Torsten Landsiedel, Toru Miki, Travis Northcutt, treecutter, truongwp, tsimmons, Tung Du, Udit Desai, Ulrich, Vagios Vlachos, valchovski, Valentin Bora, Vayu Robins, veromary, Viktor Szépe, vinkla, virginienacci, Vladimir, Vladislav Abrashev, vortfu, voyager131, vtieu, webaware, Weston Ruter, William Earnhardt, williampatton, Winstina, wittich, wpdesk, WPDO, WPMarmite, wppinar, Yahil Madakiya, yashrs, yoancutillas, Yoav Farhi, yohannp, yuhin, Yui, Yuri Salame, Yvette Sonneveld, Zack Tollman, zaheerahmad, zakkath, Zebulan Stanphill, zieladam, and Česlav Przywara.\n\n\n\n

 

\n\n\n\n

Many thanks to all of the community volunteers who contribute in the support forums. They answer questions from people across the world, whether they are using WordPress for the first time or since the first release. These releases are more successful for their efforts!

\n\n\n\n

Finally, thanks to all the community translators who worked on WordPress 5.5. Their efforts bring WordPress fully translated to 46 languages at release time, with more on the way.

\n\n\n\n

If you want to learn more about volunteering with WordPress, check out Make WordPress or the core development blog.

\n
\n\n\n\n
\n
\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8799\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"WordPress 5.5 Release Candidate 2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"https://wordpress.org/news/2020/08/wordpress-5-5-release-candidate-2/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 04 Aug 2020 19:12:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8764\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:420:\"The second release candidate for WordPress 5.5 is here! WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time! You can test the WordPress 5.5 release candidate in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding edge nightlies” option) Or download the release […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2503:\"\n

The second release candidate for WordPress 5.5 is here!

\n\n\n\n

WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

For a more detailed breakdown of the changes included in WordPress 5.5, check out the WordPress 5.5 beta 1 post. The WordPress 5.5 Field Guide is also out! It’s your source for details on all the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8764\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n\n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:33:\"The Month in WordPress: July 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"https://wordpress.org/news/2020/08/the-month-in-wordpress-july-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 03 Aug 2020 13:54:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8755\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:340:\"July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:11539:\"\n

July was an action-packed month for the WordPress project. The month saw a lot of updates on one of the most anticipated releases – WordPress 5.5! WordCamp US 2020 was canceled and the WordPress community team started experimenting with different formats for engaging online events, in July. Read on to catch up with all the updates from the WordPress world.

\n\n\n\n
\n\n\n\n

WordPress 5.5 Updates

\n\n\n\n

July was full of WordPress 5.5 updates! The WordPress 5.5 Beta 1 came out on July 7, followed by Beta 2 on July 14, Beta 3 on July 21, and Beta 4 on July 27. Subsequently, the team also published the first release candidate of WordPress 5.5 on July 28. 

\n\n\n\n

WordPress 5.5, which is slated for release on August 11, 2020, is a major update with features like automatic updates for plugins and themes, a block directory, XML sitemaps, block patterns, and lazy-loading images, among others. To learn more about the release, check out its field guide post.

\n\n\n\n

Want to get involved in building WordPress Core? Follow the Core team blog, and join the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 8.5 and 8.6

\n\n\n\n

The core team launched Gutenberg 8.5 and 8.6. Version 8.5 – the last plugin release will be included entirely (without experimental features) in WordPress 5.5, introduced improvements to block drag-and-drop and accessibility, easier updates for external images, and support for the block directory. Version 8.6 comes with features like Cover block video position controls and block pattern updates. For full details on the latest versions on these Gutenberg releases, visit these posts about 8.5 and 8.6.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Reimagining Online WordPress Events

\n\n\n\n

The Community team made the difficult decision to suspend in-person WordPress events for the rest of 2020 in light of the COVID-19 pandemic. The team has also started working on reimagining online events. Based on feedback from the community members, the team decided to make changes to the current online WordCamp format. Key changes include wrapping up financial support for A/V vendors, ending event swag support for newer online WordCamps, and suspending the Global Community Sponsorship program for 2020. The team encourages upcoming online WordCamps to experiment with their events to facilitate an effective learning experience for attendees while avoiding online event fatigue. The team is currently working on a proposal to organize community-supported recorded workshops and synchronous discussion groups to help community members learn WordPress.

Want to get involved with the Community team? Follow the Community blog here, or join them in the #community-events channel in the Making WordPress Slack group. To organize a Meetup or WordCamp, visit the handbook page

\n\n\n\n

WordCamp US 2020 is canceled

\n\n\n\n

The organizers of WordCamp US 2020 have canceled the event in light of the continued pandemic and online event fatigue. The flagship event, which was originally scheduled for October 27-29 as an in-person event, had already planned to transition to an online event. Several WCUS Organizers will be working with the WordPress Community team to focus on other formats and ideas for online events, including a 24-hour contributor day, and contributing to the workshops initiative currently being discussed. Matt Mullenweg’s State of the Word (which typically accompanies WordCamp US) is likely to take place in a different format later in 2020.

\n\n\n\n

Plugin and theme updates are now available over zip files

\n\n\n\n

After eleven years, WordPress now allows users to update plugins and themes by uploading a ZIP file, in WordPress 5.5.  The feature, which was merged on July 7, has been one of the most requested features in WordPress. Now, when a user tries to upload a plugin or theme zip file from the WordPress dashboard by clicking the “Install Now” button, WordPress will direct users to a new screen that compares the currently-installed extension with the uploaded versions. Users can then choose between continuing with the installation or canceling. WordPress 5.5 will also offer automatic plugin and theme updates

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n
  • The Block directory is coming to WordPress with the 5.5 release. Plugin authors can now submit their Block plugins to the directory.
  • The Core team has opened up the call for features in the WordPress 5.6 release. You can comment on the post with features that you’d like to be included, current UX pain points, or maintenance tickets that need to be addressed. August 20 is the deadline for feature requests. 
  • Editor features such as the new Navigation block, the navigation screen, and the widget screen that were originally planned to be merged with WordPress 5.5 have been pushed for the next release
  • The Theme team is inviting proposals on whether to allow themes to place an additional top-level menu link in the admin.
  • BuddyPress 6.2 beta is out in the wild, and the team will soon release the stable version. The update includes changes that will make BuddyPress fully compatible with WordPress 5.5.
  • WordCamp EU 2021, which was being planned as an in-person event in Porto, Portugal, is moving online. The team is considering an in-person WordCamp EU in 2022. 
  • The Polyglots team has prepared and finalized a Translation Editor & Locale Manager Vetting Criteria to provide more clarity on how global mentors assign PTE/GTE/Locale Managers and to help locale teams set their own guidelines. The document, which was finalized after a lot of discussion, is now available in the Polyglots handbook.
  • Members of the Community team are discussing whether WordCamp volunteers, WordCamp attendees, or Meetup attendees should be awarded a WordPress.org profile badge. The ongoing discussion will be open for comments until August 13.
  • The WP Notify project, which aims to create a better way to manage and deliver notifications to the relevant audience, is on to its next steps. The team has finalized the initial requirements, and is kicking off the project build.
  • The WordPress documentation team is considering a ban on links to commercial websites in a revision to its external linking policy. The policy change does not remove external links to commercial sites from WordPress.org and only applies to documentation sites. The idea is to protect documentation from being abused, and to prevent the WordPress project from being biased. Discussion on this post is still ongoing, and a decision has not yet been made. Feel free to comment on the discussion posts, if you would like to share your thoughts on the topic.
\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8755\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"WordPress 5.5 Release Candidate\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://wordpress.org/news/2020/07/wordpress-5-5-release-candidate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 28 Jul 2020 19:08:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8732\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:370:\"The first release candidate for WordPress 5.5 is now available! This is an important milestone in the community’s progress toward the final release of WordPress 5.5. “Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"Jb Audras\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:2970:\"\n

The first release candidate for WordPress 5.5 is now available!

\n\n\n\n

This is an important milestone in the community’s progress toward the final release of WordPress 5.5.

\n\n\n\n

“Release Candidate” means that the new version is ready for release, but with millions of users and thousands of plugins and themes, it’s possible something was missed. WordPress 5.5 is slated for release on August 11, 2020, but we need your help to get there—if you haven’t tried 5.5 yet, now is the time!

\n\n\n\n

You can test the WordPress 5.5 release candidate in two ways:

\n\n\n\n\n\n\n\n

Thank you to all of the contributors who tested the Beta releases and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

What’s in WordPress 5.5?

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developer notes tag for updates on those and other changes that could affect your products.

\n\n\n\n

Plugin and Theme Developers

\n\n\n\n

Please test your plugins and themes against WordPress 5.5 and update the Tested up to version in the readme file to 5.5. If you find compatibility problems, please be sure to post to the support forums, so those can be figured out before the final release.

\n\n\n\n

The WordPress 5.5 Field Guide, due very shortly, will give you a more detailed dive into the major changes.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help us translate WordPress into more than 100 languages! This release also marks the hard string freeze point of the 5.5 release schedule.

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, fill one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8732\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 4\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-4/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 27 Jul 2020 20:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8719\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:313:\"WordPress 5.5 Beta 4 is now available! This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 4 in two ways: Try the WordPress Beta Tester plugin (choose the […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"David Baumwald\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3812:\"\n

WordPress 5.5 Beta 4 is now available!

\n\n\n\n

This software is still in development, so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 4 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 3 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 3, 43 bugs have been fixed. Here are a few changes in beta 4:

\n\n\n\n
  • Add \"loading\" as an allowed kses image attribute (see #50731).
  • Add filter for the plugin/theme auto-update message in the Info tab of Site health (see #50663).
  • $_SERVER[\'SERVER_NAME\'] not a reliable when generating email host names (see #25239)
  • Several backported fixes from Gutenberg are included in WordPress 5.5 Beta 4 (See PR #24218)
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8719\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:20:\"WordPress 5.5 Beta 3\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/07/wordpress-5-5-beta-3/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 21 Jul 2020 17:51:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:11:\"Development\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Releases\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:3:\"5.5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=8706\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:324:\"WordPress 5.5 Beta 3 is now available! This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version. You can test WordPress 5.5 Beta 3 in two ways: Try the WordPress Beta Tester plugin (choose the “bleeding […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Jake Spurlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:3876:\"\n

WordPress 5.5 Beta 3 is now available!

\n\n\n\n

This software is still in development,so it’s not recommended to run this version on a production site. Consider setting up a test site to play with the new version.

\n\n\n\n

You can test WordPress 5.5 Beta 3 in two ways:

\n\n\n\n\n\n\n\n

WordPress 5.5 is slated for release on August 11th, 2020, and we need your help to get there!

\n\n\n\n

Thank you to all of the contributors who tested the beta 2 development release and gave feedback. Testing for bugs is a critical part of polishing every release and a great way to contribute to WordPress.

\n\n\n\n

Some highlights

\n\n\n\n

Since beta 2, 43 bugs have been fixed. Here are a few changes in beta 3:

\n\n\n\n
  • Plugin and theme versions are now shared in the emails when automatically updated (see #50350).
  • REST API routes without a permission_callback now trigger a _doing_it_wrong() warning (see #50075).
  • Over 23 Gutenberg changes and updates (see #24068 and #50712).
  • A bug with the new import and export database Dashicons has been fixed (see #49913).
\n\n\n\n

Developer notes

\n\n\n\n

WordPress 5.5 has lots of refinements to polish the developer experience. To keep up, subscribe to the Make WordPress Core blog and pay special attention to the developers’ notes for updates on those and other changes that could affect your products.

\n\n\n\n

How to Help

\n\n\n\n

Do you speak a language other than English? Help translate WordPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post to the Alpha/Beta area in the support forums. We’d love to hear from you!

\n\n\n\n

If you’re comfortable writing a reproducible bug report, file one on WordPress Trac, where you can also find a list of known bugs.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"8706\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}s:27:\"http://www.w3.org/2005/Atom\";a:1:{s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:4:\"href\";s:32:\"https://wordpress.org/news/feed/\";s:3:\"rel\";s:4:\"self\";s:4:\"type\";s:19:\"application/rss+xml\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:44:\"http://purl.org/rss/1.0/modules/syndication/\";a:2:{s:12:\"updatePeriod\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"\n hourly \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:15:\"updateFrequency\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"\n 1 \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:4:\"site\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"14607090\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:9:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:30 GMT\";s:12:\"content-type\";s:34:\"application/rss+xml; charset=UTF-8\";s:25:\"strict-transport-security\";s:11:\"max-age=360\";s:6:\"x-olaf\";s:3:\"⛄\";s:13:\"last-modified\";s:29:\"Wed, 21 Oct 2020 20:10:31 GMT\";s:4:\"link\";s:63:\"; rel=\"https://api.w.org/\"\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(129,'_transient_timeout_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603427491','no'),(130,'_transient_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1603384291','no'),(131,'_transient_timeout_feed_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(132,'_transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:16:\"WordPress Planet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"en\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WordPress Planet - http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:50:{i:0;a:6:{s:4:\"data\";s:13:\"\n\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Loginizer Plugin Gets Forced Security Update for Vulnerabilities Affecting 1 Million Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106557\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users?utm_source=rss&utm_medium=rss&utm_campaign=loginizer-plugin-gets-forced-security-update-for-vulnerabilities-affecting-1-million-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5484:\"

WordPress.org has pushed out a forced security update for the Loginizer plugin, which is active on more than 1 million websites. The plugin offers brute force protection in its free version, along with other security features like two-factor auth, reCAPTCHA, and PasswordLess login in its commercial upgrade.

\n\n\n\n

Last week security researcher Slavco Mihajloski discovered an unauthenticated SQL injection vulnerability, and an XSS vulnerability, that he disclosed to the plugin’s authors. Loginizer version 1.6.4 was released on October 16, 2020, with patches for the two issues, summarized on the plugin’s blog:

\n\n\n\n

1) [Security Fix] : A properly crafted username used to login could lead to SQL injection. This has been fixed by using the prepare function in PHP which prepares the SQL query for safe execution.

2) [Security Fix] : If the IP HTTP header was modified to have a null byte it could lead to stored XSS. This has been fixed by properly sanitizing the IP HTTP header before using the same.

\n\n\n\n

Loginizer did not disclose the vulnerability until today in order to give users the time to upgrade. Given the severity of the vulnerability, the plugins team at WordPress.org pushed out the security update to all sites running Loginizer on WordPress 3.7+.

\n\n\n\n

In July, 2020, Loginizer was acquired by Softaculous, so the company was also able to automatically upgrade any WordPress installations with Loginizer that had been created using Softaculous. This effort, combined with the updates from WordPress.org, covered a large portion of Loginizer’s user base.

\n\n\n\n
\n

Any #WordPress install with @loginizer probably isn\'t using another WAF solution. As you can notice from the graph 600k+500k active installs were updated upside down, so … Preauth SQLi in it, reported by me. Update! Crunching write up :) https://t.co/gkEVWun9wt pic.twitter.com/XWXVMYO1ED

— mslavco (@mslavco) October 19, 2020
\n
\n\n\n\n

The automatic update took some of the plugin’s users by surprise, since they had not initiated it themselves and had not activated automatic updates for plugins. After several users posted on the plugin’s support forum, plugin team member Samuel Wood said that “WordPress.org has the ability to turn on auto-updates for security issues in plugins” and has used this capability many times.

\n\n\n\n

Mihajloski published a more detailed proof-of-concept on his blog earlier today. He also highlighted some concerns regarding the systems WordPress has in place that allowed this kind of of severe vulnerability to slip through the cracks. He claims the issue could have easily been prevented by the plugin review team since the plugin wasn’t using the prepare function for safe execution of SQL queries. Mihajloski also recommended recurring code audits for plugins in the official directory.

\n\n\n\n

“When a plugin gets into the repository, it must be reviewed, but when is it reviewed again?” Mihajloski said. “Everyone starts with 0 active installs, but what happens on 1k, 10k, 50k, 100k, 500k, 1mil+ active installs?”

\n\n\n\n

Mihajloski was at puzzled how such a glaring security issue could remain in the plugin’s code so long, given that it is a security plugin with an active install count that is more than many well known CMS’s. The plugin also recently changed hands when it was acquired by Softaculus and had been audited by multiple security organizations, including WPSec and Dewhurst Security.

\n\n\n\n

Mihajloski also recommends that WordPress improve the transparency around security, as some site owners and closed communities may not be comfortable with having their assets administered by unknown people at WordPress.org.

\n\n\n\n

“WordPress.org in general is transparent, but there isn’t any statement or document about who, how and when decides about and performs automatic updates,” Mihajloski said. “It is kind of [like] holding all your eggs in one basket.

\n\n\n\n

“I think those are the crucial points that WP.org should focus on and everything will came into place in a short time: complete WordPress tech documentation for security warnings, a guide for disclosure of the bugs (from researchers, but also from a vendor aspect), and recurring code audits for popular plugins.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 22 Oct 2020 03:47:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"Post Status: Joe Casabona on creating quality content and courses\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=80099\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"https://poststatus.com/joe-casabona-on-creating-quality-content-and-courses/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1407:\"

David Bisset interviews Joe Casabona, an independent creator and teacher, and discusses what it\'s like to be a creator as his job, plus some news topics.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Sandhills Development

\n\n\n\n

Sandhills Development crafts ingenuity, developed with care:

\n\n\n\n
  • Easy Digital Downloads – Sell digital products with WordPress
  • AffiliateWP – A full-featured affiliate marketing solution
  • Sugar Calendar – WordPress event management made simple
  • WP Simple Pay – A lightweight Stripe payments plugin
\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:17:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: MakeStories 2.0 Launches Editor for WordPress, Rivaling Google’s Official Web Stories Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106327\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin?utm_source=rss&utm_medium=rss&utm_campaign=makestories-2-0-launches-editor-for-wordpress-rivaling-googles-official-web-stories-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8860:\"Recipe slide from the MakeStories WordPress plugin.\n\n\n\n

Earlier today, MakeStories launched version 2.0 of its plugin for creating Web Stories with WordPress. In many ways, this is a new plugin launch. The previous version simply allowed users to connect their WordPress installs to the MakeStories site. With the new version, users can build and edit their stories directly from the WordPress admin.

\n\n\n\n

Version 2.0 of the plugin still requires an account and a connection with the MakeStories.io website. However, it is simple to set up. Users can log in without leaving their WordPress admin interface. This API connection means that user-created Stories are stored on the MakeStories servers. If an end-user wanted to jump platforms from WordPress to something else, this would allow them to take their Stories with them.

\n\n\n\n

“One of the things we would like to assure is your content is still yours, and none of the user data is being consumed or analyzed by us,” said Pratik Ghela, the founder and product manager at MakeStories. “We only take enough data to help serve you better.”

\n\n\n\n

The plugin is a competing solution to the official Web Stories plugin by Google. While the two share similarities in the final output (they are built to utilize the same front-end format for creating Stories on the web), they take different paths to get there.

\n\n\n\n

The two share similarities on the backend too. However, MakeStories may be more polished in some areas. For example, it allows users to zoom in on the small canvas area. Having the ability to reorder slides from the grid view also feels more intuitive.

\n\n\n\n

“The main unique selling proposition of our plugin is that it comes with a guarantee of the MakeStories team,” said Ghela. “We as a team have been building this for over two years, and we are proud to be one of the tools that has stood the test of time, and competition and is still growing at a very fast pace.”

\n\n\n\n

The team also wants to make the Story-creating process faster, safer, and rewarding. The goal is to cater to designers, developers, and content creators. Ghela also feels like his team’s support turnaround time of a few hours will be the key to success and is a good reason for users to give this plugin a try before settling on something else.

\n\n\n\n

“We feel that our goal is to see Web Stories flourish,” he said. “And we may have different types of users looking out for various options. So, the official plugin from Google and the one from MakeStories at least opens up the options for users to choose from. And we feel that the folks at Google are also building a great editor, and, at the end of the day, it’s up to the user to select what they feel is the best.

\n\n\n\n

Technically, MakeStories is a SaaS (software as a service) product. Even though it is a free plugin, there will eventually be a commercial component to it. Currently, it is free at least until the first quarter of 2021, which may be extended based on various factors. There is no word on what pricing tiers may be available after that.

\n\n\n\n

“There will always be a free tier, and we have always stood for it that your data belongs to you,” said Ghela. “In case you do not like the pricing, we will personally assist you to port out from using our editor and still keep the data and everything totally intact.”

\n\n\n\n

Diving Into the Plugin

\n\n\n\nStory management screen.\n\n\n\n

MakeStories is a drag-and-drop editor for building Web Stories. It works and feels much like typical design editors like Gimp or Photoshop. It shares similarities with QuarkXPress or InDesign, for those familiar with page layout programs. In some ways, it feels a lot like a light-colored version of Google’s Web Stories plugin with more features and a slightly more intuitive interface.

\n\n\n\n

The end goal is simple: create a Story through designing slides/pages that site visitors will click through as the narrative unfolds.

\n\n\n\n

The plugin provides a plethora of shapes, textures, and animations. These features are easy to find and implement. It also includes free access to images, GIFs, and videos. These are made possible via API integrations with Unsplash, Tenor, and Pexels.

\n\n\n\n

MakeStories includes access to 10 templates at the moment. However, what makes this feature stand out is that end-users can create and save custom templates for reuse down the road.

\n\n\n\nEditing a Story from a predesigned template.\n\n\n\n

One of the more interesting, almost hidden, features is the available text patterns. The plugin allows users to insert these patterns from a couple of dozen choices. This makes it easier to visualize a design without having to build everything from scratch.

\n\n\n\nInserting a text pattern and adjusting its size.\n\n\n\n

While the editing process is a carefully-crafted experience that makes the plugin worth a look, it is the actual publishing aspect of the workflow that is a bit painful. Traditional publishing in WordPress means hitting the “publish” button to make content live. This is not the case with the MakeStories plugin. It takes you through a four-step process of entering various publisher details, setting up metadata and SEO, validating the Story content, and analytics. It is not that these steps are necessarily bad. For example, MakeStories lets you know when images are missing alt text, which is needed information screen readers. The problem is that it feels out of place to go through all of these details when I, as a user, simply want my content published. And, many of these details, such as the publisher (author), should be automatically filled in.

\n\n\n\n

Updating a Story is not as simple as hitting an “update” button either. The system needs to run through some of the same steps too.

\n\n\n\n

Ghela said the publishing process might be a bit tough but will prove fruitful in the end. The plugin takes care of the technical aspects of adding title tags, meta, and other data on the front end after the user fills in the form fields.

\n\n\n\n

“We will definitely work on improving the flow as the community evolves and improve it a lot to be easier, faster, and, most importantly, still very customizable,” he said.

\n\n\n\n

The MakeStories team has no plans of stopping at its current point on the roadmap. Ghela sounded excited about some of the upcoming additions they are planning, including features like teams, branding, easy template customization, polls, and quizzes.

\n\n\n\n

On the Web Stories Format

\n\n\n\nUN report on COVID-19 and poverty published with MakeStories.\n\n\n\n

Many will ultimately hesitate to use any plugin that implements Web Stories given Google’s history of dropping projects. There is also a feeling that the format is a bit of a fad and will not stand the test of time.

\n\n\n\n

“We greatly believe in AMP and Web Stories as a content format,” said Ghela. “We, as an agency, have been involved a lot in AMP and have done a lot of experiments with it, including a totally custom WooCommerce site in fully-native, valid AMP with support for variable products, subscriptions, and other functionalities.”

\n\n\n\n

The company is all-in on the format and feels like it will be around for the long term, particularly if there is a good ecosystem around monetization.

\n\n\n\n

“We think that the initial reactions are because there are not enough proven results and because we never imagined the story format to come to the web,” said Ghela. “There were definitely plugins that did this. Few folks tried to build stories using good ol’ HTML, CSS, and JavaScript. But, the performance and UX were not that great. On the other hand, the engineers at the AMP Team are making sure that everything is just perfect. The UX, load time, WCV Score, just everything.”

\n\n\n\n

He feels that some of the early criticisms are unwarranted and that the web development community should give the format a try and provide feedback.

\n\n\n\n

“The more data we all get, actually gives the AMP team a clear idea of what’s needed, and they can design the roadmap accordingly,” he said. “So, just giving out early reactions won’t help, but constructive criticism and getting back to the AMP team with what you are doing will.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 21 Oct 2020 21:12:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"WordPress.org blog: WordPress 5.6 Beta 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9085\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://wordpress.org/news/2020/10/wordpress-5-6-beta-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7956:\"

WordPress 5.6 Beta 1 is now available for testing!

\n\n\n\n

This software is still in development, so we recommend that you run this version on a test site.

\n\n\n\n

You can test the WordPress 5.6 beta in two ways:

\n\n\n\n\n\n\n\n

The current target for final release is December 8, 2020. This is just seven weeks away, so your help is needed to ensure this release is tested properly.

\n\n\n\n

Improvements in the Editor

\n\n\n\n

WordPress 5.6 includes seven Gutenberg plugin releases. Here are a few highlighted enhancements:

\n\n\n\n
  • Improved support for video positioning in cover blocks.
  • Enhancements to Block Patterns including translatable strings.
  • Character counts in the information panel, improved keyboard navigation, and other adjustments to help users find their way better.
  • Improved UI for drag and drop functionality, as well as block movers.
\n\n\n\n

To see all of the features for each release in detail check out the release posts: 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, and 9.2 (link forthcoming).

\n\n\n\n

Improvements in Core

\n\n\n\n

A new default theme

\n\n\n\n

The default theme is making its annual return with Twenty Twenty-One. This theme features a streamlined and elegant design, which aims to be AAA ready.

\n\n\n\n

Auto-update option for major releases

\n\n\n\n

The much anticipated opt-in for major releases of WordPress Core will ship in this release. With this functionality, you can elect to have major releases of the WordPress software update in the background with no additional fuss for your users.

\n\n\n\n

Increased support for PHP 8

\n\n\n\n

The next major version release of PHP, 8.0.0, is scheduled for release just a few days prior to WordPress 5.6. The WordPress project has a long history of being compatible with new versions of PHP as soon as possible, and this release is no different.

\n\n\n\n

Because PHP 8 is a major version release, changes that break backward compatibility or compatibility for various APIs are allowed. Contributors have been hard at work fixing the known incompatibilities with PHP 8 in WordPress during the 5.6 release cycle.

\n\n\n\n

While all of the detectable issues in WordPress can be fixed, you will need to verify that all of your plugins and themes are also compatible with PHP 8 prior to upgrading. Keep an eye on the Making WordPress Core blog in the coming weeks for more detailed information about what to look for.

\n\n\n\n

Application Passwords for REST API Authentication

\n\n\n\n

Since the REST API was merged into Core, only cookie & nonce based authentication has been available (without the use of a plugin). This authentication method can be a frustrating experience for developers, often limiting how applications can interact with protected endpoints.

\n\n\n\n

With the introduction of Application Password in WordPress 5.6, gone is this frustration and the need to jump through hoops to re-authenticate when cookies expire. But don’t worry, cookie and nonce authentication will remain in WordPress as-is if you’re not ready to change.

\n\n\n\n

Application Passwords are user specific, making it easy to grant or revoke access to specific users or applications (individually or wholesale). Because information like “Last Used” is logged, it’s also easy to track down inactive credentials or bad actors from unexpected locations.

\n\n\n\n

Better accessibility

\n\n\n\n

With every release, WordPress works hard to improve accessibility. Version 5.6 is no exception and will ship with a number of accessibility fixes and enhancements. Take a look:

\n\n\n\n
  • Announce block selection changes manually on windows.
  • Avoid focusing the block selection button on each render.
  • Avoid rendering the clipboard textarea inside the button
  • Fix dropdown menu focus loss when using arrow keys with Safari and Voiceover
  • Fix dragging multiple blocks downwards, which resulted in blocks inserted in wrong position.
  • Fix incorrect aria description in the Block List View.
  • Add arrow navigation in Preview menu.
  • Prevent links from being focusable inside the Disabled component.
\n\n\n\n

How You Can Help

\n\n\n\n

Keep your eyes on the Make WordPress Core blog for 5.6-related developer notes in the coming weeks, breaking down these and other changes in greater detail.

\n\n\n\n

So far, contributors have fixed 188 tickets in WordPress 5.6, including 82 new features and enhancements, and more bug fixes are on the way.

\n\n\n\n

Do some testing!

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute.

\n\n\n\n

If you think you’ve found a bug, please post to the Alpha/Beta area in the support forums. We would love to hear from you! If you’re comfortable writing a reproducible bug report, file one on WordPress Trac. That’s also where you can find a list of known bugs.

\n\n\n\n

Props to @webcommsat@yvettesonneveld@estelaris, @cguntur, @desrosj, and @marybaum for editing/proof reading this post, and @davidbaumwald for final review.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 22:14:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"WPTavern: WordPress 5.6 Release Team Pulls the Plug on Block-Based Widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106466\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:193:\"https://wptavern.com/wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-release-team-pulls-the-plug-on-block-based-widgets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8762:\"Current block-based widgets admin screen design.\n\n\n\n

I was wrong. I assured our readers that “the block-based widget system will be ready for prime time when WordPress 5.6 lands” in my previous post on the new feature’s readiness. I also said that was on the condition of not trying to make it work with the customizer — that experience was still broken. However, the 5.6 team pulled the plug on block-based widgets for the second time this year.

\n\n\n\n

One week ago, WordPress 5.6 release lead Josepha Haden seemed to agree that it would be ready. However, things can change quickly in a development cycle, and tough decisions have to be made with beta release deadlines.

\n\n\n\n

This is not the first feature the team has punted to a future release. Two weeks ago, they dropped block-based nav menus from the 5.6 feature list. Both features were originally planned for WordPress 5.5.

\n\n\n\n

A new Widgets admin screen has been under development since January 2019, which was not long after the initial launch of the block editor in WordPress 5.0. For now, the block-based widgets feature has been punted to WordPress 5.7. It has also been given the “early” tag, which means it should go into core WordPress soon after the 5.7 release cycle begins. This will give it more time to mature and more people an opportunity to test it.

\n\n\n\n

Helen Hou-Sandì, the core tech lead for 5.6, provided a historical account of the decision and why it was not ready for inclusion in the new ticket:

\n\n\n\n

My question for features that affect the front-end is “can I try out this new thing without the penalty of messing up my site?” — that is, user trust. At this current moment, given that widget areas are not displayed anything like what you see on your site without themes really putting effort into it and that you have to save your changes live without revisions to get an actual contextual view, widget area blocks do not allow you to try this new feature without penalizing you for experimenting.

\n\n\n\n

She went on to say that the current experience is subpar at the moment. Problems related to the customizer experience, which I covered in detail over a month ago, were also mentioned.

\n\n\n\n

“So, when we come back to this again, let’s keep sight of what it means to keep users feeling secure that they can get their site looking the way they want with WordPress, and not like they are having to work around what we’ve given them,” said Hou-Sandì.

\n\n\n\n

This is a hopeful outlook despite the tough decision. Sometimes, these types of calls need to be made for the good of the project in the long term. Pushing back a feature to a future version for a better user experience can be better than launching early with a subpar experience.

\n\n\n\n

“The good part of this is that now widgets can continue to be ‘re-imagined’ for 5.7, and get even more enhancements,” said lead WordPress developer Andrew Ozz in the ticket. “Not sure how many people have tested this for a bit longer but having blocks in the widgets areas (a.k.a. sidebars) opens up many new possibilities and makes a lot of the old, limited widgets obsolete. The ‘widget areas’ become something like ‘specialized posts with more dynamic content,’ letting users (and designers) do a lot of stuff that was either hard or impossible with the old widgets.”

\n\n\n\n

After the letdown of seeing one of my most anticipated features of 5.6 being dropped, it is encouraging to see the positive outlook from community leaders on the project.

\n\n\n\n

“You know, I was really hopeful for it too, and that last-minute call was one I labored over,” said Haden. “When I last looked, it did seem close to ready, but then more focused testing was done and there were some interactions that are a little rough for users. I’m grateful for that because the time to discover painful user experiences is before launch rather than after!”

\n\n\n\n

Despite dropping its second major feature, WordPress 5.6 still has some big highlights that will be shipping in less than two months. The new Twenty Twenty-One theme looks to be a breath of fresh air and will explore block-related features not seen in previous default themes. Haden also pointed out auto-updates for major releases, application passwords support for the REST API, and accessibility improvements as features to look forward to.

\n\n\n\n

WordPress 5.6 Beta 1 is expected to ship today.

\n\n\n\n

Adding New Features To an Old Project

\n\n\n\n

At times, it feels like the Gutenberg project has bitten off more than it can chew. Many of the big feature plans continually miss projections. Between full-site editing, global styles, widgets, nav menus, and much more, it is tough to get hyper-focused on one feature and have it ready to ship. On the other hand, too much focus one way can be to the detriment to other features in the long run. All of these pieces must eventually come together to create a more cohesive whole.

\n\n\n\n

WordPress is also 17 years old. Any new feature could affect legacy features or code. The goal for block-based widgets is to transition an existing feature to work within a new system without breaking millions of websites in the process. Twenty-one months of work on a single feature shows that it is not an easy problem to solve.

\n\n\n\n

“You are so right about complex engineering problems!” said Haden. “We are now at a point in the history of the project where connecting all of the pieces can have us facing unforeseen complications.”

\n\n\n\n

The project also needs to think about how it can address some of the issues it has faced with not quite getting major features to completion. Is the team stretched too thin to focus on all the parts? Are there areas we can improve to push features forward?

\n\n\n\n

“There will be a retrospective where we can identify what parts of our process can be improved in the future, but I also feel like setting stretch goals is good for any software project,” said Haden. “Many contributors have a sense of urgency around bringing the power of blocks to more spaces in WordPress, which I share, but when it’s time to ship, we have to balance that with our deep commitment to usability.”

\n\n\n\n

One problem that has become increasingly obvious is that front-end editing has become tougher over the years. Currently, widgets and nav menus can be edited in two places in WordPress with wildly different interfaces. Full-site editing stands to add an entirely new interface to the mix.

\n\n\n\n

“I think one of the problems that we’re trying to solve with Gutenberg has always been a more consistent experience for editing elements across the WordPress interface,” said Haden. “No user should have to learn five different workflows to make sure their page looks the way they imagined it when it’s published.”

\n\n\n\n

In the meantime, which may be numbered in years, end-users will likely have these multiple interfaces to deal with — overlap while new features are being developed. This may simply be a necessary growing pain of an aging project, one that is trying to lead the pack of hungry competitors in the CMS space.

\n\n\n\n

“There’s a lot of interest in reducing the number of workflows, and I’m hopeful that we can consolidate down to just one beautiful, intuitive interface,” said Haden.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 21:16:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce Tests New Instagram Shopping Checkout Feature, Now in Closed Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106398\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-tests-new-instagram-shopping-checkout-feature-now-in-closed-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2878:\"

Instagram’s checkout feature, which allows users to purchase products without leaving the app, has become an even more important part of Facebook’s long-term investment in e-commerce now that the pandemic has so heavily skewed consumer behavior towards online shopping. When Instagram introduced checkout in 2019, it reported that 130 million users were tapping to reveal product tags in shopping posts every month.

\n\n\n\nimage credit: Instagram\n\n\n\n

Business owners who operate an existing store can extend their audience to Instagram by funneling orders from the social network into their own stores, without shoppers having to leave Instagram. Checkout supports integration with several e-commerce platform partners, including Shopify and BigCommerce, and will soon be available for WooCommerce merchants.

\n\n\n\n

WooCommerce is testing a new Instagram Shopping Checkout feature for its Facebook for WooCommerce plugin. The free extension is used on more than 900,000 websites and will provide the bridge for store owners who want to tap into Instagram’s market. The checkout capabilities are currently in closed beta. Anyone interested to test the feature can sign up for consideration. Businesses registered in the USA that meet certain other requirements may be selected to participate, and the beta is also expanding to other regions soon.

\n\n\n\n

WooCommerce currently supports shoppable posts, which are essentially products sourced from a product catalog created on Facebook that are then linked to the live store through an Instagram business account. Instagram’s checkout takes it one step further to provide a native checkout experience inside the app. Merchants pay no selling fees until December 31, 2020. After that time, the fee is 5% per shipment or a flat fee of $0.40 for shipments of $8.00 or less. 

\n\n\n\n

On the customer side, shoppers only have to enter their information once and thereafter it is stored for future Instagram purchases. Instagram also pushes shipment and delivery notifications inside the app. Store owners will need to weigh whether the convenience of the in-app checkout experience is worth forking over 5% to Facebook, or if they prefer funneling users over to the live store instead.

\n\n\n\n

Instagram Shopping Checkout is coming to WooCommerce in the near future but the company has not yet announced a launch date, as the feature is just now entering closed beta.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Oct 2020 04:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Past Twenty* WordPress Themes To Get New Block Patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106396\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/past-twenty-wordpress-themes-to-get-new-block-patterns?utm_source=rss&utm_medium=rss&utm_campaign=past-twenty-wordpress-themes-to-get-new-block-patterns\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6608:\"

Mel Choyce-Dwan, the Default Theme Design Lead for WordPress 5.6, kick-started 10 tickets around two months ago that would bring new features to the old default WordPress themes. The proposal is to add unique block patterns, a feature added to WordPress 5.5, to all of the previous 10 Twenty* themes. It is a lofty goal that could breathe some new life into old work from the previous decade.

\n\n\n\n

Currently, only the last four themes are marked for an update by the time WordPress 5.6 lands. Previous themes are on the list to receive their block patterns in a future release. For developers and designers interested in getting involved, the following is a list of the Trac tickets for each theme:

\n\n\n\n\n\n\n\n

If you are wondering where Twenty Eighteen is in that list, that theme does not actually exist. It is the one missing year the WordPress community has had since the one-default-theme-per-year era began with Twenty Ten. It is easy to forget that we did not get a new theme for the 2017-2018 season. With all that has happened in the world this year, we should count ourselves fortunate to see a new default theme land for WordPress this December. WordPress updates and its upcoming default theme are at least one consistency that we have had in an otherwise chaotic time.

\n\n\n\n

More than anything, it is nice to see some work going toward older themes — not just in terms of bug fixes but feature updates. The older defaults are still a part of the face of WordPress. Twenty Twenty and Twenty Seventeen each have over one million active installs. Twenty Nineteen has over half a million. The other default themes also have significant user bases in the hundreds of thousands — still some of the most-used themes in the directory. We owe it to those themes’ users to keep them fresh, at least as long as they maintain such levels of popularity.

\n\n\n\n

This is where the massive theme development community could pitch in. Do some testing of the existing patches. Write some code for missing patterns or introduce new ideas. This is the sort of low-hanging fruit that almost anyone could take some time to help with.

\n\n\n\n

First Look at the New Patterns

\n\n\n\n

None of the proposed patterns have landed in trunk, the development version of WordPress, yet. However, several people have created mockups or added patches that could be committed soon.

\n\n\n\n

One of my favorite patterns to emerge thus far is from Beatriz Fialho for the Twenty Nineteen theme. Fialho has created many of the pattern designs proposed thus far, but this one, in particular, stands out the most. It is a simple two-column, two-row pattern with a circular image, heading, and paragraph for each section. Its simplicity fits in well with the more elegant, business-friendly look of the Twenty Nineteen theme.

\n\n\n\nServices pattern for Twenty Nineteen.\n\n\n\n

It is also fitting that Twenty Nineteen get a nice refresh with new patterns because it was the default theme to launch with the block editor. Ideally, it would continually be updated to showcase block-related features.

\n\n\n\n

While many people will focus on some of the more recent default themes, perhaps the most interesting one is a bit more dated. Twenty Thirteen was meant to showcase the new post formats feature in WordPress 3.6. According to Joen Asmussen, the theme’s primary designer, the original idea was for users to compose a ribbon of alternating colors as each post varied its colors.

\n\n\n\n

“The alternating ribbon of colors did not really come to pass because post formats were simply not used enough to create an interesting ribbon,” he wrote in the Twenty Thirteen ticket. “However, perhaps for block patterns, we have an opportunity to revisit those alternating ribbons of colors. In other words, I’d love to see those warm bold colors used in big swathes that take up the whole pattern background.”

\n\n\n\n
Patterns designed to match post formats.\n\n\n\n

This could be a fun update for end-users who are still using that feature that shall not be named post formats.

\n\n\n\n

There is a lot to like about many of the pattern mockups so far. I look forward to seeing what lands along with WordPress 5.6 and in future updates.

\n\n\n\n

Establishing Pattern Category Standard

\n\n\n\n

With the more recent Twenty Twenty-One theme’s block patterns and the new patterns being added to some of the older default themes, it looks like a specific pattern category naming scheme is starting to become a standard. Of the patches thus far, each is creating a new pattern category named after the theme itself.

\n\n\n\n

This makes sense. Allowing users to find all of their theme’s patterns in one location means that they can differentiate between them and those from core or other plugins. Third-party theme authors should follow suit and stick with this convention for the same reason.

\n\n\n\n

Developers can also define multiple categories for a single pattern. This allows theme authors to create a category that houses all of their patterns in one location. However, they can also split them into more appropriate context-specific categories for discoverability.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 19 Oct 2020 21:13:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"BuddyPress: BuddyPress 7.0.0-beta1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://buddypress.org/?p=315150\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://buddypress.org/2020/10/buddypress-7-0-0-beta1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4332:\"

BuddyPress 7.0.0-beta1 is now available for testing!

\n\n\n\n

Please note the plugin is still in development, so we recommend running this beta release on a testing site.

\n\n\n\n

You can test BuddyPress 7.0.0-beta1 in 4 ways :

\n\n\n\n\n\n\n\n

The 7.0.0 stable release is slated to the beginning of December, and we’d love you to give us a hand to get there!

\n\n\n\n

Please note BuddyPress 7.0.0 will require at least WordPress 4.9.

\n\n\n\n

Testing for bugs is an important part of polishing the release during the beta stage and a great way to contribute. Here are some of the big changes and features to pay close attention to while testing (Check out this report on Trac for the full list).

\n\n\n\n
\n\n\n\n

New Administration screens to manage BuddyPress types

\n\n\n\n

In BuddyPress 7.0.0 site administrators will be able to add, edit or delete Member & Group types using their WordPress Administration Screens just like they would do for Post tags.

\n\n\n\n

Read this development note to learn more about it.

\n\n\n\n
\n\n\n\n

Let’s welcome 3 new BP Blocks into our Block Editor

\n\n\n\n
  • The Activity Embed block let authors embed an activity into their post or page.
  • Use the BP Members block to select community users you want to feature into a post or a page.
  • Enjoy the BP Groups block to pick the groups you want to highlight into a post or a page.
\n\n\n\n

Get to know these new blocks reading this development note.

\n\n\n\n
\n\n\n\n

Improved support for WP CLI

\n\n\n\n

WP-CLI is the command-line interface for WordPress. You can update plugins, configure multisite installs, and much more, without using a web browser. In 7.0.0, you will be able to Enjoy new BuddyPress CLI commands to manage BuddyPress Group Meta, BuddyPress Activity Meta, activate or deactivate the BuddyPress signup feature and create BuddyPress specific testing code for plugins.

\n\n\n\n

Discover more about it from this development note.

\n\n\n\n
\n\n\n\n

And so much more such as improvements to the BP REST API, our Template pack, images and iframes lazy loading support…

\n\n\n\n
\n\n\n\n

How You Can Help

\n\n\n\n

Do you speak a language other than English? Help us translate BuddyPress into more than 100 languages!

\n\n\n\n

If you think you’ve found a bug, you can post in the support forums. We’d love to hear from you! If you’re comfortable writing a reproducible bug report, file one on BuddyPress Trac.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 22:30:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:12:\"Mathieu Viet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:89:\"WPTavern: Using the Web Stories for WordPress Plugin? You Better Play By Google’s Rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105848\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:215:\"https://wptavern.com/using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules?utm_source=rss&utm_medium=rss&utm_campaign=using-the-web-stories-for-wordpress-plugin-you-better-play-by-googles-rules\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4080:\"Web Stories dashboard screen in WordPress.\n\n\n\n

What comes as a surprise to few, Google has updated its content guidelines for its Web Stories format. For users of its recently-released Web Stories for WordPress plugin, they will want to follow the extended rules for their Stories to appear in the “richer experiences” across Google’s services. This includes the grid view on Search, Google Images, and Google Discover’s carousel.

\n\n\n\n

Google released its Web Stories plugin in late September to the WordPress community. It is a drag-and-drop editor that allows end-users to create custom Stories from a custom screen in their WordPress admin.

\n\n\n\n
Visual Stories on Search.
\n\n\n\n

The plugin does not directly link to Google’s content guidelines anywhere. For users who do not do a little digging, they may be caught unaware if their stories are not surfaced in various Google services.

\n\n\n\n

On top of the Discover and Webmaster guidelines, Web Stories have six additional restrictions related to the following:

\n\n\n\n
  • Copyrighted content
  • Text-heavy Web Stories
  • Low-quality assets
  • Lack of narrative
  • Incomplete stories
  • Overly commercial
\n\n\n\n

While not using copyrighted content is one of those reasonably-obvious guidelines, the others could trip up some users. Because Stories are meant to represent bite-sized bits of information on each page, they may become ineligible if most pages have more than 180 words of text. Videos should also be limited to fewer than 60 seconds on each page.

\n\n\n\n

Low-quality media could be a flag for Stories too. Google’s guidelines point toward “stretched out or pixelated” media that negatively impacts the reader’s experience. They do not offer any specific resolution guidelines, but this should mostly be a non-issue today. The opposite issue is far more likely — users uploading media that is too large and not optimized for viewing on the web.

\n\n\n\n

The “lack of narrative” guideline is perhaps the vaguest, and it is unclear how Google will monitor or police narrative. However, the Stories format is about storytelling.

\n\n\n\n

“Stories are the key here imo,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Google specifically states that Stories need a “binding theme or narrative structure” from one page to the next. Essentially, the company is telling users to use the format for the purpose it was created for. They also do not want users to create incomplete stories where readers must click a link to finish the Story or get information.

\n\n\n\nCNN’s Web Story on Remembering John Lennon.\n\n\n\n

Overly commercial Stories are frowned upon too. While Google will allow affiliate marketing links, they should be restricted to a minor part of the experience.

\n\n\n\n

Closing his Twitter thread, Marsland seemed to hit the point. “I’ve seen some initial Google Web Stories where the platform is being used as a replacement for a brochure or website,” he wrote. “In my view that’s a huge missed opportunity. If I was advising brands I would say ‘Tell Stories’ this is a platform for Story Telling.”

\n\n\n\n

If users of the plugin follow this advice, their Stories should surface on Google’s rich search experiences.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Oct 2020 20:51:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:45:\"WPTavern: Stripe Acquires Paystack for $200M+\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106269\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:131:\"https://wptavern.com/stripe-acquires-paystack-for-200m?utm_source=rss&utm_medium=rss&utm_campaign=stripe-acquires-paystack-for-200m\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3196:\"

The big news in the world of e-commerce today is Stripe’s acquisition of Paystack, a Nigeria-based payments system that is widely used throughout African markets. The company, which became informally known as “the Stripe of Africa” picked up $8 million in Series A funding in 2018, led by Stripe, Y Combinator, and Tencent. Paystack has grown to power more than 60,000 businesses, including FedEx, UPS, MTN, the Lagos Internal Revenue Service, and AXA Mansar.

\n\n\n\n

Stripe’s acquisition of the company is rumored to be more than $200M, a small price to pay for a foothold in emerging African markets. In the company’s announcement, Stripe noted that African online commerce is growing 21% year-over-year, 75% faster than the global average. Paystack dominates among payment systems, accounting for more than half of all online transactions in Nigeria.

\n\n\n\n

“In just five years, Paystack has done what many companies could not achieve in decades,” Stripe EMEA business lead Matt Henderson said. “Their tech-first approach, values, and ambition greatly align with our own. This acquisition will give Paystack resources to develop new products, support more businesses and consolidate the hyper-fragmented African payments market.”

\n\n\n\n

Long term, Stripe plans to embed Paystack’s capabilities in its Global Payments and Treasury Network (GPTN), the company’s programmable infrastructure for global money movement.

\n\n\n\n

“Paystack merchants and partners can look forward to more payment channels, more tools, accelerated geographic expansion, and deeper integrations with global platforms,” Paystack CEO and co-founder Shola Akinlade said. He also assured customers that there’s no need to make any changes to their technical integrations, as Paystack will continue expanding and operating independently in Africa.

\n\n\n\n

Paystack is used as a payment gateway for thousands of WordPress-powered stores through plugins for WooCommerce, Easy Digital Downloads, Paid Membership Pro, Give, Contact Form 7, and an assortment of booking plugins. The company has an official WordPress plugin, Payment Forms for Paystack, which is active on more than 6,000 sites, but most users come through the Paystack WooCommerce Payment Gateway (20,000+ active installations).

\n\n\n\n

Stripe’s acquisition was a bit of positive news during what is currently a turbulent time in Nigeria, as citizens are actively engaged in peaceful protests to end police brutality. Paystack’s journey is an encouraging example of the flourishing Nigerian tech ecosystem and the possibilities available for smaller e-commerce companies that are solving problems and removing barriers for businesses in emerging markets.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 22:26:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:10;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WPTavern: Diving Into the Book Review Block Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106273\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:145:\"https://wptavern.com/diving-into-the-book-review-block-plugin?utm_source=rss&utm_medium=rss&utm_campaign=diving-into-the-book-review-block-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6791:\"

Created by Donna Peplinskie, a Product Wrangler at Automattic, the Book Review Block plugin is nearly three years old. However, it only came to my attention during a recent excursion to find interesting block plugins.

\n\n\n\n

The plugin does pretty much what it says on the cover. It is designed to review books. It generally has all the fields users might need to add to their reviews, such as a title, author, image, rating, and more. The interesting thing is that it can automatically fill in those details with a simple ISBN value. Plus, it supports Schema markup, which may help with SEO.

\n\n\n\n

Rain or shine, sick or well, I read every day. I am currently a month and a half shy of a two-year reading streak. When the mood strikes, I even venture to write a book review. As much as I want to share interesting WordPress projects with the community, I sometimes have personal motives for testing and writing about plugins like Book Review Block. Anything that might help me or other avid readers share our thoughts on the world of literature with others is of interest.

\n\n\n\n

Admittedly, I was excited as I plugged in the ISBN for Rhthym of War, the upcoming fourth book of my favorite fantasy series of all time, The Stormlight Archive. I merely needed to click the “Get Book Details” button.

\n\n\n\n

Success! The plugin worked its magic and pulled in the necessary information. It had my favorite author’s name, the publisher, the upcoming release date, and the page count. It even had a long description, which I could trim down in the editor.

\n\n\n\nDefault output of the Book Review block.\n\n\n\n

There was a little work to make this happen before the success. To automatically pull in the book details, end-users must have an API Key from Google. It took me around a minute to set that up and enter it into the field available in the block options sidebar. The great thing about the plugin is that it saves this key so that users do not have to enter each time they want to review a book.

\n\n\n\n

Book Review Block a good starting point. It is straightforward and simple to use. It is not yet at a point where I would call it a great plugin. However, it could be.

\n\n\n\n

Falling Short

\n\n\n\n

The plugin’s Book Review block should be taking its cues from the core Media & Text block. When you get right down to it, the two are essentially doing the same thing visually. Both are blocks with an image and some content sitting next to each other.

\n\n\n\n

The following is a list of items where it should be following core’s lead:

\n\n\n\n
  • No way to edit alt text (book title is automatically used).
  • The image is always aligned left and the content to the right with no way to flip them.
  • The media and content are not stackable on mobile views.
  • Cannot adjust the size of the image or content columns.
  • While inline rich-text controls are supported, users cannot add Heading, List, or Paragraph blocks to the content area and use their associated block options.
\n\n\n\n

That is the shortlist that could offer some quick improvements to the user experience. Ultimately, the problems with the plugin essentially come down to not offering a way to customize the output.

\n\n\n\n

One of the other consistent problems is that the book image the plugin loads is always a bit small. This seems to be more of an issue from the Google Books API than the plugin. Each time I tested a book, I opted to add a larger image — the plugin does allow you to replace the default.

\n\n\n\n

The color settings are limited. The block only offers a background color option with no way to adjust the text color. A better option for plugin users is to wrap it in a Group block and adjust the background and text colors there.

\n\n\n\nBook Review block wrapped inside a Group block.\n\n\n\n

It would also be nice to have wide and full-alignment options, which is an often-overlooked featured from many block plugin authors.

\n\n\n\n

Using the Media & Text Block to Recreate the Book Review Block

\n\n\n\n

The Book Review Block plugin has a lot of potential, and I want to see it evolve by providing more flexibility to end-users. Because the Media & Text block is the closest core block to what the plugin offers, I decided to recreate a more visually-appealing design with it.

\n\n\n\nBook review section created with the Media & Text block.\n\n\n\n

I made some adjustments on the content side of things. I used the Heading block for the book title, a List block for the book metadata, and a Paragraph block for the description.

\n\n\n\n

The Media & Text block also provided me the freedom to adjust the alignment, stack the image and content on mobile views, and tinker with the size of the image. Plus, it has that all-important field for customizing the image alt attribute.

\n\n\n\n

The Media & Text block gave me much more design mileage.

\n\n\n\n

However, there are limitations to the core block. It does not fully capture some of the features available via the Book Review block. The most obvious are the automatic book details via an ISBN and the Schema markup. Less obvious, there is no easy way to recreate the star rating — I used emoji stars — and long description text does not wrap under the image. To recreate that, you would have to opt to use a left-aligned image followed by content.

\n\n\n\n

Overall, the Media & Text block gives me the ability to better style the output, which is what I am more interested in as a user. I want to put my unique spin on things. That is where the Book Review Plugin misfires. It is also the sort of thing that the plugin author can iterate on, offering more flexibility in the future.

\n\n\n\n

This is where many block plugins go wrong, particularly when there is more than one or two bits of data users should enter. Blocks represent freedom in many ways. However, when plugin developers stick to a rigid structure, users can sometimes lose that sense of freedom that they would otherwise have with building their pages.

\n\n\n\n

One of the best blocks, hands down, that preserves that freedom is from the Recipe Block plugin. It has structured inputs and fields. However, it allows freeform content for end-users to make it their own.

\n\n\n\n

When block authors push beyond this rigidness, users win.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 20:44:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:11;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: WooCommerce 4.6 Makes New Home Screen the Default for New and Existing Stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores?utm_source=rss&utm_medium=rss&utm_campaign=woocommerce-4-6-makes-new-home-screen-the-default-for-new-and-existing-stores\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3018:\"

WooCommerce 4.6 was released today. The minor release dropped during WooSesh, a global, virtual conference dedicated to WooCommerce and e-commerce topics. It features the new home screen as the default for all stores. Previously, the screen was only the default on new stores. Existing store owners had to turn the feature on in the settings.

\n\n\n\n
\n\n\n\n

The updated home screen, originally introduced in version 4.3, helps store admins see activity across the site at a glance and includes an inbox, quick access to store management links, and an overview of stats on sales, orders, and visitors. This redesigned virtual command center arrives not a moment too soon, as anything that makes order management more efficient is a welcome improvement, due to the sheer volume of sales increases that store owners have seen over the past eight months.

\n\n\n\n

In stark contrast to industries like hospitality and entertainment that have proven to be more vulnerable during the pandemic, e-commerce has seen explosive growth. During the State of the Woo address at WooSesh 2020, the WooCommerce team shared that e-commerce is currently estimated to be a $4 trillion market that will grow to $4.5 trillion by 2021. WooCommerce accounts for a sizable chunk of that market with an estimated total payment volume for 2020 projected to reach $20.6 billion, a 74% increase compared to 2019.

\n\n\n\n

The WooCommerce community is on the forefront of that growth and is deeply invested in the products that are driving stores’ success. The WooCommerce team shared that 75% of people who build extensions also build and maintain stores for merchants, and 70% of those who build stores for merchants also build and maintain extensions or plugins. In 2021, they plan to invest heavily in unlocking more features in more countries and will make WooCommerce Payments the native payment method for the global platform.

\n\n\n\n

A new report from eMarketer shows that US e-commerce growth has jumped 32.4%, accelerating the online shopping shift by nearly two years. Experts also predict the top 10 e-commerce players will swallow up more of US retail spending to account for 63.2% of all online sales this year, up from 57.9% in 2019.

\n\n\n\n

The increase in e-commerce spending may not be entirely tied to the pandemic, as some experts believe this historic time will mark permanent changes in consumer spending habits. This is where independent stores, powered by WooCommerce and other technologies, have the opportunity to establish a strong reputation for themselves by providing quality products and reliable service, as well as by being more nimble in the face of pandemic-driven increases in volume.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 15 Oct 2020 03:48:32 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:12;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:101:\"WPTavern: The Future of Starter Content: WordPress Themes Need a Modern Onboarding and Importing Tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106177\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:245:\"https://wptavern.com/the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool?utm_source=rss&utm_medium=rss&utm_campaign=the-future-of-starter-content-wordpress-themes-need-a-modern-onboarding-and-importing-tool\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7385:\"Image credit: picjumbo.com on Pexels.\n\n\n\n

Starter content. It was a grand idea, one of those big dreams of WordPress. It was the new kid on the block in late 2016. Like the introduction of post formats in 2011, the developer community was all in for at least that particular release version. Then, it was on to the next new thing, with the feature dropping off the radar for all but the most ardent evangelists.

\n\n\n\n

Some of us were burned over the years, living and dying by the progress of features that we wanted most.

\n\n\n\n

Released in WordPress 4.7, starter content has since seemed to be going the way of post formats. After four years, only 141 themes in the WordPress theme directory support the feature. There has been no movement to push it beyond its initial implementation. And, it never really covered the things that theme authors wanted in the first place. It was a start. But, themers were ultimately left to their own devices, rolling custom solutions for something that never panned out — fully-featured demo and imported content. Four years is an eternity in the web development world, and there is no sense in waiting around to see if WordPress would push forward.

\n\n\n\n

Until Helen Hou-Sandí published Revisiting Starter Content last week, most would have likely assumed the feature would be relegated to legacy code used by old-school fans of the feature and those theme authors who consider themselves completionists.

\n\n\n\n

“Starter content in 4.7 was always meant to be a step one, not the end goal or even the resting point it’s become,” wrote Hou-Sandí. “There are still two major things that need to be done: themes should have a unified way of showing users how best to put that theme to use in both the individual site and broader preview contexts, and sites with existing content should also be able to take advantage of these sort of ‘ideal content’ definitions.”

\n\n\n\n

Step two should have been this four-year-old accompanying ticket to allow users to import starter content into existing, non-fresh sites.

\n\n\n\n

Since the initial feature dropped, the theme landscape has changed. Let’s face it. WordPress might simply not be able to compete with theme companies that are pushing the limits, creating experiences that users want at much swifter speeds.

\n\n\n\n

Look at where the Brainstorm Force’s Starter Templates plugin for its Astra theme is now. Users can click a button and import a full suite of content-filled pages or even individual templates. And, the Astra theme is not alone in this. It has become an increasingly-common standard to offer some sort of onboarding to users. GoDaddy’s managed WordPress service fills a similar need on the hosting end.

\n\n\n\nAstra’s starter templates and content.\n\n\n\n

As WordPress use becomes more widespread, the more it needs a way to onboard users.

\n\n\n\n

This essentially boils down to the question: how can I make it look like the demo?

\n\n\n\n

Ah, the age-old question that theme authors have been trying to solve. Whether it has been limitations in the software or, perhaps, antiquated theme review guidelines related to demo and imported content, this has been a hurdle that has been tough to jump. But, some have sailed over it and moved on. While WordPress has seemingly been twiddling its thumbs for years, Brainstorm Force and other theme companies have solved this and continued to innovate.

\n\n\n\n

This is not necessarily a bad thing. There are plenty of ideas to steal copy and pull into the core platform.

\n\n\n\n

One of the other problems facing the WordPress starter content feature is that it is tied to the customizer. With the direction of the block system, it is easy to ask what the future holds. The customizer — originally named the theme customizer — was essentially a project to allow users to make front-end adjustments and watch those customizations happen in real time. However, new features like global styles and full-site editing are happening on their own admin screens. Most theme options will ultimately be relegated to global styles, custom templates, block styles, and block patterns. There may not be much left for the customizer to do.

\n\n\n\n

Right now, there are too many places in WordPress to edit the front-end bits of a WordPress site. My hope is that all of these things are ultimately merged into one less-confusing interface. But, I digress…

\n\n\n\n

Starter content should be rethought. Whoever takes the reins on this needs a fresh take that adopts modern methods from leading theme companies.

\n\n\n\n

The ultimate goal should be to allow theme authors to create multiple sets of templates/content that end-users can preview and import. It should not be tied to whether it is a new site. Any site owner should be able to import content and have it automagically go live. It should also be extendable to allow themes to support page builders like Elementor, Beaver Builder, and many others.

\n\n\n\n

This seems to be in line with Hou-Sandí’s thoughts. “For a future release, we should start exploring what it might look like to opt into importing starter content into existing sites, whether wholesale or piecewise,” she wrote. “Many of us who work in the WordPress development/consulting space tend not to ever deal in switching between public themes on our sites, but let’s not forget that’s a significant portion of our user audience and we want to continue to enable them to not just publish but also publish in a way that matches their vision.”

\n\n\n\n

Let’s do it right this go-round, keep a broad vision, and provide an avenue for theme authors to adopt a standardized core WordPress method instead of having everyone build in-house solutions.

\n\n\n\n

I haven’t even touched on the recent call to use starter content for WordPress.org theme previews. It will take more than ideas to excite many theme authors about the possibility. That ticket has sat for seven years with no progress, and most have had it on their wish list for much longer. It is an interesting proposal, one that has been tossed around in various team meetings for years.

\n\n\n\n

Like so many other things, theme authors have either given up hope or moved onto doing their own thing. They need to be brought into the fold, not only as third parties who are building with core WordPress tools but as developers who are contributing to those features.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 14 Oct 2020 20:07:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:13;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:116:\"WPTavern: Google Podcasts Manager Adds More Data from Search: Impressions, Top-Discovered Episodes, and Search Terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:271:\"https://wptavern.com/google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms?utm_source=rss&utm_medium=rss&utm_campaign=google-podcasts-manager-adds-more-data-from-search-impressions-top-discovered-episodes-and-search-terms\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2568:\"

Google announced an expansion of listener engagement metrics today for those using its Podcast Manager. Previously, audience insights included data about the types of devices listeners are using, where listeners tune in and drop off during a given episode, total number of listens, and listening duration, but the service lacked analytics regarding how visitors were discovering the podcast.

\n\n\n\n

Google is remedying that today by expanding the dashboard to show impressions, clicks, top-discovered episodes, and search terms that brought listeners to the podcast. This information can help podcasters understand how their content is getting discovered so they can better tailor their episodes to attract more new listeners.

\n\n\n\n

The podcasting industry has seen remarkable growth over the past five years, which previously led experts to project that marketers will spend over $1 billion in advertising by 2021. After the pandemic hit, podcast listening took a downturn in the U.S. but at the same time, podcast creators have found more time to create new shows and episodes. Businesses are turning to the medium to supplement traditional marketing methods that no longer have the same impact now that consumer spending habits heavily favor online products.

\n\n\n\n

Along with the new metrics available inside Google Podcasts Manager, the company also published a guide to optimizing podcasts for Google Search. It highlights four important items for making sure a podcast can be found:

\n\n\n\n
  • Detailed show and episode metadata
  • Ensure the podcast’s webpage and RSS data match
  • Include cover art
  • Ensure Googlebot can access your audio files
\n\n\n\n

A detailed breakdown of your audience’s listening habits isn’t worth much if you’re having trouble getting your podcast discovered. Any podcasting plugin for WordPress should handle these basic optimization recommendations, but if you are still having trouble being found via Google, you can dig deeper into the podcast setup guide for more detailed recommendations.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 22:57:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:14;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Are Block-Based Widgets Ready To Land in WordPress 5.6?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/are-block-based-widgets-ready-to-land-in-wordpress-5-6?utm_source=rss&utm_medium=rss&utm_campaign=are-block-based-widgets-ready-to-land-in-wordpress-5-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8214:\"

Two weeks ago, the Gutenberg team put out an open call for block-based widgets feedback. I had already written a lengthy review of the new system earlier in September but was asked by a member of the team to share my thoughts on the most recent iteration. With the upcoming freeze for WordPress 5.6 Beta 1 just a week away, I figured it would not hurt to do another deep dive.

\n\n\n\n

For reference, my latest testing is against version 9.2.0-alpha-172f589 of the Gutenberg plugin, which was a build from earlier today. Gutenberg development moves fast, but everything should be accurate to that point.

\n\n\n\n

Ultimately, many of the problems I pointed out over a month ago still exist. However, the team has cleaned most of the minor issues, such as pointing the open/close arrows for sidebars (block areas) in the correct direction and making it more consistent with the post-editing screen. The UI is much more polished.

\n\n\n\n

Before I dive into all the problems, I want to answer the question I am proposing. Yes, the block-based widget system will be ready for prime time when WordPress 5.6 lands. It is not there yet, but it is at a point where there is a clear finish line that is reachable in the next two months.

\n\n\n\n

I will ignore the failure of block-based widgets in the customizer, which landed in Gutenberg 8.9 and was removed in 9.1. I will also look past the recent proposal to reconstruct the widgets screen to use the Customize API, at least for now. There is a boatload of problems that block-based widgets present for the customizer, and those problems are insurmountable for WordPress 5.6. Long term, WordPress needs to have a single place for editing widget/block areas. Users will likely have to live with some inconsistencies for a while.

\n\n\n\n

Assuming the team does not try to throw a last-minute Hail Mary and implement full editing of blocks in the customizer this round, it is safe to say that block-based widgets are well on their way toward a successful WordPress 5.6 debut.

\n\n\n\n

The User Experience

\n\n\n\nBlock-based widgets screen.\n\n\n\n

As a user, I genuinely enjoy using the new Widgets admin screen. The open-ended, free-form block areas create untold possibilities for designing my WordPress sites. Traditional widgets were limited in scope. Users were buckled down to a handful of core widgets, possibly some plugin widgets, and whatever their theme author offered up. However, with blocks, the pool of choices expands to at least triple the out-of-the-box options (I am not counting embed-type blocks individually). Plus, blocks provide a far more extensive set of design options than a traditional widget.

\n\n\n\n

In comparison, traditional widgets are outdated. Blocks are superior in almost every way. However, there are still problems with this new system.

\n\n\n\n

The biggest issue right now is that end-users can exit the Widgets screen without saving their changes. There is no warning to let them know that all their work is about to be lost in the ether. This is one of those OMGBBQ-level items that need to happen before WordPress 5.6 drops.

\n\n\n\n

One nice-to-have-but-not-necessary feature would be the ability to drag blocks from one block area to another. In the old widgets system, users could move widgets from sidebar to sidebar. The current alternative is to copy a widget, paste it in a new block area, and remove the original.

\n\n\n\n

I am also not a fan of not having an option for the top toolbar, which is available on the post-editing screen. One of the reasons for using this toolbar is because I dislike the default popup toolbar on individual blocks. It is distracting and often gets in the way of my work.

\n\n\n\n

Legacy widgets seem to still be a work in progress. The Legacy Widget block did not work at all for me at times. Then, it magically began to work. However, Gutenberg does now automatically add registered third-party widgets to the block inserter just as if they were blocks.

\n\n\n\nGetting a plugin’s widget to work.\n\n\n\n

This presented its own problems. The only way I managed to make third-party plugin widgets work was to insert the widget, save, and refresh the widgets screen. At that point, the widgets appeared and became editable.

\n\n\n\n

The Theme Author Experience

\n\n\n\n

One of my biggest concerns for theme authors right now is that there does not seem to be any documentation in the block editor handbook. There is plenty of time to make that happen, but there are things theme authors need to be aware of. Having a centralized location, even while the feature is under development, would help them gear up for the 5.6 release.

\n\n\n\n

Some of these questions, which may be answered in various Make blog posts, should exist on a dedicated documentation page:

\n\n\n\n
  • How can a theme opt out of block-based widgets?
  • What are the hooks to add custom styles for the Widgets screen?
  • Can themes target specific sidebar styles on the Widgets screen?
  • Is it possible to consistently style sections like traditional widgets on the front end?
  • Can themes opt into wide and full-alignment within block areas, which could essentially be used similarly to the post content area?
\n\n\n\n

These are some of the questions I would want to be answered as a former theme author. I am no longer in the thick of the theme design game and presume that those who are would have a larger list of questions.

\n\n\n\n

One less-obvious piece of documentation should center on how to handle fallbacks or default widgets. Traditionally, themes that needed to show a default set of widgets would check if the sidebar has widgets and fall back to using the_widget() to output one or more defaults. While theme authors can still do that, we should start to transition them across the board to the block system.

\n\n\n\n

Should theme authors copy/paste block HTML as a fallback? Would the starter content system be better for this, and can starter widget content handle blocks? What is the recommended method for widget fallbacks in WordPress 5.6?

\n\n\n\n

There is still the ongoing issue of how theme authors should handle the traditional widget and widget title wrapper HTML in the new block paradigm. One patch added since the Gutenberg 9.1 release wraps every top-level block with the widget wrapper. If this lands in the 9.2 release, it will likely make the issue worse.

\n\n\n\n

In the traditional system, both the widget title and content are wrapped within a container together. However, if a user adds a Heading block (widget title) and another block (widget content), each block is wrapped separately with the theme’s widget wrappers. The only way to rectify the situation as it stands is for end-users to add a Group block for each “widget” they want, which would require an extensive amount of re-education for WordPress users. It is not an ideal scenario.

\n\n\n\nEach block is wrapped as an individual section.\n\n\n\n

Instead of attempting to directly “fix” this issue, WordPress should instead do nothing to the output. Blocks and traditional widgets are fundamentally different.

\n\n\n\n

Let theme authors take the reins on this one and explore possibilities. However, give them the tools to do so, such as supporting block patterns.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 13 Oct 2020 21:35:39 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:15;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: WordCamp Austin 2020 Finds Success with VR Experience for Sessions and Networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=106119\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking?utm_source=rss&utm_medium=rss&utm_campaign=wordcamp-austin-2020-finds-success-with-vr-experience-for-sessions-and-networking\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7246:\"

WordCamp Austin 2020 attendees are raving about their experiences attending the virtual event last Friday. It was no secret that the camp’s organizers planned to use Hubs Virtual Rooms by Mozilla to create a unique environment, but few could imagine how much more interactive and personalized the experience would be than a purely Zoom-based WordCamp.

\n\n\n\n

After selecting a custom avatar, attendees entered the venue using a VR headset or the browser to check out sessions or network in the hallway track.

\n\n\n\n
\n

Here’s a small taste of the experience at @WordCampATX today. #WordPress logos and no sponsor banners on any elevator doors. #WCATX pic.twitter.com/Nv2p2VchXf

— David Bisset (@dimensionmedia) October 9, 2020
\n
\n\n\n\n

Speaker and Q&A sessions were broadcast through Zoom but organizers can also embed YouTube videos and streams within the standalone VR environment.

\n\n\n\n

“The VR experience was the most life-like WordCamp experience I’ve had since the start of global lockdowns,” attendee and speaker David Vogelpohl said. “You could attend sessions in one of two virtual presentation halls depending on what track you wanted to see at that time. The speaker presented on a virtual stage and you could see the other attendees watching the presentation.”

\n\n\n\n

Vogelpohl said he enjoyed his experience getting to know others in the Slack and VR venue. Organizers preserved the general vibe of the “hallway track” to recreate what is arguably one of the most valuable aspects of in-person WordCamps.

\n\n\n\n
\n

So cool – checking out the Virtual Space of WordCamp Austin – love the background noise of people talking, ran into @ChrisWiegman and @Josh412 #WCATX pic.twitter.com/68EdgDN2Om

— Birgit Pauli-Haack (@bph) October 9, 2020
\n
\n\n\n\n

“In the hallway track between the virtual presentation halls was a large foyer where you could meet new people, spot a friend speaking with someone else, and virtually step aside from a group conversation to have a private conversation,” Vogelpohl said.

\n\n\n\n

“It was great to see folks like Josepha circling around speaking with attendees, Josh Pollock nerding out in a corner with a group of advanced WP developers, and having random friends drop into a conversation I was having with a group of others. While VR WordCamp doesn’t wholly replace the value of attending a WordCamp live, a lot of the best parts of meeting and collaborating with others was captured in the VR context.”

\n\n\n\n

The live music interludes, which showcased talents from around the community, also provided a way for virtual attendees to stay connected while waiting for the next session.

\n\n\n\n

Behind the Scenes with Anthony Burchell: Creative Director for WordCamp Austin’s Virtual World

\n\n\n\n

WordPress core contributor Anthony Burchell, who started a company dedicated to creating interactive XR sound and art experiences, was the creative director behind the WordCamp Austin’s VR backdrop.

\n\n\n\n

“For WordCamp Austin we wanted to give folks something to be excited about outside of the typical webcam and chat networking,” Burchell said. “I feel that virtual events are not utilizing the networking layer nearly enough to make folks feel like they are really at an event. I’ve seen many compelling formats for virtual events utilizing webcams and chat rooms, but in the end, it feels like there’s been a missing element of presence; something video games and virtual reality excel at.”

\n\n\n\n
\n

Virtual mission control for #WCATX pic.twitter.com/WyrFkIsW2Q

— Anthony Burchell (@antpb) October 9, 2020
\n
\n\n\n\n

Setting up the virtual world involves spinning up a self-hosted instance of Hubs Cloud, which Burchell said is very similar to the complexity of making a WordPress site.

\n\n\n\n

“The most time consuming part of creating a 3D world for an event is making the 3D assets for the space,” Burchell said. “In total I streamed 11 hours of video leading up to the event to give a glimpse into the process.”

\n\n\n\n

Burchell’s YouTube playlist documents the incredible amount of work that went into creating the WordCamp’s virtual venue for attendees to enjoy.

\n\n\n\n

“While it took quite a bit of time to prepare, the code and assets are completely reusable for another event,” Burchell said. “A lot of the time was spent trying to make the space purpose built for the goals of the camp. Much like a real WordCamp, I found the majority of folks packing into the theater rooms for presentations and dipping out a little early to network with friends in the hallway area. That was very much by design!”

\n\n\n\n

Burchell and the other organizers were careful to ensure that the Hubs space was not the primary viewing experience of the camp but rather an extension of the networking activities that attendees could drop in on. The event had nearly identical numbers of attendees joining the virtual space as it did for those joining the video channels. At the end of the afterparty, Burchell turned on flying for all attendees to conclude the successful event:

\n\n\n\n
\n\n
\n\n\n\n

“With Hubs we were able to give attendees the ability to express themselves within a venue vs within a camera and chat box,” Burchell said. “It was incredible to see characteristics of folks in the community shine through a virtual avatar! Just the simple act of seeing your WordCamp friends in the hallway joking and chatting just as they would at a real life event was enough to make me feel like I was transported to a real WordCamp.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 22:31:02 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:16;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Privacy-Conscious WordPress Plugin Caches and Serves Gravatar Images Locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105825\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally?utm_source=rss&utm_medium=rss&utm_campaign=privacy-conscious-wordpress-plugin-caches-and-serves-gravatar-images-locally\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5285:\"

Ari Stathopoulos released his new Local Gravatars plugin last week. The goal of the plugin is to allow site owners to take advantage of the benefits of a global avatar system while mitigating privacy concerns by hosting the images locally.

\n\n\n\n

In essence, it is a caching system that stores the images on the site owner’s server. It is an idea that Peter Shaw proposed in the comments on an earlier Tavern article covering local avatar upload. It is a middle ground that may satisfy some users’ issues with how avatars currently work in WordPress.

\n\n\n\n

“I am one of the people that blocks analytics, uses private sessions when visiting social sites, I use DuckDuckGo instead of Google, and I don’t like the ‘implied’ consents,” said Stathopoulos. “I built the plugin for my own use because I don’t know what Gravatar does, I don’t understand the privacy policies, and I am too lazy to spend two hours analyzing them. It’s faster for me to build something that is safe and doesn’t leave any room for misunderstandings.”

\n\n\n\n

He is referring to Automattic’s extensive Privacy Policy. He said it looks benign. However, he does not like the idea of any company being able to track what sites he visits without explicit consent.

\n\n\n\n

“And when I visit a site that uses Gravatar, some information is exposed to the site that serves them — including my IP,” said Stathopoulos. “Even if it’s just for analytics purposes, I don’t think the company should know that page A on site B got 1,000 visitors today with these IPs from these countries. There is absolutely no reason why any company not related to the page I’m actually visiting should have any kind of information about my visit.”

\n\n\n\n

The Local Gravatars plugin must still connect to the Gravatar service. However, the connection is made on the server rather than the client. Stathopoulos explained that the only information exposed in this case is the server’s IP and nothing from the client, which eliminates any potential privacy concerns.

\n\n\n\n

The Latest Plugin Update

\n\n\n\n

Stathopoulos updated the plugin earlier today to address some performance concerns for pages that have hundreds or more Gravatar images. In the version 1.0.1 update, he added a maximum processing time of five seconds and changed the cache cleanup process from daily to weekly. Both of these are filterable via code.

\n\n\n\n

“Now, if there are Gravatars missing in a page request, it will get as many as it can, and, after five seconds, it will stop,” said Stathopoulos. “So if there are 100 Gravatars missing and it gets the first 20, the rest will be blank (can be filtered to use a fallback URL, or even fall back to the remote URL, though that would defeat the privacy improvement). The next page request will get the next 20, and so on. At some point, all will be there, and there will be no more delays.”

\n\n\n\n

He did point out that performance could temporarily suffer when installing it on a site that has individual posts with 1,000s of comments and a lot of traffic. However, nothing would crash on the site, and the plugin should eventually lead to a performance boost in this scenario. For such large sites, owners could use the existing filter hooks to tweak the settings.

\n\n\n\n

Right now, the plugin is primarily an itch he wanted to scratch for his own purposes. However, if given enough usage and feedback, he may include a settings screen to allow users to control some of the currently-filterable defaults, such as the cleanup timeframe and the maximum process time allowed.

\n\n\n\n

The Growing List of Alternatives

\n\n\n\n

With growing concerns around privacy in the modern world, Local Gravatars is another tool that end-users can employ if they have any concerns around the Gravatar service. For those who are OK with an auto-generated avatar, Pixel Avatars may be a solution.

\n\n\n\n

“I’ve seen some of them, and they are wonderful!” Stathopoulos said of alternatives for serving avatars. “However, this plugin is slightly different in that the avatars the user already has on Gravatar.com are actually used. They can see the image they have uploaded. The user doesn’t need to upload a separate avatar, and an automatic one is not used by default.”

\n\n\n\n

He would not mind using an auto-generated avatar when commenting on blogs or news sites at times. However, Stathopoulos prefers Gravatar for community-oriented sites.

\n\n\n\n

“My Gravatar is part of my online identity, and when I see, for example, a comment from someone on WordPress.org, I know who they are by their Gravatar,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 12 Oct 2020 21:06:20 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:17;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: WordPress 5.6 to Introduce Application Passwords for REST API Authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105997\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2604:\"

In 2015, WordPress 4.4 introduced a REST API, but one thing that has severely limited its broader use is the lack of authentication capabilities for third-party applications. After considering the benefits and drawbacks of many different types of authentication systems, George Stephanis published a proposal for integrating Application Passwords, into core.

\n\n\n\n

Stephanis highlighted a few of the major benefit that were important factors in the decision to use Application Passwords: the ease of making API requests, ease of revoking credentials, and the ease of requesting API credentials. The project is available as a standalone feature plugin, but Stephanis and his collaborators recommended WordPress merge a pull request that is based off the feature plugin’s codebase.

\n\n\n\n

After WordPress 5.6 core tech lead Helen Hou-Sandi gave the green light for Application Passwords to be merged into core, the developer community responded enthusiastically to the news.

\n\n\n\n

“I am/we are 100% in favor of this,” Joost deValk commented on the proposal. “Opening this up is like opening the dawn of a new era of WordPress based web applications. Suddenly authentication is not something you need to fix when working with the API and you can just build awesome stuff.”

\n\n\n\n

Stephanis’ proposal also mentioned how beneficial a REST API authentication system would be for the Mobile teams‘ contributors who are relying on awkward workarounds while integrating Gutenberg support.

\n\n\n\n

“This would be a first step to replace the use of XMLRPC in the mobile apps and it would allow us to add more features for self hosted users,” Automattic mobile engineer Maxime Biais said.

\n\n\n\n

After the REST API was added to WordPress five years ago, many had the expectation that WordPress-based web applications would start popping up everywhere. Without a reliable authentication system, it wasn’t easy for developers to just get inspired and build something quickly. Application Passwords in WordPress 5.6 will open up a lot of possibilities for those who were previously deterred by the lack of core methods for authenticating third-party access.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 23:01:31 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:18;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"WPTavern: WP Agency Summit Begins Its Second Annual Virtual Event October 12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105160\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:197:\"https://wptavern.com/wp-agency-summit-begins-its-second-annual-virtual-event-october-12?utm_source=rss&utm_medium=rss&utm_campaign=wp-agency-summit-begins-its-second-annual-virtual-event-october-12\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6357:\"

Jan Koch, the founder and host of WP Agency Summit, is kicking off his second annual event on October 12. The five-day event will feature 37 speakers from a wide range of backgrounds across the WordPress industry. It is a free virtual event that anyone can attend.

\n\n\n\n

“The focus for the 2020 WP Agency Summit is showing attendees how to bring back the fun into scaling their agencies,” said Koch. “It is all about reducing the daily hustle by teaching how to successfully build and manage teams, how to work with enterprises (allowing for fewer customers but bigger projects), how to build sustainable recurring revenue, and how to position your agency to dominate your niche.”

\n\n\n\n

This year’s event includes three major changes to make the content more accessible to a larger group of people. Each session will be available between October 12 – 16 instead of the previous 48-hour window that attendees had to find time for in 2019.

\n\n\n\n

After the event has concluded, access to the content will be behind a paywall. Koch reduced the price to $77 for lifetime access for those who purchase pre-launch, which will increase to $127 during the event. Last year’s prices ballooned to $497, which meant that it was simply not affordable for many who found it too late.

\n\n\n\n

Some of the proceeds this year are going toward transcribing all the videos so that hearing-impaired users can enjoy the content.

\n\n\n\n

This year’s event will also focus on a virtual networking lounge for attendees. “I’ve seen how well it worked at the WP FeedBack Summit — we even had BobWP record a podcast episode on the fly in that lounge!” said Koch. “I’ve seen many new friendships develop, people connecting with new suppliers or getting themselves booked on podcasts, and sharing experiences about their businesses.”

\n\n\n\n

The lounge will be open during the entirety of the summit, which will allow attendees to jump into the conversation on their own time.

\n\n\n\n

A More Diverse Speaker Lineup

\n\n\n\n

Koch received some backlash for the lack of gender diversity last year. The 2019 event had over 20 speakers from a diverse male lineup. However, only four women from our industry led sessions.

\n\n\n\n

When asked about this issue in 2019, Koch responded, “I recognize this as a problem with my event. The reason I have so much more male than female speakers is quite simple, the current speaker line-up is purely based on connections I had when I started planning for the event. It was a relatively short amount of time for me, so I wasn’t able to build relationships with more female WP experts beforehand.”

\n\n\n\n

The host said he paid attention to the feedback he received. While not hitting the 50/50 split goal he had for 2020’s event, 16 of the 37 speakers are women.

\n\n\n\n

Koch said he strived to get speakers from a wider range of backgrounds. He wanted to bring in both freelancers and multi-million dollar agency owners. He also focused on getting people from multiple countries to represent WordPress agencies.

\n\n\n\n

“I did reach out to around 130 people four months before the event to make new connections,” he said. “The community around the Big Orange Heart (a non-profit for mental well-being) also helped a lot with introducing me to new members of the WP community.”

\n\n\n\n

Koch said he learned two valuable lessons when branching out beyond his existing connections for this year’s event:

\n\n\n\n

Firstly, don’t hesitate to reach out to people you think will never talk to you because they’re running such big companies. For example, I immediately got confirmations from Mario Peshev from Devrix, Brad Touesnard from Delicious Brains, or Marieke van de Rakt from Yoast. When first messaging them, I had little hope they’d set aside time to jump on an interview with me – but they were super supportive and accommodating! The WordPress community really is a welcoming environment if you approach people in a humble way.

Secondly, build connections with sincerity. Do not just focus on what you can get from that connection but how you can help the other person. I know this sounds cheesy and you’ve heard this quite often — but it is true. Once I got the first response from new contacts and explained my goal of connecting fellow WordPress community members virtually, most immediately agreed because they also benefit from new connections and being positioned as a thought-leader in this event.

\n\n\n\n

WP Agency Summit? WP FeedBack Summit?

\n\n\n\n

For readers who recall the Tavern’s coverage of the WP FeedBack Summit earlier this year, the article specifically stated that the WP FeedBack Summit was a continuation of 2019’s WP Agency Summit. The official word at the time from WP FeedBack’s public relations team was the following:

\n\n\n\n

Last year’s event, the WP Agency Summit has been rebranded under the umbrella of WP FeedBack’s brand when Jan Koch the host of last’s year WP Agency Summit joined WP FeedBack as CTO.

\n\n\n\n

Koch said that it was a standalone event and not directly connected to WP Agency Summit but had the same target audience. However, the WP FeedBack Summit did use the previous WP Agency Summit’s stats and data to promote the event.

\n\n\n\n

“The WP FeedBack Summit was hosted under the WP FeedBack brand because I joined their team as CTO in March this year,” he said. “Vito [Peleg] and I had the idea to host a virtual conference around WordPress because of WordCamp Asia being canceled — we wanted to help connect the community online through our summit.

\n\n\n\n

Koch left WP FeedBack soon after the summit ended and is currently back on his own and has a goal of making WP Agency Summit a yearly event.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 09 Oct 2020 17:01:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:19;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: Navigation Screen Sidelined for WordPress 5.6, Full-Site Editing Edges Closer to Public Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105839\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:247:\"https://wptavern.com/navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta?utm_source=rss&utm_medium=rss&utm_campaign=navigation-screen-sidelined-for-wordpress-5-6-full-site-editing-edges-closer-to-public-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4676:\"

The new block-based navigation screen is once again delayed after it was originally slated for WordPress 5.5 and then put on deck for 5.6. Contributors have confirmed that it will not be landing in WordPress core until 2021 at the earliest.

\n\n\n\n

“The Navigation screen is still in experimental state in the Gutenberg plugin, so it hasn’t had any significant real-world use and testing yet,” Editor Tech Lead Isabel Brison said. She made the call to remove it from the 5.6 lineup after the feature missed the deadline for bringing it out of the experimental state. It still requires a substantial amount of development work and accessibility feedback before moving forward.

\n\n\n\n

Contributors will focus instead on making sure the Widgets screen gets out the door for 5.6 and plan to pick up again on Navigation towards the end of November.

\n\n\n\n

WordPress 5.6 lead Josepha Haden gave an update this week on the progress of all the anticipated features, including the planned public beta for full-site editing (FSE).

\n\n\n\n

“I don’t expect FSE to be feature complete by the time WP5.6 is released,” Haden said. “What I expect is that FSE will be functional for simple, routine user flows, which we can start testing and iterating on. That feedback will also help us more confidently design and build our complex user flows.”

\n\n\n\n

Frank Klein, an engineer at Human Made, asked in the comments of another update why full-site editing is being tied to 5.6 progress in the first place, since it will still only be available in the plugin at the time of release.

\n\n\n\n

“The main value is that it provides a good checkpoint along the path of FSE’s development,” Kjell Reigstad said. “Full-site editing is very much in progress. It is still experimental, but the general approach is coming into view, and becoming clearer with every plugin release.”

\n\n\n\n

Reigstad posted an update on what developers can expect regarding block-based theming and the upcoming release, since the topic is closely tied to full-site editing. He emphasized that the infrastructure is already in place and that, despite it still being experimental, future block-based themes should work in a similar way to how they are working now.

\n\n\n\n

“The focus is now shifting towards polishing the user experience: using the site editor to create templates, using the query block, iterating on the post and site blocks, and implementing the Global Styles UI,” Reigstad said.

\n\n\n\n

“The main takeaway is that when 5.6 is released, the full-site editing feature set will look similar to where it is today, with added polish to the UI, and additional features in the Query block.”

\n\n\n\n

Theme authors are entering a new time of uncertainty and transition, but Reigstad reassured the community that themes as we know them today are not on track to be phased out in the immediate future.

\n\n\n\n

“There is currently no plan to deprecate the way themes are built today,” Reigstad said. “Your existing themes will continue to work as they always have for the foreseeable future.” He also encouraged contributors to get involved in an initiative to help theme authors transition to block-based themes. (This project is not targeted for the 5.6 release.)

\n\n\n\n

Developers can follow important FSE project milestones on GitHub, and subscribe to the weekly Gutenberg + Themes updates to track progress on block-based theming. A block-based version of the Twenty Twenty-One theme is in the works and should pick up steam after 5.6 beta 1, expected on October 20.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 22:57:37 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:20;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:68:\"WPTavern: EditorPlus 1.9 Adds Animation Builder for the Block Editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105678\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:181:\"https://wptavern.com/editorplus-1-9-adds-animation-builder-for-the-block-editor?utm_source=rss&utm_medium=rss&utm_campaign=editorplus-1-9-adds-animation-builder-for-the-block-editor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4535:\"

Munir Kamal shows no signs of slowing down. He continues to push forward with new features for his EditorPlus plugin, which allows end-users to customize the look of the blocks in their posts and pages. He calls it the “no-code style editor for WordPress.”

\n\n\n\n

The latest addition to his plugin? Animation styles for every core block.

\n\n\n\n

My first thought was that this would bloat the plugin with large amounts of unnecessary CSS and JavaScript for what is essentially a few bells and whistles. However, Kamal pulled it off with minimal custom CSS.

\n\n\n\n

Inspired by features from various website builders, he wanted to bring more and more of those things to the core block editor. The animations feature is just another ticked box on a seemingly never-ending checklist of features. And, so far, it’s all still free.

\n\n\n\n

Since we last covered EditorPlus in June, Kamal has added the ability to insert icons via any rich-text area (e.g., paragraphs, lists, etc.). He has also added shape divider, typography, style copying, and responsive editing options for the core WordPress blocks.

\n\n\n\n

How Do Animations Work?

\n\n\n\n

In the version 1.9 release of EditorPlus, Kamal added “entrance” animations. These types of animations happen when a visitor sees the block for the first time on the screen. For example, users could set the Image block to fade into visibility as a reader views the block.

\n\n\n\n

Currently, the plugin adds seven animations:

\n\n\n\n
  • Fade
  • Slide
  • Bounce
  • Zoom
  • Flip
  • Fold
  • Roll
\n\n\n\nAdding a Slide animation for the Cover block text.\n\n\n\n

Each animation has its own subset of options to control how it behaves on the page. The bounce animation, for example, allows users to select the bounce direction. Other options include duration, delay, speed curve, delay, and repeat. There are enough choices to spend an inordinate amount of time tinkering with the output.

\n\n\n\n

One of the best features of this new feature is that Kamal has included an Animation Player under the block options. By clicking the play button, users can view the animation in action without previewing the post.

\n\n\n\n

Watch a quick video of the Animations feature:

\n\n\n\n
\n\n
\n\n\n\n

After testing and using each animation, everything seemed to work well. The one downside — and this is not limited to animations — is that applying styles on the block level sometimes does not make sense. In many cases, it would help users to have options to style or animate the items within the block, such as the images in the Gallery block. When I broached the subject with Kamal, he was open to the idea of finding a solution to this in the future.

\n\n\n\n

What Is Next for EditorPlus?

\n\n\n\n

At a certain point, too many block options can almost feel like overkill and become unwieldy. EditorPlus does allow users to disable specific features from its settings screen, which can help get rid of some unwanted options. Kamal said he would like to continue making it more modular so that users can use only the features they need.

\n\n\n\n

“What I plan is to have micro-level feature control for this extension so that a user can switch off individual styling panels like, Typography, Background, etc.,” he said. “Even further, I plan to bring these controls based on the user role as well. So an admin can disable these features for the editor, author, etc.”

\n\n\n\n

That may be a bit down the road though. For now, he wants to focus on adding new features that he already has planned.

\n\n\n\n

“I do plan to add more animation features,” said Kamal. “I got too many ideas, such as scroll-controlled animation, hover animation, text animation, Lottie animation, background animation, animated shape dividers, and more. But, having said that, I will be careful adding only those features that don’t affect page performance much.”

\n\n\n\n

Outside of extra styles and animations for existing blocks, he plans to jump on the block-building train in future releases. EditorPlus users could see accordion, toggle, slider, star rating, and other blocks in an upcoming release.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:53:40 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:21;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"Donncha: Hide featured image if it’s in the post\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://odd.blog/?p=89503242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"https://odd.blog/2020/10/08/hide-featured-image-if-its-in-the-post/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3885:\"

I’ve been running a photoblog at inphotos.org since 2005 on WordPress. (And thanks to writing this I noticed it’s 15 years old today!)

\n\n\n\n
\n\n\n\n

In that time WordPress has changed dramatically. At first I used Flickr to host my images, but after a short time I hosted the images myself. (Good thing too since Flickr limited free user accounts to 1000 images, so I wrote a script to download the Flickr images I used in posts.)

\n\n\n\n
\n\n\n\n

For quite a long time I used the featured image instead of inserting the image into the post content, but then about two years ago I went back to inserting the photo into the post. Unfortunately that meant the photo was shown twice, once as a featured image, and once in the post content.

\n\n\n\n

The last theme I used supported custom post types, one of which was a photo type that displayed the featured image but hid the post content. It was an ok compromise, but not perfect.

\n\n\n\n
\n\n\n\n

Recently I started using Twenty Twenty, but after 15 years I had a mixture of posts with:

\n\n\n\n
  • Featured image with no image in the post.
  • Featured image with the same image in the post.
\n\n\n\n

I knew I needed something more flexible. I wanted to hide the featured image if it also appeared in the post content. I procrastinated and never got around to it until this evening when I discovered it was actually quite easy.

\n\n\n\n\n\n\n\n

Copy the following code into the function.php of your child theme and you’ll be all set! It relies on you having unique filenames for your images. If you don’t then remove the call to basename(), and that may help.

\n\n\n
\nfunction maybe_remove_featured_image( $html ) {\n        if ( $html == \'\' ) {\n                return \'\';\n        }\n        $post = get_post();\n        $post_thumbnail_id = get_post_thumbnail_id( $post );\n        if ( ! $post_thumbnail_id ) {\n                return $html;\n        }\n\n        $image_url = wp_get_attachment_image_src( $post_thumbnail_id );\n        if ( ! $image_url ) {\n                return $html;\n        }\n\n        $image_filename = basename( parse_url( $image_url[0], PHP_URL_PATH ) );\n        if ( strpos( $post->post_content, $image_filename ) ) {\n                return \'\';\n        } else {\n                return $html;\n        }\n}\nadd_filter( \'post_thumbnail_html\', \'maybe_remove_featured_image\' );\n
\n\n\n

The post_thumbnail_html filter acts on the html generated to display the featured image. My code above gets the filename of the featured image, checks if it’s in the current post and if it is returns a blank string. Feedback welcome if you have a better way of doing this!

\n\n\n\n
\n\n\n\n

\n\n

Related Posts

\n

Source

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 20:43:35 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Donncha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:22;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches Automatic Platform Optimization for WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105641\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-automatic-platform-optimization-for-wordpress?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-automatic-platform-optimization-for-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6128:\"

Just a day after launching its new privacy-first web analytics product last week, Cloudflare announced Automatic Platform Optimization (APO) for WordPress. The new service boasts staggering performance improvements for sites that might otherwise be slowed down by shared hosting, slow database lookups, or sluggish plugins:

\n\n\n\n

Our testing… showed a 72% reduction in Time to First Byte (TTFB), 23% reduction to First Contentful Paint, and 13% reduction in Speed Index for desktop users at the 90th percentile, by serving nearly all of your website’s content from Cloudflare’s network. 

\n\n\n\n

APO uses Cloudflare Workers to cache dynamic content and serve the website from its edge network. In most cases this eliminates origin requests and origin processing time. That means visitors requesting your website will get near instant load times. Cloudflare reports that its testing shows APO delivers consistent load times of under 400ms for HTML Time to First Byte (TTFB).

\n\n\n\n

The effects of using APO are similar to hosting static files on a CDN, but without the need to manage a complicated tech stack. Content creators retain their ability to create dynamic websites without any changes to their workflow for the sake of performance.

\n\n\n\n

Version 3.8 of Cloudflare’s official WordPress plugin was recently updated to include support for APO. It detects when users make changes to their content and purges the content stored on Cloudflare’s edge.

\n\n\n\n

The new service is available to Cloudflare users with a single click of a button. APO is included at no cost for existing Cloudflare customers on the Professional, Business, and Enterprise plans. Users on the Free plan can add it to their sites for $5/month. The service is a flat fee and is not metered.

\n\n\n\n

Cloudflare’s announcement has so far been well-received by WordPress professionals and hosting companies and many have already begun testing it.

\n\n\n\n
\n

So the week after @Cloudflare Birthday Week I try and play with as many of the new products as possible. Today was the WordPress APO on my simple demo site. You can see TTFB dropped from ~350ms to ~75ms! https://t.co/zg976EjrZI pic.twitter.com/KuaHqtHLom

— Matt Bullock (@mibullock) October 6, 2020
\n
\n\n\n\n

WordPress lead developer Mark Jaquith called APO “incredible news for the WordPress world.”

\n\n\n\n

“On sites I manage this is going to lower hosting complexity and easily save hundreds of dollars a month in hosting costs,” Jaquith said.

\n\n\n\n

After running several speed tests from six different locations around the world, early testers at Kinsta got remarkable results using APO:

\n\n\n\n

“By caching static HTML on Cloudflare’s edge network, we saw a 70-300% performance increase. As expected, the testing locations furthest away from Tokyo saw the biggest reduction in load time.

“If your WordPress site uses a traditional CDN that only caches CSS, JS, and images, upgrading to Cloudflare’s WordPress APO is a no-brainer and will help you stay competitive with modern Jamstack and static sites that live on the edge by default.”

\n\n\n\n

George Liu, a “self-confessed page speed addict” and Cloudflare Community MVP, performed a series of detailed tests on the new APO product with his blog. After many comparisons, he found that Cloudoflare’s WordPress plugin with APO turned on delivers results similar to his heavily optimized WordPress blog that uses a custom Cloudflare Worker caching configuration.

\n\n\n\n

“You’ll find that Cloudflare WordPress plugin’s one click Automatic Platform Optimization button does wonders for page speed for the average WordPress user not well versed in page speed optimizations,” Liu said.

\n\n\n\n

“Cloudflare’s WordPress plugin Automatic Platform Optimization will in theory beat all other WordPress caching solutions other than you rolling out your own Cloudflare Worker based caching like I did. So you get a good bang for your buck at US$5/month for Cloudflare’s WordPress plugin APO.”

\n\n\n\n

Liu also warned of some speed bumps with the initial rollout, as Cloudflare’s APO supports a limited set of WordPress cookies for bypassing the Cloudflare CDN cache, leaving certain use cases unsupported. APO does not seem to work on subdomains and users are also reporting that it’s not compatible with other caching plugins. It also disables real visitor IP address detection.

\n\n\n\n

Cloudflare is aware of many of these issues, which have been raised in the comments of the announcement, and is in the process of adding more cookies to the list to bypass caching. Due to some plugin conflicts, APO may not be as plug-and-play as it sounds for some users right now, but the product is very promising and should improve over time with more feedback.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 08 Oct 2020 04:18:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:23;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: Kick off Block-Based WordPress Theme Development With the Theme.json Creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105832\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:217:\"https://wptavern.com/kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator?utm_source=rss&utm_medium=rss&utm_campaign=kick-off-block-based-wordpress-theme-development-with-the-theme-json-creator\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4674:\"

Gutenberg 9.1 made a backward-incompatible change to its theme.json file (experimental-theme.json while full-site editing is under the experimental flag). This is the configuration file that theme developers will need to create as part of their block-based themes. Staying up to date with such changes can be a challenge for theme authors, but Ari Stathopoulos, a Themes Team representative, wrote a full guide for developers.

\n\n\n\n

Jon Quach, a Principal Designer at Automattic, has also been busy creating a tool to help theme authors transition to block-based themes. He recently built a UI-based project called Theme.json Creator that builds out the JSON code for theme authors. Plus, it is up to date with the most recent changes in the Gutenberg plugin.

\n\n\n\n

Tools like these will be what the development community needs as it gets over the inevitable hump of moving away from the traditional theme development paradigm and into a new era where themes are made almost entirely of blocks and a config file.

\n\n\n\n

While plugin development is becoming more complex with the addition of JavaScript, theme development is taking a sharp turn toward its roots of HTML and CSS. We are barreling toward a future in which far more people will be able to create WordPress themes. Even the possibility of sharing pieces of themes (e.g., template parts and patterns) is on the table. This could not only empower theme designers by lowering the barrier to entry, it could also empower some end-users to make the jump into theme building.

\n\n\n\n

However, the theme.json file is one aspect of future theme authorship that is extremely developer-oriented. JSON is a universal format shared between various programming languages. It is meant to be read by machines and is not quite as human-friendly as other formats. As the theme.json file grows to accommodate more configuration options over time, the less friendly it will become to simply typing keys and values in.

\n\n\n\n

It makes sense to build tools to simplify this part of the theme building process.

\n\n\n\n

That is where the Theme.json Creator tool comes in. Theme authors pick and choose the options they want to support and input custom values. Then, the tool spits out everything in properly-formatted JSON.

\n\n\n\nUsing the Theme.json Creator tool.\n\n\n\n

One big thing the tool does not yet cover is custom CSS variables. This feature is a recent addition to the theme.json specification. It allows theme authors to create any custom property that WordPress will automatically output as CSS. In his announcement post, Stathopoulos covered how to create a typographic scale with custom properties and use those variables for editor features, such as line-height and font-size values.

\n\n\n\n

Currently, Theme.json Creator’s primary focus is on global styles. However, Gutenberg allows theme authors to configure default styles on the block level. For example, theme designers can set the color or typography options for the core Heading block to be different from the default global styles. This provides theme authors with fine-tuned control over every block.

\n\n\n\n

Theme.json Creator does not yet support configuration at this level. However, it would be interesting to see if Quach adds it in the future.

\n\n\n\n

The focus on setting up global styles is a good start for now. This is still an experimental feature. The great thing about it is that it can help theme authors begin to see how one piece of the block-based themes puzzle fits in. It is a starting point for an entirely new method of adding theme support for features when most are accustomed to adding multiple add_theme_support() PHP function calls.

\n\n\n\n

With the direction that theme development seems to be heading, it is easy to imagine that it could evolve into a completely UI-based affair at some point down the line. If templates are made up of blocks and patterns, which anyone can already build with the block editor, and if styles will essentially boil down to a config file, there will be little-to-no programming required to build a basic WordPress theme.

\n\n\n\n

If someone is not already at least jotting down notes for a plugin that allows users to create and package a block-based theme, I would be surprised. For now, Theme.json Creator is removing the need to write code for at least one part of the theme design process.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 07 Oct 2020 20:53:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:24;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"WPTavern: Jetpack 9.0 Introduces Loom Block, Twitter Threads Feature, and Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105743\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-introduces-loom-block-twitter-threads-feature-and-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4033:\"
\n\n\n\n

Jetpack’s highly anticipated 9.0 release has landed, introducing some of the new features the team has previewed over the past week. Users can now publish WordPress posts to Twitter as threads. This new feature is available as part of the Publicize module when you have connected a Twitter account.

\n\n\n\n

Posting Twitter threads is a feature that only works with the block editor, as it takes advantage of how content is naturally split into chunks (blocks).

\n\n\n\n

In the comments on his demo post, Automattic engineer Gary Pendergast gave a more detailed breakdown of the logic Jetpack uses to ensure full sentences aren’t broken up in the tweets.

\n\n\n\n

“With the mental model now being focused on mapping blocks to tweets, it’s much easier to make logical decisions about how to handle each block,” Pendergast said. “So, a paragraph block is the text of a tweet, if the paragraph is too long for a single tweet, it tries to split the paragraph up by sentences. If a sentence is too long, then it resorts to splitting by words. Then, if there’s an embed/image/video/gallery block following that paragraph, we can attach it to the tweet containing that paragraph. There are additional rules for other blocks, but that’s the basic process. It then just iterates over all of the supported blocks in the post.”

\n\n\n\n

Pendergast published his post as thread to demonstrate the new feature in action. The advantage of posting a thread from your WordPress site is that it doesn’t end up getting lost in Twitter’s fast-moving timeline. Most important Twitter threads evaporate from public consciousness almost as soon as they are published. Publishing threads from your website ensures they are better indexed and easier to reference in the future.

\n\n\n\n

Jetpack Adds Loom Block for Embedding Screen Recordings

\n\n\n\n

Loom was added to Jetpack as a new oEmbed provider three weeks ago. The video recording service allows for recording camera, microphone, and desktop simultaneously. The service is especially popular in educational settings. Jetpack 9.0 introduces a new Loom block for embedding recordings.

\n\n\n\n\n\n\n\n

“Loom is growing in popularity as it is being recommended more and more to assist in distance learning efforts,” Jetpack Director of Innovation Jesse Friedman said. “Now more than ever we want to be able to help those working, learning, and teaching from home. The Loom block was a natural addition to join the other Jetpack video blocks which now include YouTube, TikTok, DailyMotion, and Vimeo.”

\n\n\n\n

Loom’s free tier allows users to record up to 25 videos, but the Pro plan is free for educators. Friedman confirmed that Jetpack does not have any kind of partnership with Loom. The team decided to support the product to assist professionals, educators, and students. Having it available as a block also makes it more convenient for those using P2 for communication.

\n\n\n\n

As anticipated, Jetpack 9.0 also provides a seamless transition necessary to ensure Instagram and Facebook embeds will continue working after Facebook drops unauthenticated oEmbed support on October 24. The Jetpack team reports that it “partnered with Facebook” to make sure these embeds continue to work with the WordPress.com REST API.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 23:28:38 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:25;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:51:\"Post Status: Joost de Valk on WordPress marketshare\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"https://poststatus.com/?p=79914\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:62:\"https://poststatus.com/joost-de-valk-on-wordpress-marketshare/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1193:\"

David Bisset makes his podcast debut for Post Status, as he interviews Joost de Valk, Founder and Chief Product Officer of Yoast, and discusses all things WordPress marketshare related.

\n\n\n\n\n\n\n\n

Links

\n\n\n\n\n\n\n\n

Partner: Jilt

\n\n\n\n

Jilt offers powerful email marketing built for eCommerce. From newsletters to highly segmented automations, Jilt is your one-stop show for eCommerce email. Join thousands of stores that have already earned tens of millions of dollars in extra sales using Jilt. Try Jilt for free

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 22:28:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Brian Krogsgard\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:26;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"WPTavern: iThemes Buys WPComplete, Complementing Its Recent Restrict Content Pro Acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105631\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:227:\"https://wptavern.com/ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition?utm_source=rss&utm_medium=rss&utm_campaign=ithemes-buys-wpcomplete-complementing-its-recent-restrict-content-pro-acquisition\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4395:\"

Just one month after publicly announcing its acquisition of Restrict Content Pro (RCP), iThemes purchased WPComplete for an undisclosed amount. The acquisition is for the product, website, and customers only.

\n\n\n\n

Paul Jarvis and Zack Gilbert created the WPComplete plugin in 2016. However, it has outgrown what the duo could maintain and support alone. After the transition period in which the new owners take over, the two will step away from the project.

\n\n\n\n

In essence, WPComplete is a “course completion” plugin. Site owners can create online courses while allowing students/users to mark their work as completed. It also gives students a way to track their progress through courses, which can often boost the potential for them to finish.

\n\n\n\n

“Paul and Jack believe a key to their success has been their ability to keep their team small and manageable,” wrote Matt Danner, the COO at iThemes, in the announcement. “The growth of WPComplete has presented a number of challenges for a team of two people, so the decision was made to start looking towards alternative ownership solutions that could continue to grow WPComplete and provide it with a stable team. iThemes is a perfect fit.”

\n\n\n\n

iThemes customers who have a Plugin Suite or Toolkit membership will get automatic access to the pro version of the WPComplete plugin. For current WPComplete users, Danner said everything should be “business as usual.” However, iThemes has assigned a few of its team members to work on the product and site, so customers should see some new faces.

\n\n\n\n

RCP and WPComplete are obviously complementary products. RCP is a membership plugin that allows site owners to restrict content based on that membership. WPComplete allows site members to mark lessons or coursework as completed. “We’ll be rolling out a new bundle later this month that combines both RCP and WPComplete for course and membership creators to take advantage of these two plugins,” said AJ Morris, the Product Innovation and Marketing Manager at iThemes.

\n\n\n\n

WPComplete is still a young product. The free version of the plugin currently has 2,000+ active installs and a solid 4.7 rating on WordPress.org. If marketed as an extension of the RCP plugin, it automatically puts it in front of the eyes of 1,000s of more potential customers. It should be much easier to grow the plugin as part of a membership bundle.

\n\n\n\n

iThemes is making some bold moves in the membership space. It will be interesting to see if the company makes any other acquisitions that could strengthen its product line and help it become more dominant. There is still a ton of room for growth in the membership segment of the market. There is also the potential for integrations with other major plugins.

\n\n\n\n

“Adding WPComplete to the iThemes product lineup also allows us to move more quickly on some plans we have for Restrict Content Pro,” said Danner in the initial announcement. He also vaguely mentioned a couple of ideas the team had in the works but did not go into detail.

\n\n\n\n

With a little prodding, Morris provided some insight into what they are planning for the immediate future. The biggest first step is tackling integration with the block editor. Currently, WPComplete uses shortcodes. The team’s next step is likely to begin with creating block equivalents for those shortcodes.

\n\n\n\n

“After that, we’ve touched on a few deeper integrations with Restrict Content Pro, like the possibility to restrict courses to memberships,” said Morris.

\n\n\n\n

The iThemes team does not plan to stop with WPComplete as part of its product lineup. One of the goals is to use the plugin for the iThemes website itself.

\n\n\n\n

“We always try to eat our own dogfood when we can,” said Morris. “You’ll see that with RCP and WPComplete early next year as we look to integrate them into our iThemes Training membership.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 06 Oct 2020 20:59:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:27;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: Exploring Full-Site Editing With the Q WordPress Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105676\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:173:\"https://wptavern.com/exploring-full-site-editing-with-the-q-wordpress-theme?utm_source=rss&utm_medium=rss&utm_campaign=exploring-full-site-editing-with-the-q-wordpress-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7492:\"

I have been eagerly awaiting the moment when I could install a theme and truly test Gutenberg’s full-site editing feature. By and large, each time I have tested it over the past few months, the experience has felt utterly broken. This is why I have remained skeptical of seeing the feature land in WordPress 5.6 this December.

\n\n\n\n

The Q theme by Ari Stathopoulos is the first theme that seems to be a decent working example. Whether that is a stroke of luck with timing or that this particular theme is simply built correctly is hard to tell — Stathopoulos is a team rep for the Themes Team. Gutenberg 9.1 dropped last week with continued work toward site editing.

\n\n\n\n

Q is as experimental as it gets. The Themes Team put out an open call for experimental, block-based themes as far back as March this year. However, not many have taken the team up on this offer. If approved, Q stands to be the first block-based theme to go live in the official WordPress directory. It still has to work its way through the standard review process, awaiting its turn in the coming weeks.

\n\n\n\n

On the whole, full-site editing remains a frustrating and confusing experience. I still remain skeptical about its readiness, even in beta form, to show off to the world in WordPress 5.6.

\n\n\n\n

However, Q is an interesting theme to explore at this point for both end-users and theme developers. Users can install it and start tinkering with the site editing screen via the Gutenberg plugin. Developers can learn how global styles, templates, and template parts fit together from a working theme.

\n\n\n\n

Using the Site Editor

\n\n\n\nEditing a single post in the site editor.\n\n\n\n

The Q theme requires the Gutenberg plugin and its full-site editing mode to be enabled. Generally, requiring a plugin is not allowed for themes in the directory. However, experimental Gutenberg themes are allowed to bypass this guideline.

\n\n\n\n

Stathopoulos pointed out that the theme is highly experimental and should not be used on a production site. However, he is hopeful that it will get more eyes focused on full-site editing.

\n\n\n\n

He mentioned that several items are broken, such as category archives not showing the correct posts. This is a current limitation of the Query block in Gutenberg. However, one of the best ways to find and recognize these types of issues is to have a theme that stays up with the pace of development.

\n\n\n\n

Currently, the site editor feels like it is biting off more than it can chew. Not only can users edit the layout and design of the page, but they can also directly edit existing post content — don’t try this at home unless you are willing for your post titles to get switched to the hyphenated slug. Should the site editor be handling the double-duty of design and content editing? If so, should design and content editing be handled in separate locations in the long term or be merged into one feature?

\n\n\n\n

It feels raw. It is not geared toward users at this point.

\n\n\n\n

The bright spot with the site editor is the current progress on template parts in the editor. Template parts are essentially “modules” that handle one part of the page. For example, the typical theme will have a header and footer template part. Currently, end-users can insert custom template parts or switch one template part for another. This opens a world of possibilities, such as users choosing between multiple header designs (template parts) for their sites.

\n\n\n\nSwitching the header template part.\n\n\n\n

The downside to the entire template system is that it seems so divorced from the site editor that it is hard to believe the average user would understand what is going on. Templates and template parts reside under the Appearance menu in the admin. The Site Editor is a separate, top-level menu item. Without any preexisting knowledge of how these pieces work together, it can be confusing.

\n\n\n\n

Template parts worked for me in the site editor from the outset. However, they did not work on the front end at first. I continually received the “template part not found” message for hours. Then, at some point — whether through magic or a random save that pulled everything together — the feature began to output the previously-missing header and footer template parts.

\n\n\n\n

Glimpse Into the Future of Theme Development

\n\n\n\n

The Q theme has a scant few style rules, which it loads directly in the <head> section of the site in lieu of adding an extra stylesheet. It relies on the stock Gutenberg block styles on the front end with a few minor overrides. Most other custom styles are handled via the global styles system, which pulls from the theme’s experimental-theme.json config file (will be theme.json in the future).

\n\n\n\n

It begs the question of whether themes will necessarily need much in the way of CSS when full-site editing lands.

\n\n\n\n

If WordPress allows users to configure most styles via block options and global styles overrides, themes may not need much more than their config files. After that, it would come down to registering custom block styles and patterns.

\n\n\n\n

If this is the future that we are headed toward, anyone could essentially create a WordPress theme. And, those pieces, such as template parts and patterns, could all be shared between any site. In that future, themes may simply not matter anymore.

\n\n\n\n

Last year, Mike Schinkel proposed deprecating the theme system altogether and replacing it with web components.

\n\n\n\n

“Rather than look for a theme that has all the features one needs — which I have found always limits the choices to zero — a site owner could look for the components and modules they need and then assemble their site from those modules,” he said. “They could pick a header, a footer, a home-page hero, a set of article cards, a pricing module, and so on.”

\n\n\n\n

The more I tinker with full-site editing, the more it feels like that is the lane that it will ultimately merge into. Imagine a future where end-users could pick and choose the pieces they wanted and simply have it look right on the front end.

\n\n\n\n

It is exciting to think about that possibility. Both Schinkel and I have more of a background in programming than we do in design. It makes sense from that sort of analytical mindset to put everything into neat, reusable boxes because reuse is a cornerstone of smart programming.

\n\n\n\n

However, I worry about the state of design in such a system with so many replaceable parts. Will designers be able to take holistic approaches to theme development, creating truly intricate pieces of art? Will that system essentially create a web of cookie-cutter sites? Or, will designers simply find ways to think outside the box while within the constraints of the block system?

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 21:21:13 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:28;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Virtual Jamstack Conf to Feature Fireside Chat with Matt Mullenweg and Matt Biilmann, October 6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105680\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6?utm_source=rss&utm_medium=rss&utm_campaign=virtual-jamstack-conf-to-feature-fireside-chat-with-matt-mullenweg-and-matt-biilmann-october-6\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2618:\"
image credit: Jamstack Conf
\n\n\n\n

The greater Jamstack community is coming together on October 6-7, 2020, for a virtual conference. Organizers expect more than 15,000 attendees from around the globe over a two-day span that includes keynotes, sessions, interactive topic tables, workshops, speaker Q&As, and networking opportunities.

\n\n\n\n

Matt Mullenweg will be joining Netlify CEO Matt Biilmann on day 1 at 12PM PDT for a fireside chat moderated by CSS-Tricks Creator Chris Coyier. The chat will go deeper on recent topics of contention, including developer sentiment, complexity, security, and performance. Coyier also plans to discuss how the Jamstack and WordPress communities intersect through headless implementations of the CMS.

\n\n\n\n

A provocative post from TheNewStack at the end of August quoted Mullenweg as saying that “JAMstack is a regression for the vast majority of the people adopting it.” This sparked multiple heated exchanges across blogs and social media. Biilimann, who originally coined the term “Jamstack,” wrote a response to Mullenweg’s remarks, hailing “the end of the WordPress era.”

\n\n\n\n

Live conversations tend to be more cordial than shots fired across the blogosphere. It will be interesting to see if Biilimann cares to join Stackbit CEO Ohad Eder-Pressman in his wager that Jamstack will become the predominant architecture for the web by 2025. The fireside chat should be recorded, in case you cannot catch the live session. Recordings of talks from the previous virtual Jamstack event held in May are available on YouTube.

\n\n\n\n

Today is the last call for registration. Many of the workshops have already sold out, but tickets to the regular sessions on October 6 are still available. Sign up on the event website to get your free ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 05 Oct 2020 20:12:50 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:29;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Gutenberg 9.1 Adds Patterns Category Dropdown and Reverts Block-Based Widgets in the Customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105629\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-9-1-adds-patterns-category-dropdown-and-reverts-block-based-widgets-in-the-customizer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5615:\"

Gutenberg 9.1 was released to the public on Wednesday. The team announced over 200 commits from 77 contributors in its release post yesterday. One of the biggest changes to the interface was the addition of a new dropdown selector for block pattern categories. The team also reverted the block-based widgets section in the customizer and added an image size control to the Media & Text block.

\n\n\n\n

One of the main focuses of this release was improving the block-based widgets editor. The feature was taken out of the experimental stage in Gutenberg 8.9 and continues to improve. The widgets screen now uses the same inserter UI as the post-editing screen. However, users can currently only insert regular blocks. Patterns and reusable blocks are still not included.

\n\n\n\n

Theme authors can now control aspects of the block editor via a custom theme.json file. This is part of the ongoing Global Styles project, which will allow theme authors to configure features for their users.

\n\n\n\n

The development team has also added an explicit box-sizing style rule to the Cover and Group blocks. This is to avoid any potential issues with the new padding/spacing options. Theme authors who rely on the block editor styles should test their themes to make sure this change does not break anything.

\n\n\n\n

Better Pattern Organization

\n\n\n\nNew block patterns UI in the inserter.\n\n\n\n

I have been calling for the return of the tabbed pattern categories since Gutenberg 8.0, which was a regression from previous versions. For 11 versions, users have had to scroll and scroll and scroll through every block pattern just to find the one they wanted. The development team has sought to address this issue by using a category dropdown selector. When selecting a specific category, its patterns will appear.

\n\n\n\n

At first, I was unsure about this method over the old tabbed method. However, after some use, it feels like the right direction.

\n\n\n\n

As more and more theme and plugin authors add block pattern categories to users’ sites, the dropdown is a more sensible route. Even tabs could become unwieldy over time. The dropdown better organizes the list of categories and makes the UI cleaner. More than anything, I am enjoying the experience and look forward to this eventually landing in WordPress 5.6 later this year.

\n\n\n\n

Customizer Widgets Reverted

\n\n\n\nReverted widgets panel in the customizer.\n\n\n\n

On the subject of WordPress 5.6, one of its flagship features has been hitting some roadblocks. Block-based widgets are expected to land in core with the December release, but the team just reverted part of the feature. They had to remove the widgets block editor from the customizer they added just two major releases ago.

\n\n\n\n

It was for the best. The customizer’s block-based widgets editor was fundamentally broken. It was not ready for primetime and should have remained in the experimental stage until it was somewhat usable.

\n\n\n\n

“I will approve this since the current state of the customizer in the Gutenberg plugin is broken, and there is no clear path forward about how to fix that,” wrote Andrei Draganescu in the reversion ticket. “With this patch, the normal widgets can still be edited in the customizer and the block ones don’t break it anymore. This is NOT to mean that we won’t proceed with fixing the block editor in the customizer, that is still an ongoing discussion.”

\n\n\n\n

The current state of editing widgets via the customizer is at least workable with this change. If end-users add a block via the admin-side widgets editor, it will merely appear as an uneditable, faux widget named “Block” in the customizer. They will need to edit blocks via the normal widgets screen.

\n\n\n\n

There is no way that WordPress can ship the current solution when 5.6 rolls out. However, we are still two months out. This leaves plenty of time for a fix, but Draganescu’s note that “there is no clear path forward” may make some people a bit uneasy at this stage of development.

\n\n\n\n

Control Image Size for Media & Text

\n\n\n\nImage size dropdown selector for the Media & Text block.\n\n\n\n

One of the bright spots in this update is the addition of an image size control to the Media & Text block. Like the normal Image block, end-users can choose from any registered image size created for their uploaded image.

\n\n\n\n

This is a feature I have been looking forward to in particular. Previously, using the full-sized image often made the page weight a bit heftier than necessary. It is also nice to go along with themes that register sizes for both landscape and portrait orientations, giving users more options.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 20:56:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:30;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"WordPress.org blog: The Month in WordPress: September 2020\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://wordpress.org/news/?p=9026\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://wordpress.org/news/2020/10/the-month-in-wordpress-september-2020/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8711:\"

This month was characterized by some exciting announcements from the WordPress core team! Read on to catch up with all the WordPress news and updates from September. 

\n\n\n\n
\n\n\n\n

WordPress 5.5.1 Launch

\n\n\n\n

On September 1, the  Core team released WordPress 5.5.1. This maintenance release included several bug fixes for both core and the editor, and many other enhancements. You can update to the latest version directly from your WordPress dashboard or download it directly from WordPress.org. The next major release will be version 5.6.

\n\n\n\n

Want to be involved in the next release?  You can help to build WordPress Core by following the Core team blog, and joining the #core channel in the Making WordPress Slack group.

\n\n\n\n

Gutenberg 9.1, 9.0, and 8.9 are out

\n\n\n\n

The core team launched version 9.0 of the Gutenberg plugin on September 16, and version 9.1 on September 30. Version 9.0 features some useful enhancements — like a new look for the navigation screen (with drag and drop support in the list view) and modifications to the query block (including search, filtering by author, and support for tags). Version 9.1 adds improvements to global styles, along with improvements for the UI and several blocks. Version 8.9 of Gutenberg, which came out earlier in September, enables the block-based widgets feature (also known as block areas, and was previously available in the experiments section) by default — replacing the default WordPress widgets to the plugin. You can find out more about the Gutenberg roadmap in the What’s next in Gutenberg blog post.

\n\n\n\n

Want to get involved in building Gutenberg? Follow the Core team blog, contribute to Gutenberg on GitHub, and join the #core-editor channel in the Making WordPress Slack group.

\n\n\n\n

Twenty Twenty One is the WordPress 5.6 default theme

\n\n\n\n

Twenty Twenty One, the brand new default theme for WordPress 5.6, has been announced! Twenty Twenty One is designed to be a blank canvas for the block editor, and will adopt a straightforward, yet refined, design. The theme has a limited color palette: a pastel green background color, two shades of dark grey for text, and a native set of system fonts. Twenty Twenty One will use a modified version of the Seedlet theme as its base. It will have a comprehensive system of nested CSS variables to make child theming easier, a native support for global styles, and full site editing. 

\n\n\n\n

Follow the Make/Core blog if you wish to contribute to Twenty Twenty One. There will be weekly meetings every Monday at 15:00 UTC and triage sessions every Friday at 15:00 UTC in the #core-themes Slack channel. Theme development will happen on GitHub

\n\n\n\n
\n\n\n\n

Further Reading:

\n\n\n\n\n\n\n\n

Have a story that we should include in the next “Month in WordPress” post? Please submit it here.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 09:34:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Hari Shanker R\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:31;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: Cloudflare Launches New Web Analytics Product Focusing on Privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105446\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/cloudflare-launches-new-web-analytics-product-focusing-on-privacy?utm_source=rss&utm_medium=rss&utm_campaign=cloudflare-launches-new-web-analytics-product-focusing-on-privacy\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2448:\"

In pursuit of “democratizing web analytics,” Cloudflare announced it is launching privacy-first analytics as a new standalone product. The company is entering a market that has been dominated by Google Analytics for years but with a major differentiating feature – it will not track individual users by a cookie or IP address to show unique visits.

\n\n\n\n

Cloudflare Web Analytics defines a visit as “a successful page view that has an HTTP referer that doesn’t match the hostname of the request.” It’s not the same as Google’s “unique” metric, and Cloudflare says it may differ from other reporting tools. Weeding out bots from the total traffic numbers is a nascent feature that Cloudflare is improving as part of its Bot Management product.

\n\n\n\n
\n\n\n\n

Cloudflare Web Analytics is launching with features that are largely similar to Google Analytics but with some unique ways of zooming into different traffic segments and time ranges to see where traffic is originating from.

\n\n\n\n

“The most popular analytics services available were built to help ad-supported sites sell more ads,” Cloudflare product manager Jon Levine said. “But, a lot of websites don’t have ads. So if you use those services, you’re giving up the privacy of your users in order to understand how what you’ve put online is performing.

\n\n\n\n

“Cloudflare’s business has never been built around tracking users or selling advertising. We don’t want to know what you do on the Internet — it’s not our business.”

\n\n\n\n

Paying customers on the Pro, Biz, and Enterprise plans can access their analytics from their dashboards immediately. Cloudflare is also offering the product for free as JavaScript-based analytics for users who are not currently customers. Those who want access to the free plan can sign up for the waitlist.

\n\n\n\n

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 02 Oct 2020 04:03:01 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:32;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:67:\"WPTavern: Virtual WordPress Page Builder Summit Kicks Off October 5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105570\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:179:\"https://wptavern.com/virtual-wordpress-page-builder-summit-kicks-off-october-5?utm_source=rss&utm_medium=rss&utm_campaign=virtual-wordpress-page-builder-summit-kicks-off-october-5\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6348:\"

From October 5 through October 9, the first Page Builder Summit will open its virtual doors to all attendees for free. Nathan Wrigley, the podcaster behind WP Builds, and Anchen le Roux, the founder and lead developer of Simply Digital Design, are hosting the five-day online event that focuses on the vast ecosystem of page builders for WordPress.

\n\n\n\n

The summit will include 35 sessions spread out over the event schedule. Each session will last around 30 minutes, so it will be easy to pop in and watch one in your downtime. Sessions will cover a range of builders, including the default WordPress block editor, Elementor, Beaver Builder, Oxygen, Brizy, and Divi.

\n\n\n\n

“It’s an event specifically for users of WordPress page builders, or those curious about what they can do,” said Wrigley. “I feel like a page builder style interface for creating websites is the future for our industry. WordPress itself is moving in this direction with the block editor (a.k.a. Gutenberg). With that in mind, it seemed like a good idea to create a dedicated event to share knowledge about this side of WordPress. We’ve tried to include presentations from as many page builders as we could.”

\n\n\n\n

Wrigley made sure to point out that it is not all geared toward developers, discussing the inner-workings of builders. Some of the sessions focus on marketing, optimization, and conversion, which provides a wider range of topics for potential attendees.

\n\n\n\n

The summit hosts created an online quiz for those who are unsure about which sessions to watch.

\n\n\n\n

There is a small catch. The sessions will be freely available only from the time they begin and the following 24 hours. After that, accessing the videos will come at a premium. Attendees can gain lifetime access to the PowerPack for $47 if they purchase within 15 minutes of signing up. Then, prices will rise to $97 until the event kicks off on October 5. Beyond, the price jumps to $147. The lifetime access includes access to the presentations, transcripts, a workbook, and other bonuses from the speakers.

\n\n\n\n

For those unsure about forking over the cash, they can still watch the sessions during the 24-hour window.

\n\n\n\n

The proceeds from the event will go out to paying affiliate commissions to speakers and partners. Some of it will go into planning and investing in a second summit down the road.

\n\n\n\n

“Both myself and Nathan have specific charities that we want to donate to after the event,” said le Roux. “It was part of our goals to be able to do this, but we didn’t want to make this an official contribution.”

\n\n\n\n

Why a Page Builder Summit?

\n\n\n\n

Both Wrigley and le Roux have their preferred builders. But, the goal of the summit is to offer a wide look at the tools available and help freelancers and agencies better streamline their businesses and create happier clients.

\n\n\n\n

“I’ve been a user of page builders for many years, but only at the point where they truly showed in the editing interface something that almost perfectly reflected what the end-user would see did I get really immersed,” said Wrigley. “Having come from a background in which I built entire websites from a collection of text files (HTML, CSS, PHP, etc.), I was fascinated that we’d reached a point where the learning curve for building a good website was significantly reduced.”

\n\n\n\n

He pointed out that it is not always so simple though. While the same level of coding skills may not be necessary, people must figure out how to navigate their preferred page builder, which can come with its own learning curve.

\n\n\n\n

“You need to learn their way of doing things and how to achieve your design choices,” he said. “It’s always going to work out better if you know the code, but the WordPress mission of democratizing publishing certainly seems to align quite nicely with the adoption of tools, like page builders, which mean that once-difficult tasks are now easier.”

\n\n\n\n

For le Roux, her interest in hosting the Page Builder Summit falls back to her design studio.

\n\n\n\n

“As a developer, my main reason for switching to page builders was around streamlining and creating more efficient but quality websites in the shortest amount of time,” she said. “Especially now that we focus on day rates, creating the best possible website that clients would love fast would not have been possible without page builders.”

\n\n\n\n

The Hosts’ Go-To Builders

\n\n\n\n

“We prefer using Beaver Builder with Themer at Simply Digital Design,” said le Roux. “We use Gutenberg for blog posts or where possible with custom post types or LMS software. However, we’ve also taken on a few Elementor projects where that’s the client’s preferred option.”

\n\n\n\n

Wrigley uses some of the same tools. His main work is on the WP Builds website where he hosts podcasts.

\n\n\n\n

“I have used Beaver Builder’s Themer to create templates for specific layouts, but for content creation within those layouts I’m using the block editor,” said Wrigley. “My content is mainly text and the WordPress editor is utterly remarkable in this situation. I kept the classic editor installed for a few months after WordPress 5.0 came about, but I soon realized that this was folly and that the editing interface of Gutenberg is superior. The ability to insert and move text, buttons, etc. is such a joy to work with, and the iterations that have been made in the last two years make it, in my opinion, the best text editing experience on the web.”

\n\n\n\n

Wrigley sees a future in which the WordPress block editor takes over much of the work that page builders are currently handling. However, that future is “still over the horizon.”

\n\n\n\n

“I’m excited about this future though, and we’ve got a few crystal ball-gazing presentations; trying to work out what that future might look like,” he said.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 20:31:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:33;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"WPTavern: Jetpack 9.0 to Introduce New Feature for Publishing WordPress Posts to Twitter as Threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105448\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads?utm_source=rss&utm_medium=rss&utm_campaign=jetpack-9-0-to-introduce-new-feature-for-publishing-wordpress-posts-to-twitter-as-threads\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3318:\"

Jetpack 9.0, coming on October 6, will debut a new feature that allows users to share blog posts as Twitter threads in multiples tweets. A recent version of Jetpack introduced the ability to import and unroll tweetstorms for publishing inside a post. The 9.0 release will run it back the other way so the content originates in WordPress, yet still reaps all the same benefits of circulation on Twitter as a thread.

\n\n\n\n

The new Twitter threads feature is being added as part of Jetpack’s Publicize module under the Twitter settings. After linking up a Twitter account, the Jetpack sidebar options for Publicize allow users to publish to Twitter as a link to the blog or a set of threaded tweets. It’s not just limited to text content – the threads feature will also upload and attach any images and videos included in the post.

\n\n\n\n\n\n\n\n

When first introduced to the idea of publishing a Twitter thread from WordPress, I wondered if threads might lose their trademark pithy punch, since users aren’t forced to keep each segment to the standard length of a tweet. Would each tweet be separated in an odd, unreadable way? The Jetpack team anticipated this, so the thread option adds more information to the block editor to show where the paragraphs will be split into multiple tweets.

\n\n\n\n

“Threads are wildly underused on Twitter,” Gary Pendergast said in a post introducing the feature. “I think a big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.” The tool Pendergast has been working on for Jetpack gives users the best of both worlds.

\n\n\n\n

In response to a comment requesting Automattic “concentrate on tools to get people off social media,” Pendergast said, “If we’re also able to improve the quality of conversations on social media, I think it’d be remiss of us to not do so.” He also credits IndieWeb discussions on Tweetstorms and POSSE (Publish (on your) Own Site, Syndicate Elsewhere) as inspirations for the feature.

\n\n\n\n

For years, blogging advocates have tried to convince those who post lengthy tweetstorms to switch to a publishing medium that is more suitable to the length of their thoughts. The problem is that Twitter users lose so much of the immediate feedback and momentum that their thoughts would have generated when composed as a tweetstorm.

\n\n\n\n

Instead of lecturing people about how they should really be blogging instead of tweetstorming, Jetpack is taking a fresh approach by enabling full content ownership with effortless social syndication. You can test out the experience for yourself by adding the Jetpack Beta Testers plugin and running the 9.0 RC version on your site.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Oct 2020 02:56:46 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:34;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: Ask the Bartender: How To WordPress in a Block World?\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105491\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:167:\"https://wptavern.com/ask-the-bartender-how-to-wordpress-in-a-block-world?utm_source=rss&utm_medium=rss&utm_campaign=ask-the-bartender-how-to-wordpress-in-a-block-world\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9755:\"

I love your articles. And now, in the middle of the WordPress revolution, I realized I’m constantly searching for an answer regarding WP these days.

So many things are being said, so many previsions of the future, problems, etc., but, right now, I think I, as a designer, just want to understand one thing that seemed answered already but it’s never clear:

Is WordPress a good choice to build a client’s template where he just has to insert the info that will show in the frontend where I want to? And he doesn’t have to worry about formatting blocks? I love blocks, don’t get me wrong, but will normal templating end?

I just think that having a super CMS, HTML, CSS, and being able to play with a database with ACF is so powerful, that I’m wondering if it’s lost. After so much reading, I still don’t understand if this paradigm is going to disappear.

Right now, I don’t know if it’s best to stop making websites as I used to and adopt block patterns instead.

Ricardo
\n\n\n\n

WordPress is definitely changing. Over the past two years, we have seen much of it reshaped into something different from the previous decade and more. However, this is not new. WordPress has always been a constantly-changing platform. It just feels far too different this time around, almost foreign to many. The platform had to make a leap. Otherwise, it would have started falling behind.

\n\n\n\n

And, it is a big ask of the existing community to come along with it, to take that leap together.

\n\n\n\n

It can be scary as a developer whose livelihood has depended on things working a certain way or who has built tools and systems around pre-block WordPress. Many freelancers and agencies had their world turned upside down with the launch of the block editor. It is perfectly OK to feel a bit lost.

\n\n\n\n

Now, it is time for a little tough love. It has been two years. As a professional, you need to have a plan in place already. Whether that is an educational plan for yourself or a transitional plan for your clients, you should already be tackling projects that leverage the block editor. If you are at a point where you have not been building with blocks, you are now behind. However, you can still catch up and continue advancing in your WordPress career.

\n\n\n\n

There are so many changes coming down the pipeline that anyone who plans to develop for WordPress will be in continual education mode for years to come.

\n\n\n\n

When building for clients, the biggest thing to remember is that it is not about you. It is about getting something into the hands of your clients that addresses their specific needs. Freelancers and agencies need to often be the Jacks and Jills of all trades. Sometimes, this even means having a backup CMS or two that you can use that are not named WordPress. It helps to be well-rounded enough to jump around when needed, especially if you are not at a point in your career where you can demand specific work and pass on jobs that would put food on the table.

\n\n\n\n

It is also easy to look at every job as a nail and WordPress as the hammer. Or, even specific plugins as the tool that will always get the job done. I have seen developers in the past rely on tools like ACF, CMB2, or Meta Box but could not code a custom metadata solution when necessary to save their life. Sometimes a bigger toolbox is necessary.

\n\n\n\n

Every WordPress developer needs a solid, foundational understanding of the languages that WordPress uses. Gone are the days of skating by on HTML, CSS, and PHP knowledge. You need to learn JavaScript deeply. Matt Mullenweg, the co-founder of WordPress, was not joking around when he said this back in 2015. It holds true more and more each day. In another five years, it will tough to be a developer in the WordPress world without knowing JavaScript, at least for backend work.

\n\n\n\n

It also depends on what types of sites you are building. If you are primarily handling front-end design, you will likely be able to get by with a lower skill level. You will just need to know the “WordPress way” of building themes.

\n\n\n\n

Within the next year, you should be able to build just about any theme design with decent CSS and HTML knowledge along with an understanding of how the block system works. Full-site editing and block-based themes will change how we build the front end of the web. It is going to be a challenging transition at first, especially for those of us who are steeped in traditional theme development, but client sites will often be far easier to build. I highly recommend the twice-monthly block-based themes meetings if your focus is on the front end.

\n\n\n\n

Block Templates

\n\n\n\n

Based on your question, I am going to make some assumptions. You have a history of essentially building out meta boxes via ACF where the client just pops in their data. Then, you format that data on the front end. You are likely mixing this with custom post types (CPTs). This is a fairly common scenario.

\n\n\n\n

One of the great things about the block system is that you can lock the post editor for individual CPTs. WordPress already has you covered with its block templates feature, which allows you to define just what a post should look like. You can set up which blocks you want to appear and have the client drop their content in. At the moment, this feature is limited to the post type level. However, it should grow more robust over time, particularly when it works alongside the traditional “page templates” system.

\n\n\n\n

Block templates are a powerful tool in the ol’ toolbox that will come in handy when building client sites.

\n\n\n\n

Block Patterns

\n\n\n\n

You do not have to stop making websites as you are accustomed to at the moment. However, you should start leveraging new block features as they become available and make sense for a specific project. I am a fanatic when it comes to block patterns, so my bias will definitely show.

\n\n\n\n

The biggest thing with block patterns and clients is education. For the uninitiated, you will need to spend some time teaching them how to insert a pattern and how it can be used to their advantage. That is the hurdle you must jump.

\n\n\n\n

For many of the users that I have seen introduced to well-designed patterns, they have fallen in love with the feature. Even many who were reluctant to switch to the block editor became far more comfortable working with it after learning how patterns worked. This is not the case for every user or client, but it has been a good introduction point to the block editor for many.

\n\n\n\n

To answer your question regarding patterns: yes, you should absolutely begin to adopt them.

\n\n\n\n

ACF Is Evolving

\n\n\n\n

Because you are accustomed to ACF, you should be aware that the framework is evolving to keep up with the block editor. Version 5.8.0 introduced a PHP framework for creating custom blocks over a year ago. And, it has been improving ever since. There are even projects like ACF Blocks, which will provide even more tools for your arsenal.

\n\n\n\n

It is important to learn from what some of the larger agencies are doing. Read up on how WebDevStudios is tackling block development. The company also has an open-source block library for ACF.

\n\n\n\n

Solving Problems

\n\n\n\n

Your job as a developer is to be a problem solver. Whatever system you are building with is merely a part of your toolset. You need to be able to solve issues regardless of what tool you are using. At the end of the day, it is just code. If you can learn HTML, you can learn CSS. If you can learn those, you can learn PHP. And, if you can manage PHP, you can certainly pick up JavaScript.

\n\n\n\n

A decade or two from now, you will need to learn something else to stay relevant in your career. Web technology changes. You must change with it. Always consider yourself a student and continue your education. Surround yourself and learn from those who are more advanced than you. Emulate, borrow, and steal good ideas. Use what you have learned to make them great.

\n\n\n\n

There is no answer I can give that will be perfect for every scenario. Each client is unique, and you will need to decide the best direction for each.

\n\n\n\n

However, yes, you should already be on the path to building with a block-first mindset if you plan to continue working with WordPress for the long haul. Immerse yourself in the system. Read, study, and build something any chance you get.

\n\n\n\n

This is the first post in the Ask the Bartender series. Have a question of your own? Shoot it over.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 30 Sep 2020 20:35:25 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:35;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"WPTavern: Supercharge the Default WordPress Theme With Twentig, a Toolbox for Twenty Twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105344\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:225:\"https://wptavern.com/supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty?utm_source=rss&utm_medium=rss&utm_campaign=supercharge-the-default-wordpress-theme-with-twentig-a-toolbox-for-twenty-twenty\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6455:\"Custom page pattern from the Twentig plugin.\n\n\n\n

I am often on the hunt for those hidden gems when it comes to block-related plugins. I like to see the interesting places that plugin authors venture. That is why it came as a surprise when someone recommended I check out the Twentig plugin a few days ago. Somehow, it has flown under my radar for months. And, it has managed to do this while being one of the more interesting plugins for WordPress I have seen in the past year.

\n\n\n\n

Twentig is a plugin that essentially gives superpowers to the default Twenty Twenty theme. Diane and Yann Collet are the sibling co-founders and brains behind the plugin.

\n\n\n\n

While I have been generally a fan of Twenty Twenty since it was first bundled in core, it was almost a bit of a letdown in some ways. It was supposed to be the theme that truly showcased what the block editor could do — and it does a fine job of styling the default blocks — but there was a lot of potential left on the table. The Twentig plugin turns Twenty Twenty into something worthier of a showcase for the block editor. It is that missing piece, that extra mile in which WordPress should be marching its default themes.

\n\n\n\n

While the new Twenty Twenty-One default theme is just around the corner, Twentig is breathing new life into the past year’s theme. The developers behind the plugin are still fixing bugs and bringing new features users.

\n\n\n\n

Of its 34 reviews on WordPress.org, Twentig has earned a solid five-star rating. That is a nice score for a plugin with only 4,000 active installations. As I said, it has flown under the radar a bit, but the users who have found it have obviously discovered something that adds those extra touches to their sites they need.

\n\n\n\n

What Does Twentig Do?

\n\n\n\n

It is a toolbox for Twenty Twenty. The headline feature is its block editor features, such as custom patterns and page layouts. It also offers a slew of customizer options that allow end-users to put their own design spin on the default theme. However, my interest is primarily in how it extends the block editor.

\n\n\n\n

Let’s get this out of the way up front. Twentig’s one downside is that it adds a significant amount of additional CSS on top of the already-heavy Twenty Twenty and block editor styles. I will blame the current lack of a full design system from WordPress on most of this. Styling for the block editor can easily bloat a stylesheet. Adding an extra 100+ kb per page load might be a blocker for some who would like to try the plugin. Users will need to weigh the trade-offs between the additional features and the added page size.

\n\n\n\n

The thing that makes Twentig special is its extensive patterns and pages library, which offers one-click access to hundreds of layouts specifically catered to the Twenty Twenty theme.

\n\n\n\nInserting one of the hero patterns.\n\n\n\n

It took me a few minutes to figure out how to access the patterns — mainly because I did not read the manual. I expected to find them mixed in with the core patterns inserter. However, the plugin adds a new sidebar panel to the editor, which users can access by clicking the “tw” icon. After seeing the list of options, I can understand why they probably would not fit into WordPress’s limited block and patterns inserter UI.

\n\n\n\n

It would be easier to list what the plugin does not have than to go through each of the custom patterns and pages.

\n\n\n\n

The one thing that truly sets this plugin apart from the dozens of other block-library types of plugins is that there are no hiccups with the design. Almost every similar plugin or tool I have tested has had CSS conflicts with themes because they are trying to be a tool for every user. Twentig specifically targets the Twenty Twenty theme, which means it does not have to worry about whether it looks good with the other thousands of themes out there. It has one job, which is to extend its preferred theme, and it does it with well-designed block output.

\n\n\n\n

The other aspect of this is that it does not introduce new blocks. Every pattern and page layout option uses the core WordPress blocks, which includes everything from hero sections to testimonials to pricing tables to event listings. And more.

\n\n\n\n

Twentig does not stop adding features to the block editor with custom patterns. The useful and sometimes fun bits are on the individual block level, and I have yet to explore everything. I continue to discover new settings each time I open my editor.

\n\n\n\n

Whether it is custom pullquote styles, a photo image frame, or an inner border tweak to the Cover block (shown below), the plugin adds little extras that push what users can do with their content.

\n\n\n\nInner border style for the Cover block.\n\n\n\n

Each block also gets some basic top and bottom margin options, which comes in handy when laying out a page. At this point, I am simply looking forward to discovering features I have yet to find.

\n\n\n\n

Areas Themes Should Explore

\n\n\n\n

One of the things I dislike about many of these features being within the Twentig plugin is that I would like to see them within the Twenty Twenty theme instead. Obviously not every feature belongs in the theme — some features firmly land in plugin territory. The default WordPress themes should also leave some room for plugin authors to explore. But, shipping some of the more prominent patterns and styles with Twenty Twenty would make a more robust experience for the average end-user looking to get the most out of blocks.

\n\n\n\n

Block patterns were not a core WordPress feature when Twenty Twenty landed. However, for the upcoming Twenty Twenty-One theme, which is expected to bundle some unique patterns, the design team should explore what the Twentig plugin has brought to the current default. That is the direction that theme development should be heading, and theme developers can learn a lot by stealing borrowing from this plugin.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 22:00:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:36;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:105:\"WPTavern: Coming in Jetpack 9.0: Shortcode Embeds Module Updated to Handle Facebook and Instagram oEmbeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105381\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:253:\"https://wptavern.com/coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds?utm_source=rss&utm_medium=rss&utm_campaign=coming-in-jetpack-9-0-shortcode-embeds-module-updated-to-handle-facebook-and-instagram-oembeds\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2938:\"

Facebook and Instagram are dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers in an upcoming release. After evaluating third-party solutions, WordPress VIP is recommending its partners enable Jetpack’s Shortcode Embeds module. Jetpack will be shipping the update in its 9.0 release, which is anticipated to land prior to the October 24th deadline.

\n\n\n\n

The module is being updated to provide a seamless transition for users who might otherwise be negatively impacted by Facebook’s upcoming API change. WordPress contributors have run some simulations but are not yet sure what will happen to the display for previously embedded content.

\n\n\n\n

“It is possible that they change the contents of the JS file to manipulate cached embeds, perhaps to display a warning that the site is using an old method to embed content or that the request is not properly authenticated,” Jonathan Desrosiers commented on the trac ticket for removing the oEmbed providers.

\n\n\n\n

WordPress.com VIP roughly outlined what users can expect if they do not enable a solution to begin authenticating oEmbeds:

\n\n\n\n

By default, WordPress caches oEmbed contents in post metadata. These embeds will continue to display in previously-published content. If you edit older posts in the Block Editor, regardless of whether you update the post by saving changes, the embeds in the post will no longer be cached and will stop displaying. If you view these older posts using the Classic Editor, so long as the post is not re-saved, the embeds will continue to function and display properly. If you update the post content, the embed will cease functioning unless you have a mitigation installed.

\n\n\n\n

Although WordPress VIP recommends using the Jetpack module as the best solution, self-hosted WordPress users may want to investigate other options if they are not already using Jetpack. oEmbed Plus is a free plugin created specifically for solving the problem of WordPress dropping Facebook and Instagram as oEmbed providers but it is more work to set up and configure. It requires users to register as a Facebook developer and create an app to get API credentials.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 21:18:52 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:37;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:52:\"WPTavern: W3C Selects Craft CMS for Redesign Project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105265\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:149:\"https://wptavern.com/w3c-selects-craft-cms-for-redesign-project?utm_source=rss&utm_medium=rss&utm_campaign=w3c-selects-craft-cms-for-redesign-project\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9407:\"

W3C has selected Craft CMS over Statamic for its redesign project, after dropping WordPress from consideration in an earlier round of elimination:

\n\n\n\n

In the end, our decision mostly came down to available resources. Craft had already committed to reach AA compliance in Craft 4 (it is currently on version 3.5, the release of version 4 is planned for April 2021). They had also arranged for an external agency to provide them with accessibility issues to tackle weekly. In the end, they decided instead to hire an in-house accessibility specialist to perform assessments and assist the development team in adopting accessibility patterns in the long run.

W3C CMS Selection Report
\n\n\n\n

Last week we published a post urging W3C to revisit Gutenberg for a fair shake against the proprietary CMS’s or consider adopting another open source option. During the selection process, Studio 24, the agency contracted for the redesign, cited its extensive experience with WordPress as the reason for not performing any accessibility testing on more recent versions of Gutenberg.

\n\n\n\n

When asked if the team contacted anyone from WordPress’ Accessibility Team during the process or put Gutenberg through the same tests as the proprietary CMS’s, Studio 24 founder Simon Jones confirmed they had not.

\n\n\n\n

“No, we only reached out to the two shortlisted CMS’s” Jones said. “I’m afraid we didn’t have time to do more. We did test GB a few months ago based on editing content – though it wasn’t the only factor in our choice. As an agency we do plan to keep reviewing GB in the future.”

\n\n\n\n

In response to our concerns regarding licensing, Jones penned an update titled “On not choosing WordPress,” which further elaborated on the reasons why the agency was not inclined towards using or evaluating the new editor:

\n\n\n\n

From a business perspective I also believe Gutenberg creates a complexity issue that makes it challenging for use by many agencies who create custom websites for clients; where we have a need to create lots of bespoke blocks and page elements for individual client projects.

The use of React complicates front-end build. We have very talented front-end developers, however, they are not React experts – nor should they need to be. I believe front-end should be built as standards-compliant HTML/CSS with JavaScript used to enrich functionality where necessary and appropriate.

As of yet, we have not found a satisfactory (and profitable) way to build custom Gutenberg blocks for commercial projects. 

\n\n\n\n

The CMS selection report also stated that W3C needs the CMS to be “usable by non-sighted users” by the launch date, since some members of the staff who contribute to the website are non-sighted.

\n\n\n\n

Since the most recent version of WordPress was not tested in comparison with the proprietary CMS’s, it’s unclear how much better they handle accessibility. Ultimately, W3C and Studio 24 were more comfortable moving forward with a proprietary vendor that was able to make certain assurances about the future accessibility of its authoring tool, despite having a smaller pool of contributors.

\n\n\n\n

“[I’m] also deeply curious since the cursory notes on accessibility for both of the reviewed CMSes seem to highlight a ton of issues like ‘Buttons and Checkboxes are built using div elements’ or most inputs lacking clear focus styles,” Gutenberg technical lead Matías Ventura said. “An element like the Calendar for choosing a post date seems entirely inoperable with keyboard on Craft, for example, while WordPress’ has had significant effort and rounds of feedback poured into that element alone to make it fully operable.”

\n\n\n\n

WordPress developer Anthony Burchell commented on how using a relatively new proprietary CMS seemed counter to W3C’s stated goal to select an option on the basis of longevity. Craft CMS’s continued success is contingent upon its business model and the company’s ability to remain profitable.

\n\n\n\n

“FOSS have the same opportunity of direct access to developers,” Burchell said. “I recognize there are many accessibility shortcomings in popular software, but I think it’s more constructive to rally behind and contribute, not use a proprietary CMS that boasts beer budget in their guidelines.”

\n\n\n\n

On the other side of the issue, accessibility advocates took the W3C’s decision as a referendum on Gutenberg’s continued struggles to meet WCAG AA standards. WordPress accessibility specialist Amanda Rush said it was “nice to see the W3C flip tables over this.”

\n\n\n\n

“Gutenberg is not mature software,” accessibility consultant and WordPress contributor Joe Dolson said in a post elaborating on his comments at WPCampus 2020 Online. He emphasized the lack of stability in the project that Studio 24 alluded to when documenting the reasons against using WordPress.

\n\n\n\n

“It is still undergoing rapid changes, and has grand goals to add a full-site editing experience for WordPress that almost guarantees that it will continue to undergo rapid changes for the next few years,” Dolson said. “Why would any organization that is investing a large amount into a site that they presumably hope will last another 10 years want to invest in something this uncertain?”

\n\n\n\n

Dolson also said the accessibility improvements he referenced regarding the audit were only a small part of the whole picture.

\n\n\n\n

“They only encompass issues that existed in the spring of 2019,” he said. “Since then, many features have been added and changed, and those features both resolve issues and have created new ones. The accessibility team is constantly playing catch up to try and provide enough support to improve Gutenberg. And even now, while it is more or less accessible, there are critical features that are not yet implemented. There are entirely new interface patterns introduced on a regular basis that break prior accessibility expectations.”

\n\n\n\n

WordPress is also being used by millions of people who are constantly reporting issues to fuel the software’s continued refinement, which increases the backlog of issues. Unfortunately, Studio 24 did not properly evaluate Gutenberg against the proprietary CMS’s in order to determine if these software projects are in any better shape.

\n\n\n\n

Instead, they decided that Craft CMS’s community was more receptive to collaborating on issues without reaching out to WordPress. Given the W3C’s stated preference for open source software, WordPress, as the only CMS under consideration with an OSD-compliant license, should have received the same accessibility evaluation.

\n\n\n\n

“I can’t make any statements that would be meaningful about the other content management systems under consideration; but if WordPress wants to be taken seriously in environments where accessibility is a legal, ethical, and mission imperative, there’s still a lot of work to be done,” Dolson said.

\n\n\n\n

Studio 24’s evaluation may not have been equitable to the only open source CMS under consideration, but the situation serves to highlight a unique quandary: when using open source software becomes the impractical choice for organizations requiring a high level of accessibility in their authoring tools.

\n\n\n\n

“Studio 24 ultimately determined that working with a CMS to make it better was more possible with a smaller, proprietary vendor than with a large open-source project,” accessibility advocate Brian DeConinck said. “Project leadership would be more receptive, and the smaller community means changes can be made more quickly. That should prompt a lot of soul-searching for…well, everyone. What does that say about the future of open source?”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 04:56:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:38;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"Gary: More than 280 characters\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:25:\"https://pento.net/?p=5405\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"https://pento.net/2020/09/29/more-than-280-characters/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5187:\"

It’s hard to be nuanced in 280 characters.

\n\n\n\n

The Twitter character limit is a major factor of what can make it so much fun to use: you can read, publish, and interact, in extremely short, digestible chunks. But, it doesn’t fit every topic, ever time. Sometimes you want to talk about complex topics, having honest, thoughtful discussions. In an environment that encourages hot takes, however, it’s often easier to just avoid having those discussions. I can’t blame people for doing that, either: I find myself taking extended breaks from Twitter, as it can easily become overwhelming.

\n\n\n\n

For me, the exception is Twitter threads.

\n\n\n\n

Twitter threads encourage nuance and creativity.

\n\n\n\n

Creative masterpieces like this Choose Your Own Adventure are not just possible, they rely on Twitter threads being the way they are.

\n\n\n\n
\n

Being Beyoncé’s assistant for the day: DONT GET FIRED THREAD pic.twitter.com/26ix05Hkhp

— green chyna (@CORNYASSBITCH) June 23, 2019
\n
\n\n\n\n

Publishing a short essay about your experiences in your job can bring attention to inequality.

\n\n\n\n
\n

DOWNTOWN BROOKLYN: I\'m working arraignments tonight, representing poor New Yorkers who were arrested yesterday on Thanksgiving.

It was the coldest Thanksgiving in more than a century. Tonight\'s also bitterly cold, even in the courtroom. I\'m wearing my scarf & coat.

— Rebecca Kavanagh (@DrRJKavanagh) November 24, 2018
\n
\n\n\n\n

And Tumblr screenshot threads are always fun to read, even when they take a turn for the epic (over 4000 tweets in this thread, and it isn’t slowing down!)

\n\n\n\n
\n

Tumblr textposts thread, probably?

— we are a family forged in bureaucracy (@ex_aItiora) August 26, 2019
\n
\n\n\n\n

Everyone can think of threads that they’ve loved reading.

\n\n\n\n

My point is, threads are wildly underused on Twitter. I think I big part of that is the UI for writing threads: while it’s suited to writing a thread as a series of related tweet-sized chunks, it doesn’t lend itself to writing, revising, and editing anything more complex.

\n\n\n\n

To help make this easier, I’ve been working on a tool that will help you publish an entire post to Twitter from your WordPress site, as a thread. It takes care of transforming your post into Twitter-friendly content, you can just… write. \"?\"

\n\n\n\n

It doesn’t just handle the tweet embeds from earlier in the thread: it handles handle uploading and attaching any images and videos you’ve included in your post.

\n\n\n\n\n\n\n\n

All sorts of embeds work, too. \"?\"

\n\n\n\n
\n
\n
\n\n\n\n

It’ll be coming in Jetpack 9.0 (due out October 6), but you can try it now in the latest Jetpack Beta! Check it out and tell me what you think. \"?\"

\n\n\n\n

This might not fix all of Twitter’s problems, but I hope it’ll help you enjoy reading and writing on Twitter a little more. \"?\"

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 29 Sep 2020 02:33:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Gary\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:39;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"WPTavern: Themes Team Releases a Web Fonts Loader, Likely To Prohibit Hotlinking Any Off-Site Assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105363\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:243:\"https://wptavern.com/themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets?utm_source=rss&utm_medium=rss&utm_campaign=themes-team-releases-a-web-fonts-loader-likely-to-prohibit-hotlinking-any-off-site-assets\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5815:\"

Last Friday, the WordPress Themes Team announced the release of its new Webfonts Loader project. It is a drop-in script that allows theme authors to load web fonts from the user’s site instead of a third-party CDN. The secondary message included in the team’s announcement is that it no longer plans to allow themes to hotlink Google Fonts in the future.

\n\n\n\n

Throughout most of the team’s history, it has not allowed themes to hotlink or use CDNs for hosting theme assets, such as CSS, JavaScript, and fonts. The one exception to this rule was the use of Google Fonts. This allowed themes to have richer typography options at their disposal from what the team has generally declared a reliable source.

\n\n\n\n

“The exception was made because there was no practical way to not have the exception at the time,” said Aria Stathopoulos, a Themes Team representative and developer behind the Webfonts Loader project. “The exception for Google Fonts was made out of necessity. Now that there is another way, the exception will not be necessary.”

\n\n\n\n

In effect, disallowing the Google Fonts CDN would not be a new ban. It would be a removal of an exception to the existing ban.

\n\n\n\n

Google Fonts has become so embedded into the theme developer toolset over the years, there was no way the team could simply pull the plug and prohibit the use of the CDN overnight. If the Themes Team members wanted to focus more on privacy, they would need to build a tool that made it dead simple for theme authors to use.

\n\n\n\n

There is no hard deadline for when the team will remove the exception for Google Fonts, and it is not set in stone at this point. Stathopoulos said removing it has been the goal from the beginning, disallowing all CDNs. However, it took a while to find an efficient way to handle this. With a viable alternative in place, they can discuss moving forward.

\n\n\n\n

Webfonts Loader for Themes

\n\n\n\n

The Webfonts Loader project keeps it simple for theme authors. It introduces a new wptt_get_webfont_styles() function that developers can plug in a stylesheet URL. Once a page is loaded with that function call, it will download the fonts locally to a /fonts folder in the user’s /wp-content directory. This way, fonts will always be served from the user’s site.

\n\n\n\n

The system is not limited to Google Fonts either. Any URL that serves CSS with an @font-face {} rule will work. It does not currently include authentication for CDNs that require API keys, such as Adobe Fonts. However, that is something the team might add in the future.

\n\n\n\n

“For end-users, moving away from CDNs and locally hosting web fonts will improve performance (fewer handshake roundtrips for SSL), and is the privacy-conscious choice,” said Stathopoulos. “The only ‘valid privacy concern’ is that the web fonts’ CDN does not disclose information that is fundamental to the GDPR: what information gets logged, for how long these logs remain, how they are processed, if there is any cross-referencing with all the other wealth of information the company has from users, etc. The concern is a lack of disclosure and information. If a site owner doesn’t know what kind of information a third-party logs for its visitors, then they should ethically not enforce that on their visitors. With this package, the CDN is removed from the equation and the font still gets served fast — if not faster.”

\n\n\n\n

A Path to Core WordPress

\n\n\n\n

Today, there is now a broader focus on privacy concerns related to third-party resources, particularly with tech giants like Google. Such concerns extend to whether third parties are tracking users or collecting data. Additional concerns are around whether sites are disclosing the use of third-party resources, which may be required in some jurisdictions. Site owners who are often unable to work through the web of potential issues are stuck in the middle.

\n\n\n\n

Jono Alderson opened a ticket to create an API for loading web fonts locally in core WordPress in February 2019. It is a lengthy and detailed proposal, but it has yet to see much buy-in outside of a handful of developers.

\n\n\n\n

“If such a script is standardized and included in WordPress core, one of the main benefits would be more respect for the end-user’s privacy,” said Stathopoulos. “In the end, that’s all privacy is about: respecting users.”

\n\n\n\n

A standard API like Alderson proposes could solve some issues. Namely, it would virtually eliminate any privacy concerns. However, loading fonts locally could allow WordPress to optimize font loading and would create a shared system where plugins and themes do not load duplicate assets because of the current limitations of the enqueuing system. A standard API would also put the responsibility of efficiently loading fonts on WordPress’s shoulders instead of theme and plugin developers.

\n\n\n\n

The Themes Team’s new project is a solid start and strengthens the current proposal.

\n\n\n\n

“If we’re serious about WordPress becoming a fast, privacy-friendly platform, we can’t rely on theme developers to add and manage fonts without providing a framework to support them,” wrote Alderson in the ticket.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 28 Sep 2020 20:58:48 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:40;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:87:\"WPTavern: Fuxia Scholz First to Pass 100K Reputation Points on WordPress Stack Exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105282\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:219:\"https://wptavern.com/fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange?utm_source=rss&utm_medium=rss&utm_campaign=fuxia-scholz-first-to-pass-100k-reputation-points-on-wordpress-stack-exchange\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5096:\"

Fuxia Scholz, a prolific WordPress Stack Exchange (WPSE) contributor, is the first member to reach 100,000 reputation points. The popular Q&A community site rewards expert advice by floating the highest quality answers to the top, allowing users to earn reputation points. The gamified help community has proven to be more motivating for developers than many traditional forums, since the upvotes communicate how useful their answers are to others.

\n\n\n\n
\n\n\n\n

Scholz started on Stack Overflow a few months before WordPress had its own site. She wrote around 50 answers and made connections with other WordPress developers ahead of the site’s beta phase in June 2010. Once the site graduated and got its own logo and design, Scholz started writing more.

\n\n\n\n

“One core idea for all Stack Exchange sites is gamification: You earn reputation, and you get access to certain privileges,” Scholz said.

\n\n\n\n

“You can say I got a bit addicted. My favorite questions were about problems for which I didn’t know the answer, and couldn’t find one with a search engine, because no one else had solved that before. I used my answers to teach myself, and I learned a lot this way! In May 2011 my reputation on WPSE was already higher than on Stack Overflow, and for the next years it went up in a steep curve.” Ten years after WPSE launched, Scholz has become the first to reach 100,000 reputation points.

\n\n\n\n

“What reputation and karma do is send a message that this is a community with norms, it’s not just a place to type words onto the internet. (That would be 4chan.)” Stack Overflow co-creator Joel Spolsky said. “We don’t really exist for the purpose of letting you exercise your freedom of speech. You can get your freedom of speech somewhere else. Our goal is to get the best answers to questions. All the voting makes it clear that we have standards, that some posts are better than others, and that the community itself has some norms about what’s good and bad that they express through the vote.”

\n\n\n\n

The reputation points were originally inspired by Reddit Karma. Spolsky admits that the points not a perfect system but they do tend to “drive a tremendous amount of good behavior.” Gamification can shape and encourage certain behaviors but Spolsky said it’s a weak force that cannot motivate people to do things they are not already interested in doing. For Scholz, it was the community aspect and an earned sense of ownership and responsibility that kept her hooked.

\n\n\n\n

“In 2012, the community elected me as a moderator, and that changed a lot,” she said. “Now it wasn’t just a game anymore, it was a duty. I felt responsible for the site. I still do. I also found some friends on there. We met at WordCamps and in private, and worked together on different projects.”

\n\n\n\n

Scholz no longer works in development and said she doesn’t care about WordPress anymore, but she is still a regular contributor on the WPSE.

\n\n\n\n

“I switched careers and work as a writer, translator, and community manager for Chess24.com now,” she said. “But I still care about the site WordPress Stack Exchange! I keep an eye on new tags, handle flagged posts and comments, try to make every new user feel welcome, and I search for people who are abusing the system — vote fraud and spam. And, very rarely, I even write an answer, because I still know all this stuff.

\n\n\n\n

“Checking the site has become a part of my daily routine, like feeding the cat.”

\n\n\n\n

This daily habit has snowballed into Scholz racking up more than 2,000 answers. She is getting upvotes on many of her old answers nearly every day, which is what pushed her over the 100k milestone.

\n\n\n\n

“There is a lot to say about the way our site developed over the years,” Scholz said. “I’m not happy about some things. The enthusiasm of the early days is gone. We don’t have enough regulars, there is no discussion about the site on WordPress Development Meta Stack Exchange, and our chat, once very active, funny, and friendly, is now almost dead.

\n\n\n\n

“Maybe that’s normal, I don’t know. But it’s still ‘my’ site. Reputation and badges don’t really mean anything for a long time now, but keeping the site working, useful and friendly is more important.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 26 Sep 2020 15:27:03 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:41;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:82:\"WPTavern: PhotoPress Plugin Seeks to Revolutionize Photography for WordPress Users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=104770\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:209:\"https://wptavern.com/photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users?utm_source=rss&utm_medium=rss&utm_campaign=photopress-plugin-seeks-to-revolutionize-photography-for-wordpress-users\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5638:\"

Peter Adams, the owner of the PhotoPress plugin, announced a couple of weeks ago that now is the time for his project to take center stage. “It’s Time for PhotoPress,” read the title of his post in which he laid out a four-phase plan for the future of his project.

\n\n\n\n

Adams is no stranger to manipulating WordPress to suit the needs of photographers. He described photography as his first love and second career. He initially found the art of taking photos in high school and set off to college to become a professional photographer in the early ’90s.

\n\n\n\n

As his university graduation loomed, he was recruited to run web development for an internet ad agency that built websites for Netscape, Bill Clinton’s White House, and dozens of Fortune 500 companies. He spent the next 15 years starting or running tech companies before returning to his roots as a photographer.

\n\n\n\n

Today, he photographs for various magazines and companies. And, that’s where his PhotoPress project comes in.

\n\n\n\n

“As far as WordPress has come, it is at risk of losing an entire generation of photographers to photo website services such as Photoshelter, SmugMug, Squarespace, and PhotoFolio,” he said. Adams wants to change that, making WordPress the go-to platform for photographers around the world.

\n\n\n\n

The Jetpack of Photography Plugins

\n\n\n\n

If you dig into the history of the PhotoPress plugin on WordPress.org, it seems to have a 15-year history. However, this is not the same plugin that was published a decade and a half ago by a different developer. The original plugin is now defunct, and Adams took over when the name was freed up on the directory.

\n\n\n\n

Adams wrote in his announcement post that WordPress has done a great job of delivering several media features over the years. “Yet despite that, there are still many rough edges and missing features that keep WordPress from being the first choice for a photographer that needs to publish a beautiful portfolio of their work, put their image catalog/archive online, or showcase a photo editorial/project.”

\n\n\n\n

He outlined a list of 10 specific problem areas that he wants to address in a “Jetpack-like” plugin for photographers. This is the bread and butter of the first of the planned four phases, which he said is about 80% finished. He had originally planned to develop PhotoPress as a series of separate plugins, each addressing a specific problem. Now, it is a single plugin with modules than can be enabled or disabled.

\n\n\n\n

When asked why the “right time” is now, Adams explained it is because the Gutenberg (block editor) project is a giant leap forward in usability in terms of creating photography blogs.

\n\n\n\nPhotoPress Gallery block in the editor.\n\n\n\n

“Photogs are a rare breed of non-technical users with high design sense,” he said. “Things that I used to have to teach photographers to do using shortcode syntax and custom CSS can now be simple controls with live feedback inside a Gutenberg block. It’s really a game-changer for getting people comfortable with customizing things like gallery styling — which is the number one thing photographers need to do.”

\n\n\n\n

The primary piece of the PhotoPress plugin is its custom PhotoPress Gallery block. It allows users to choose between a range of gallery styles, such as columns, masonry, justified, and mosaic. Each style has its own options. Images can also be launched into a slideshow when one is clicked.

\n\n\n\n

Based on some quick tests, the block’s front-end output will go farther with some themes than others. This is mainly because of conflicting CSS and issues which can be solved by testing against more themes.

\n\n\n\n

Aside from the block, the plugin can automatically extract image metadata and group that data through custom taxonomies, such as cameras, lenses, locations, keywords, and more. WordPress stores this information out of the box, but it is hidden away as post meta. The plugin uses the taxonomy system to make it manageable for end-users.

\n\n\n\n

Ultimately, Adams set out to create a photography plugin that fits in with the WordPress admin user interface and experience, which he has accomplished.

\n\n\n\n

The Future of PhotoPress

\n\n\n\n

The project is still a work in progress. Adams is still moving through Phase I of the four-phase plan. Once it is complete, he can move on to the next steps in the process.

\n\n\n\n

Phase II is to create themes that are designed specifically to work with the PhotoPress plugin. He has three planned thus far. One for handling portfolio sites. Another for creating a stock photo archive. And the last for photojournalism and exhibits. Each will be built on top of his photography theme framework.

\n\n\n\n

The themes in Phase II will likely be commercial products. Adams said he needs a way to fund the next phases of the project. He hopes to have this step underway by the end of the year.

\n\n\n\n

For 2021, he wants to begin tackling Phases III and IV. The former will be a website-as-a-service (WaaS) similar to WordPress.com but for photographers. It will begin as a paid project but could have some free options for emerging photographers and students. The final phase is to build an onboarding system.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 25 Sep 2020 19:08:15 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:42;a:6:{s:4:\"data\";s:13:\"\n\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"WPTavern: Google Officially Releases Its Web Stories for WordPress Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105227\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:191:\"https://wptavern.com/google-officially-releases-its-web-stories-for-wordpress-plugin?utm_source=rss&utm_medium=rss&utm_campaign=google-officially-releases-its-web-stories-for-wordpress-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5593:\"Web Stories for WordPress dashboard.\n\n\n\n

Two and a half months after the launch of its public beta, Google released its Web Stories for WordPress plugin. So far, the plugin has over 10,000 active installations and has garnered a solid five-star rating from four reviews.

\n\n\n\n

Google created the Web Stories format through its AMP Project to allow publishers to create visually-rich stories. It is primarily geared toward mobile site visitors, allowing them to quickly jump through story pages with small chunks of content.

\n\n\n\n

The Web Stories plugin creates a visual interface within WordPress for creating Stories. It breaks away from the traditional WordPress interface and introduces users to an almost Photoshop-like experience for building out individual Stories. The Stories editor is completely drag-and-drop.

\n\n\n\n

The plugin also offers eight predesigned templates out of the box that cover a small range of niches. However, according to Google’s announcement, the company plans to add more templates in future updates.

\n\n\n\n

Web Stories Are for Storytelling

\n\n\n\n

“Firstly…the power of Stories,” wrote Jamie Marsland, founder of Pootlepress, in a Twitter thread. “Stories are how we (humans) see the world and share our experiences. Up to now the platforms that we have to tell stories have been limited to books/films/tv/websites/blogs/instagram stories etc.”

\n\n\n\n

“Websites are ok for telling stories but in many ways the format doesn’t really fit the linear arc of storytelling. When Marshall McLuhan said ‘the medium is the message’ in 1964 he was talking about how the medium itself has a social impact, and change the communication itself…and the possibilities for what is communicated and how it is perceived. But we should keep coming back to Stories. Stories are the key here imo. Now we have an open format to tell Stories, and we have an open platform (WordPress) where those Stories can be told easily.”

\n\n\n\n

Marsland finished his thread by saying that using Stories as a replacement for a brochure or website is a missed opportunity. He said that it was a platform for storytelling and should be used as such.

\n\n\n\n

It is far too early to tell if Web Stories will simply be a fad or still in wide use years from now. The technology certainly lends itself well to telling stories, particularly in mobile format, but I doubt we have seen the best of what is possible on the web. The format feels too limited to be the end-all-be-all of storytelling. It is merely one medium that will live and die by its popularity with users.

\n\n\n\n

With the right design skills, some people will craft beautiful Web Stories. And, that is just what Marsland has done with the first Story he shared:

\n\n\n\nPage from the Wilson and Pootle Web Story by Jamie Marsland.\n\n\n\n

I agree with his conclusion. Web Stories should be about storytelling. When you move outside of that zone, the technology feels out of place.

\n\n\n\n

Where I disagree is that websites are not ideal for storytelling. Ultimately, the WordPress block editor will allow artistic end-users to craft intricate stories, mixing content and design in ways that we have not seen. We are just now scratching the surface. I expect our community of developers to build more intricate tools than what the Web Stories plugin currently allows, and we can do so in a way that revolutionizes storytelling on the web.

\n\n\n\n

New Features

\n\n\n\nStory editor with Unsplash photo integration.\n\n\n\n

The Web Stories plugin now adds support for Unsplash images and Coverr videos out of the box. The plugin adds a new tab with a “media” icon. For users of the first beta version of the plugin, this may be a bit confusing. The previous media icon was for a tab that displayed the user’s media. Now, the user’s media is under the tab with the “upload” icon.

\n\n\n\n

It is also not immediately clear that the Unsplash images and Coverr videos are not hosted on the site itself. There is a “powered by” notice at the bottom of the tab, but it can be easy to miss because it blends in with the media in the background.

\n\n\n\n

Media from Unsplash and Coverr is hosted off-site and not downloaded to the user’s WordPress media library. I could find no mention of this in the plugin’s documentation. Such hotlinking was a cause for debate over the recent official release of the Unsplash plugin.

\n\n\n\n

Google also announced it planned to add more “stock media integrations” in the near future. According to a document shared via a GitHub ticket, such future integrations may include Google Photos and GIF-sharing site Tenor.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 21:13:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:43;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"WPTavern: W3C Drops WordPress from Consideration for Redesign, Narrows CMS Shortlist to Statamic and Craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105108\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:255:\"https://wptavern.com/w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft?utm_source=rss&utm_medium=rss&utm_campaign=w3c-drops-wordpress-from-consideration-for-redesign-narrows-cms-shortlist-to-statamic-and-craft\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:11563:\"

The World Wide Web Consortium (W3C), the international standards organization for the web, is redesigning its website and will soon be selecting a new CMS. Although WordPress is already used to manage W3C’s blog and news sections of the website, the organization is open to adopting a new CMS to meet its list of preferences and requirements.

\n\n\n\n

Studio 24, the digital agency selected for the redesign project, narrowed their consideration to three CMS candidates:

\n\n\n\n
  1. Statamic
  2. Craft CMS
  3. WordPress
\n\n\n\n

Studio 24 was aiming to finalize their recommendations in July but found that none of them complied with the W3C’s authoring tool accessibility guidelines. The CMS’s that were better at compliance with the guidelines were not as well suited to the other project requirements.

\n\n\n\n

In the most recent project update posted to the site, Studio 24 reported they have shortlisted two CMS platforms. Coralie Mercier, Head of Marketing and Communications at W3C, confirmed that these include Statamic and Craft CMS.

\n\n\n\n

WordPress was not submitted to the same review process as the Studio 24 team claims to have extensive experience working with it. In the summary of their concerns, Studio 24 cited Gutenberg, accessibility issues, and the fact that the Classic Editor plugin will stop being officially maintained on December 31st, 2021:

\n\n\n\n

First of all, we have concerns about the longevity of WordPress as we use it. WordPress released a new version of their editor in 2018: Gutenberg. We have already rejected the use of Gutenberg in the context of this project due to accessibility issues.

If we choose to do away with Gutenberg now, we cannot go back to it at a later date. This would amount to starting from scratch with the whole CMS setup and theming.

Gutenberg is the future of WordPress. The WordPress core development team keeps pushing it forward and wants to roll it out to all areas of the content management system (navigation, sidebar, options etc.) as opposed to limiting its use to the main content editor as is currently the case.

This means that if we want to use WordPress long term, we will need to circumvent Gutenberg and keep circumventing it for a long time and in more areas of the CMS as time goes by. 

\n\n\n\n

Another major factor in the decision to remove WordPress from consideration was that they found “no elegant solution to content localization and translation.”

\n\n\n\n

Studio 24 also expressed concerns that tools like ACF, Fewbricks, and other plugins might not being maintained for the Classic Editor experience “in the context of a widespread adoption of Gutenberg by users and developers.”

\n\n\n\n

“More generally, we think this push to expand Gutenberg is an indication of WordPress focusing on the requirements of their non-technical user base as opposed to their audience of web developers building custom solutions for their clients.”

\n\n\n\n

It seems that the digital agency W3C selected for the project is less optimistic about the future of Gutenberg and may not have reviewed recent improvements to the overall editing experience since 2018, including those related to accessibility.

\n\n\n\n

Accessibility consultant and WordPress contributor Joe Dolson recently gave an update on Gutenberg accessibility audit at WPCampus 2020 Online. He reported that while there are still challenges remaining, many issues raised in the audit have been addressed across the whole interface and 2/3 of them have been solved. “Overall accessibility of Gutenberg is vastly improved today over what it was at release,” Dolson said.

\n\n\n\n

Unfortunately, Studio 24 didn’t put WordPress through the same content creation and accessibility tests that it used for Statamic and Craft CMS. This may be because they had already planned to use a Classic Editor implementation and didn’t see the necessity of putting Gutenberg through the paces.

\n\n\n\n

These tests involved creating pages with “flexible components” which they referred to as “blocks of layout,” for things like titles, WYSIWYG text input, and videos. It also involved creating a template for news items where all the content input by the user would be displayed (without formatting).

\n\n\n\n

Gutenberg would lend itself well to these uses cases but was not formally tested with the other candidates, due to the team citing their “extensive experience” with WordPress. I would like to see the W3C team revisit Gutenberg for a fair shake against the proprietary CMS’s.

\n\n\n\n

W3C Is Prioritizing Accessibility Over Its Open Source Licensing Preferences

\n\n\n\n

The document outlining the CMS requirements for the project states that “W3C has a strong preference for an open-source license for the CMS platform” as well as “a CMS that is long-lived and easy to maintain.” This preference may be due to the economic benefits of using a stable, widely adopted CMS, or it may be inspired by the undeniable symbiosis between open source and open standards.

\n\n\n\n

“The industry has learned by experience that the only software-related standards to fully achieve [their] goals are those which not only permit but encourage open source implementations. Open source implementations are a quality and honesty check for any open standard that might be implemented in software…”

Open Source Initiative
\n\n\n\n

WordPress is the only one of the three original candidates to be distributed under an OSD-compliant license. (CMS code available on GitHub isn’t the same.)

\n\n\n\n

Using proprietary software to publish the open standards that underpin the web isn’t a good look. While proprietary software makers are certainly capable of implementing open standards, regardless of licensing, there are a myriad of benefits for open standards in the context of open source usage:

\n\n\n\n

“The community of participants working with OSS may promote open debate resulting in an increased recognition of the benefits of various solutions and such debate may accelerate the adoption of solutions that are popular among the OSS participants. These characteristics of OSS support evolution of robust solutions are often a significant boost to the market adoption of open standards, in addition to the customer-driven incentives for interoperability and open standards.”

International Journal of Software Engineering & Applications
\n\n\n\n

Although both Craft CMS and Statamic have their code bases available on GitHub, they share similarly restrictive licensing models. The Craft CMS contributing document states:

\n\n\n\n

Craft isn’t FOSS
Let’s get one thing out of the way: Craft CMS is proprietary software. Everything in this repo, including community-contributed code, is the property of Pixel & Tonic.

That comes with some limitations on what you can do with the code:

– You can’t change anything related to licensing, purchasing, edition/feature-targeting, or anything else that could mess with our alcohol budget.
– You can’t publicly maintain a long-term fork of Craft. There is only One True Craft.

\n\n\n\n

Statamic’s contributing docs have similar restrictions:

\n\n\n\n

Statamic is not Free Open Source Software. It is proprietary. Everything in this and our other repos on Github — including community-contributed code — is the property of Wilderborn. For that reason there are a few limitations on how you can use the code:

\n\n\n\n

Projects with this kind of restrictive licensing often fail to attract much contribution or adoption, because the freedoms are not clear.

\n\n\n\n

In a GitHub issue requesting Craft CMS go open source, Craft founder and CEO Brandon Kelly said, “Craft isn’t closed source – all the source code is right here on GitHub,” and claims the license is relatively unrestrictive as far as proprietary software goes, that contributing functions in a similar way to FOSS projects. This rationale is not convincing enough for some developers commenting on the thread.

\n\n\n\n

“I am a little hesitant to recommend Craft with a custom open source license,” Frank Anderson said. “Even if this was a MIT+ license that added the license and payment, much like React used to have. I am hesitant because the standard open source licenses have been tested.”

\n\n\n\n

When asked about the licensing concerns of Studio 24 narrowing its candidates to two proprietary software options, Coralie Mercier told me, “we are prioritizing accessibility.” A recent project update also reports that both CMS suppliers W3C is reviewing “have engaged positively with authoring tool accessibility needs and have made progress in this area.”

\n\n\n\n

Even if you have cooperative teams at proprietary CMS’s that are working on accessibility improvements as the result of this high profile client, it cannot compare to the massive community of contributors that OSD-compliant licensing enables.

\n\n\n\n

It’s unfortunate that the state of open source CMS accessibility has forced the organization to narrow its selections to proprietary software options for its first redesign in more than a decade.

\n\n\n\n

Open standards go hand in hand with open source. There is a mutually beneficial connection between the two that has caused the web to flourish. I don’t see using a proprietary CMS as an extension of W3C values, and it’s not clear how much more benefit to accessibility the proprietary options offer in comparison. W3C may be neutral on licensing debates, but in the spirit of openness, I think the organization should adopt an open source CMS, even if it is not WordPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 24 Sep 2020 20:13:24 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:44;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:79:\"WPTavern: First Look at Twenty Twenty-One, WordPress’s Upcoming Default Theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105166\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:195:\"https://wptavern.com/first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme?utm_source=rss&utm_medium=rss&utm_campaign=first-look-at-twenty-twenty-one-wordpresss-upcoming-default-theme\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6907:\"

Fashion is ephemeral. Art is eternal. Indeed what is a fashion really? A fashion is merely a form of ugliness so absolutely unbearable that we have to alter it every six months!

\n\n\n\n

Thus wrote Oscar Wilde on Victorian-era fashion in an article titled “The Philosophy of Dress” for the New-York Tribune in 1885.

\n\n\n\n

In many ways, WordPress theming is the same as the ever-changing landscape of fashion. Rounded corners are in one day and out the next. Box shadows are in one year after being frowned up just months earlier. Perhaps web design is so intolerable that we must change it every six months. Or, at least freshen it up every year in the case of WordPress.

\n\n\n\n

If art is eternal, there are only two default, Twenty* themes that I can truly recall from past years: Twenty Ten and Twenty Fourteen — yes, Twenty Twenty is memorable, but it is also still the current default. Twenty Ten was a classic that paid homage to WordPress’s past. Twenty Fourteen was such a leap away from tradition that it is hard to forget. Everything else has seemed to fade to varying degrees.

\n\n\n\n

With WordPress 5.6 and the end of the year looming, it is time to look forward to the latest trend. As Mel Choyce-Dwan noted in the announcement of Twenty Twenty-One, the next default theme, “Pastels and muted colors are pretty in right now.”

\n\n\n\n

She is not wrong. The colors are a refreshing change of pace. Now that we are into the second day of autumn, I am getting the good kind of vibes from some of the more earthy-tones from a couple of the color palettes expected to ship with the theme.

\n\n\n\nPotential color palette options for Twenty Twenty-One.\n\n\n\n

Whether Twenty Twenty-One will be a fashionable theme for the year or art that we can remember a decade from now, only history will be able to judge. For now, let’s enjoy the creation and take a look at what we should expect from the next default WordPress theme.

\n\n\n\n

The Current Twenty Twenty-One

\n\n\n\n

The new default theme is a fork of Automattic’s Seedlet, a project in which I lauded as the next step in the evolution of theming. It is a theme that is focused on WordPress’s future of being completely comprised of blocks. It gives us an ideal insight into where theme development is heading. It makes sense as the foundation for the new default. Few other themes would make for a good starting point right now. With WordPress theme development in flux, Seedlet is simply ahead of the pack in terms of foundational elements.

\n\n\n\nSeedlet WordPress theme screenshot.\n\n\n\n

“This provides us with a thorough system of nested CSS variables to make child theming easier, and to help integrate with the global styles functionality that’s under development for full-site editing,” wrote Choyce-Dwan of using Seedlet as a starting point.

\n\n\n\n

There are no plans to spin up a Google Web Font for this theme. The design team is going native and sticking with the default system font stack. Choyce-Dwan listed several reasons for the choice, such as keeping a neutral font that allows broad use, speed, and customizability via a child theme.

\n\n\n\n

Despite the neutral font, the default pastel green is a fairly opinionated design decision. It will not be used broadly across industries. However, the team plans to create multiple color palettes that will give it more range. Presumably, these palettes can also be overwritten.

\n\n\n\nPastel green color scheme on single post view.\n\n\n\n

Other than the colors, the design is relatively simple. Choyce-Dwan said that the theme’s block patterns support is where it will be truly unique.

\n\n\n\n

I was initially unhappy with the patterns that were going to ship with WordPress 5.5. However, an 11th-hour update improved the situation so that they did not feel entirely experimental. The foundational Seedlet theme for Twenty Twenty-One has some unique patterns that begin to scratch the surface of what’s possible with this WordPress feature. My hope is that the new default theme steps it up a notch.

\n\n\n\n

Currently, the theme does not register any custom patterns. However, it has a placeholder file and a note that they are a work in progress. Choyce-Dwan shared some patterns the team has already designed in the announcement.

\n\n\n\nCurrently-designed block patterns.\n\n\n\n

“We’ll be relying on our talented community designers for more ideas,” she wrote. The team has also created a GitHub template for anyone to contribute pattern design ideas.

\n\n\n\n

Currently, the theme does not support the upcoming full-site editing feature of WordPress. After the Beta 1 release of WordPress 5.6, the team plans to begin exploring the addition of this support. WordPress is expected to ship a public beta of full-site editing in its next major release, but it is unclear whether it will be far enough along to be a headline feature for the Twenty Twenty-One theme.

\n\n\n\n

The team and volunteers have less than a month before the October 20th deadline for committing the new theme to trunk, the core WordPress development branch. At that stage, the theme should be nearly complete and ready for production. Of course, there will be several rounds of patches, bug fixes, and updates before WordPress 5.6 lands in December. Right now is the best time for anyone who wants to get involved with Twenty Twenty-One to do so.

\n\n\n\n

Useful links with more information:

\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 20:01:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:45;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:37:\"HeroPress: Hello World – Hevo Nyika\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=3308\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:176:\"https://heropress.com/essays/hello-world-discovering-the-world-through-wordpress/#utm_source=rss&utm_medium=rss&utm_campaign=hello-world-discovering-the-world-through-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:14438:\"\"Pull

Unokwanisa kuverenga rondedzero iyi muChiShona

\n

So I chose a career in Web Development!!

\n

To be honest it’s kind of funny when I think about it and quite surreal to be here talking about my story. It has been a journey and I would like to share my story with you.

\n

I have been lucky in the Dad department. My Dad encouraged me to work hard and dream big from a very young age. I remember occasionally having ‘when I grow up’ talks.

\n

For quite some time I wanted to be a Judge, however awesome this dream sounds it was not very inspired. After binge-watching Judge Judy for a whole weekend, I started calling myself Judge Thelma. Though I don’t remember much of this my sister says that I used to say I would arrest all the men in the World if I ever became a Judge. HAHAHA! (clearly I didn’t understand how the World works)

\n

I did not understand what being a Judge meant or what was required for me to start banging that gavel to my heart’s desire. Eventually, I learnt that I had to become a lawyer first then magistrate before I could be nominated to be a Judge and let us just say that is how I sentenced that dream to a lifetime down the drain.

\n

See what I did there? hahaha!

\nWith Daddy Dearest\n

A few years later, I was in High School and that is when I decided to pursue a career in Computer Science. I did not know what I would be doing or how I would get there but I just knew that I was going to pursue a career in ICT. I wrote my first line of code when I was 16 years old.

\n

This was after I had joined the school’s computer class, initially, I thought I would be learning about Excel Sheets and Word Documents until I was assigned to write my first program in C (talk about a double-take!!). It was not easy but it was very exciting, l remember writing up simple code for a Video Club – a simple check-in/out for VHS tapes and CDs. Dear World, thus began my fascination with computers.

\n

Seven years later, I was now in university studying ICT as I had always wanted. I was doing a Bachelors in Business Management & Information Technology. In my third year, I was interning at a local Webdesign and hosting company. This was never my plan, I only took on that job after I had failed to get a job with local banks or telecommunications companies. Before I was introduced to Website Design I envisioned myself suiting up and working in IT Audit or offering IT support. Even though things did not go as I had planned, I am glad they did not exactly go my way in that aspect. So in 2017, I was designing websites using HTML, CSS, PHP, JavaScripts and Joomla which was the prefered content management system at that company. I knew about WordPress but I was not using it for anything. People have this misconception that WordPress is not for real developers and it is not secure and at that time I was one of those people.

\n

Finding my tribe

\n

One day when I was working at the front desk Thabo Tswana came to give a colleague of mine a purple WooCommerce pen. I did not know what WooCommerce was at that time but I was taken by the purple shirt and pen he was carrying. I asked him about it and he explained what WooCommerce was and that what he was carrying was called ‘swag’. So the love of freebies led me to the WordCamp Harare website, instead of buying a ticket I applied to volunteer. I learnt more about WordPress, I was a volunteer, without any knowledge on WordPress.org or WordPress.com. I only started using WordPress because of the awesome people that l had met at that Wordcamp.

\n

Everyone was so welcoming, a week later with help from Thabo I designed my first ever WP website.

\n

Soon after I was part of the community and a bit more involved in the meetups. We had our first-ever Women Who WordPress meetup in 2018. So many ladies came on board bloggers and developers alike. We were free to talk and discuss a lot of things. We had more time to discuss the difference between WordPress.com and WordPress.org we shared views on how to handle discrimination at work, how to promote your website and a whole lot of other things.

\n

\n

Establishing roots

\n

In 2018, Harare had its first-ever female Lead Organiser Tapiwanashe Manhobo whoop whoop! I was also part of the organising team that year, I was assigned to handle Harare’s first Kids Camp. The planning process was stressful because the economic crisis in Zimbabwe was getting worse, luckily we had over 8 months to plan and with help from sponsors, we managed to pull through. In the end, everything turned out great. I wrote an article about the Kids Camp here.

\n

After the first Kids Camp, we had several WordPressors that were enthusiasts about encouraging kids to embrace ICT. In 2019 we had not planned to have a Kids Camp because of financial constraints but to our surprise, we had some anonymous donations and we managed to have a WordPress Community outreach to a youth centre a week after our WordCamp. We had the outreach at the Centre for Total Transformation which is a non-formal school that caters for underprivileged and vulnerable children. We taught them about WordPress, Computer Hardware and Software.

\n

Here is a small video I took with Ellen when we were about to leave. Did l mention that I am terrible on camera? hahaha!

\n\n

Kids Camp 2019 – Centre for Total Transformation

\n

I have fallen deeply for WordPress because of the Community, I enjoy attending WordCamps, meeting new people and just learning new stuff. I have a huge list of WordCamps I need to attend before l kick the bucket, hopefully. Last year I managed to cross WordCamp

\n

Johannesburg off my bucket list. This year I was going to attend WordCamp Capetown but unfortunately, 2020 had other plans for the whole world. Anyway when everything is back to normal my plan to travel to WordCamps will proceed. (fingers crossed)

\n

Reaping Fruits

\n

Meanwhile, my plan to improve my developing skills has not been on hold. Even though I can still cook up code in C and Java, for now, I have also included WordPress PHP functions to the mix. It was not easy to get to this point, daring myself got me to this slightly better stage. My IQ is not way up there, however, I try to do my best where I can and I am happy to say it has paid off so far.

\n

Around November last year, I was designing as a freelancer while job hunting. Out of the blue l got a call for a job offer from Trust Nhokovenzo who is big on Digital marketing and also part of the WordPress Community. He had asked someone in the community about developers and my name happened to come up. So since February, I have been part of his team at Calmlock Digital Marketing Agency.

\n

There is so much more in the world of WordPress that l am yet to tap into so even though I am ending my write up here, for now, my story is going to continue …

\n

Until next time…

\n

Hevo Nyika

\n

Saka ini ndakasarudza kugadzira mawebhusayiti.

\n

Ndakaita rombo rakanaka pana baba vandakapihwa naMwari. Baba vangu vaindikurudzira kuti ndishande nesimba. Ndinoyeuka pano neapo tichiita hurukuro dzedu dzekuti ‘kana ndakura ndoda kuveyi’.

\n

Kwenguva yakati rebei ndaida kuve Mutongi. Kunyangwe ini ndisingazvirangariri mukoma wangu anotaura kuti ndaiti ndaizosunga varume vese vari pasi rino kana ndikangoita mutongi HAHAHA zveshuwa handaiziva kuti mitemo yenyika inofambiswa seyi.
\nNdanga ndisinga nzwisisi kuti kuva mutongi kwairevei kana zvaidikanwa kwandiri kuti nditange kurova iro ghavheu kuchishuwo chemoyo wangu. Pakupedzisira, ndakadzidza kuti ndaifanirwa kuzoita gweta ipapo magistrate ndisati ndasarudzwa kuita Mutongi naizvozvo ndokupera kwakaita chiroto chekuva Mutongi.

\nNa Baba Vangu\n

Gare gare papfura makore mashoma pandakanga ndave kuHigh School ndakanga ndakuda kuita basa rema kombiyuta. Ndakanyora mutsara wekutanga wekodhi pandaive nemakore gumi nematanhatu. Izvi zvakaitika mushure mekunge ndapinda mukirasi yemakombiyuta, pakutanga ndaifunga kuti ndinenge ndichidzidza nezveExcel Sheets neWord zvisineyi ndakaona ndakunyora kodhi yangu yekutanga muC. Zvaisave nyore kunyora kodhi asi zvainakidza kwazvo, ndorangarira ndichinyora kodhi yeVhidhiyo Kirabhu.

\n

Makore manomwe apfura, ndakanga ndava kuyunivhesiti ndichidzidza ICT zvandakagara ndakaronga. Ndaiita Bachelors muBusiness Management & Information Technology. Mugore rangu rechitatu ndainge ndave kushanda kune imwe kambani yaita zvekugadzira mawebhusaiti. Ndakawana basa iri mushure mekunge ndatadza kuwana basa kumabhanga. Kunyangwe hazvo zvinhu zvisina kuenda sezvandaive ndakaronga, ndinofara kuti hazvina kunyatso enda nenzira yangu. Saka muna 2017 ndaigadzira mawebhusaiti ndichishandisa HTML, CSS, PHP, JavaScript uye Joomla iyo yaive iyo inokurudzirwa kukambani kwandaive. Panguva iyi ndaiziva nezve WordPress asi ndakanga ndisingaishandisi.

\n

Kuwanana neWordPress

\n

Rimwe zuva pandakanga ndichishanda ndakaona Thabo Tswana akauya kuzopa mumwe mukomana wandayishanda naye chinyoreso cheWooCommerce. Ndakanga ndisingazive kuti WooCommerce yaive chii asi ndakafarira chinyoreso nehembe ye WooCommerce yaanga akapfeka. Ndakamubvunza nezvazvo akatsanangura kuti WooCommerce yaive chii. Saka nekudawo zvakanaka, zvemahara ndakaenda pawebhusaiti yeWordCamp Harare ndikabata zvimbo zvegore iroro. Ndakazvipira kubatsirawo vamwe vekuWordPress kuWordCamp Harare. Nerubatsiro kubva kunaThabo ndakagadzira webhusaiti yangu yekutanga yeWordPress vhiki rakatevera .

\n

Mushure mekunge ndaitawo chipato cheavo vanoshandisa WordPress ndakanga ndakuenda kumisangano yeWordPress yaitwa muHarare. Takaita musangano wevakadzi chete muna 2018. Vakadzi vazhinji vakauya kumusangano uyu. Tainga takasununguka kukurukura zvinhu zvakawanda. Takakurukura pamusoro pemutsauko uripo pakati peWordPress.com neWordPress.org takagovana maonero ekugadzirisa rusarura kubasa nezvimwewo.

\n

\n

Nguva yandakatanga kushandisa WordPress

\n

Muna 2018, kurongwa kweWordCamp Harare kwakatungamirwa kekutanga nemusikana ainzi Tapiwanashe Manhobo (waiva mufaro mukuru). Ndakanga ndiri mumwe wevairongawo naye. Hurongwa hwekuronga WordCamp Harare mugore iri hwainetsa pamusaka pekuoma kwehupfumi wemuZimbabwe, zvisineyi takaita rombo rakanaka nokuti takawana rubatsiro kubva kunevamwewo vanhu vakatiwedzera mari. Pakupedzisira, zvese zvakabudirira zvakanaka. Takarongawo WordCamp yevana varipasi pemakore gumi nechishanu, munokwanisa kuverenga pamusoro pezuva iri pawebhisaiti yangu apa.

\n

Mushure mekuita WordCamp yevana, takave nevamwe vanhu veWordPress aifarira kukurudzira vana kuti vagamuchire ICT. Muna 2019 takanga tisina kuronga kuve neWordCamo yeVana nekuda kwezvimhingamupinyi zvemari asi chakatishamisa ndechekuti takawana mari kubvawo kune vamwe. Takaita Camp iyi paCentre for Total Transformation chinova chikoro chisiri chepamutemo chinodzidzisa vana vanotambura. Tadzidzisa vana ava pamusoro peWordPress, Computer Hardware uye Software.

\n\n

Ndofarira WordPress zvakanyanya nekuda kweavo varimu nharaunda yacho, ini ndinonakidzwa nekuenda kumaWordCampi, kusangana nevanhu vatsva uye kungo dzidza zvinhu zvitsva. Gore rakapera ndakakwanisa kuyambuka muganhu weZimbabwe ndichienda kuWordCamp Johannesburg, dai pasina kuti 2020 nyika dzepasi rino dzakawirwa nedenda reCOVID 19 zvimwe ndingadayi ndakaenda kuWordCamp Capetown. Zvisinei hazvo kana denda ranani zvimwe ndichakwanisa kufamba ndichienda kumaWordCamp edzimwe nyika.

\n

Kukowa zvandakadyara

\n

Zvichakadaro, chirongwa changu chekuvandudza hunyanzvi hwangu hachina kumira. Kunyangwe ini ndichiri kukwanisa kubika kodhi muC uye Java, ikozvino, ndasanganisirawo WordPress PHP. Zvaive zvisiri nyore kusvika apa, zvakatora kuzvishingisa nekushanda nesimba. Ndinofara mwari aiva neni pamufambo wangu uyu.

\n

Muna Mbudzi gore rakapera, ndaive ndichigadzira mawebhusayiti apo nditsvaga basa. Pasina nguva ndakataura naTrust Nhokovenzo uyo akaandipa basa mukambani yake, kambani iyi inonzi Calmlock Digital Marketing Agency.

\n

Pane zvimwe zvakawanda kuWordPress zvandisati ndapinda mazviri. Nhaizvozvo kunyangwe ndiri kupedzisa kunyora kwangu apa, nyaya yehupenyu wangu ichaenderera mberi…

\n

Kusvikira nguva inotevera …

\n

…. tsvaga chinangwa chako, chiite mushe mushe ..

\n

The post Hello World – Hevo Nyika appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 06:00:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Thelma Mutete\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:46;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n\n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:102:\"WPTavern: WordPress Contributors Debate Dashboard Notice for Upcoming Facebook oEmbed Provider Removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105132\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:249:\"https://wptavern.com/wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-contributors-debate-dashboard-notice-for-upcoming-facebook-oembed-provider-removal\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5885:\"

WordPress contributors are discussing different strategies for responding to Facebook and Instagram dropping unauthenticated oEmbed support on October 24. WordPress will be removing both Facebook and Instagram as oEmbed providers. When a user attempts to embed content by pasting a URL as they have in the past, they may not understand why it no longer works. They may assume that WordPress broke embeds, causing an increase in the support burden for this change.

\n\n\n\n

A few participants on the trac ticket for this issue have suggested WordPress detect users who will be impacted and attempt to warn them with a notice.

\n\n\n\n

“Since this may impact users unknowingly, it is possible to push a dashboard notice to users who have Facebook/Instagram embeds in their content, showing for site admins, as a one-off that can be dismissed,” Marius Jensen said.

\n\n\n\n

“We’ve previously done post-update-processing to clean up comments, so the idea of looking over content for an embed isn’t completely outlandish, and would help with those who don’t follow WordPress’ usual channels to learn of this.”

\n\n\n\n

Others don’t see the necessity. “Why should we make exception here?” Milan Dinić said. “It’s not the first time oEmbed support was discontinued for a provider, and I don’t remember anything specific was done then.”

\n\n\n\n

There is still some uncertainty about what will happen with existing oEmbeds after Facebook updates its API. During a recent core developer meeting, Helen Helen Hou-Sandí confirmed that WordPress does not clear oEmbed caches regularly. “Technically oEmbed caches are cleared if you save and a valid response is returned, we do not do cron-based garbage collection,” Hou-Sandí said.

\n\n\n\n

In a post today on the core development blog, Jake Spurlock assured users and developers that the existing embeds added before Facebook’s API change should still work:

\n\n\n\n

Because oEmbed responses are cached in the database using the hidden oembed_cache post type, any embed added prior to the October 24th deadline will be preserved past the deprecation date. These posts are not purged by default in WordPress Core, so the contents of the embed will persist unless manually deleted.

\n\n\n\n

Marius Jensen cautioned that there is still the possibility that existing embeds may not work going, depending on what Facebook does.

\n\n\n\n

“We don’t know how they plan on implementing the use of unauthorized embed attempts,” Jensen said. “It could not return an embed code and your link would remain a plain link, or maybe they decide to return some kind of embedded ‘unauthorized’ content. I don’t think anyone has heard any specifics on how Facebook plans on doing this, so we’re all just kinda waiting to either hear more, or see what happens.”

\n\n\n\n

Jensen said WordPress doesn’t re-check the cached results except when something changes with the post, but there may be plugins that clean up temporary data that may create an unpredictable outcome.

\n\n\n\n

“The reliability of the caches are hard to determine (and being caches, it’s sort of in the term that it’s not guaranteed to always be there, but rather fetched and saved for a while when needed),” Jensen said.

\n\n\n\n

Ideally WordPress’ oEmbed caches will prevent millions of embeds from breaking, but it’s still unknown how Facebook and third party plugins could change things.

\n\n\n\n

Coming off a rocky 5.5 core update that deprecated jQuery Migrate and flooded official support forums with reports of broken sites, some contributors are wary of having another situation where users are left in the dark.

\n\n\n\n

“I think a dashboard notice is desirable,” Jon Brown said. “Otherwise we’re not preemptively warning people in a way they can prepare and transition to another solution. We’re letting them know the same instant it’s going to break (when editing a specific post). I don’t think we can safely assume cached data is going to persist forever either, plenty of routines out there purge transient data before its stated expiration date.

\n\n\n\n

“I see this as potentially being similar to the problems seen in dropping JQM. It’ll cause avoidable and silent breakage client side without even any error logging for a site developer to pick up on. In hindsight, what ideally would have happened with JQM would have been incorporating the detection code from Enable jQuery Migrate Helper into core temporarily, or simply installing that plugin automatically on behalf of users.”

\n\n\n\n

Brown suggested WordPress detect calls to the cached embeds and warn users before the calls have the chance to fail so they can consider enabling a plugin to keep their embeds working more reliably.

\n\n\n\n

The discussion remains open in the make.wordpress.org/core post and the corresponding trac ticket. Spurlock said WordPress will likely remove Facebook and Instagram oEmbed providers in the upcoming 5.6 release (scheduled for December 8) but it could also be shipped in a 5.x minor release that happens after October 24.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 23 Sep 2020 04:28:56 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:47;a:6:{s:4:\"data\";s:13:\"\n \n \n\n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:65:\"WPTavern: Gutenberg Hub Launches Landing Page Templates Directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105009\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:175:\"https://wptavern.com/gutenberg-hub-launches-landing-page-templates-directory?utm_source=rss&utm_medium=rss&utm_campaign=gutenberg-hub-launches-landing-page-templates-directory\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7657:\"\n\n\n\n

Munir Kamal has created copy-and-paste blocks. He has built sections or “patterns” from those blocks. He has created a plugin that allows users to completely customize the two features via block options. Yesterday, he released an initial offering of 22 landing page templates that build upon his earlier work.

\n\n\n\n

Gutenberg Hub can almost be called his magnum opus, at least at this stage of his career. It is a continually growing library of free tools for WordPress’s block editor.

\n\n\n\n

Like previous projects, Gutenberg Hub’s landing templates require the EditorPlus plugin. This plugin is essentially a suite of design controls for the core WordPress blocks. The templates make use of these options by default. Given the limitations of the block editor’s current design controls, the use of such a plugin is necessary. Otherwise, there would be few other ways to realistically create a template system like this.

\n\n\n\n

Currently, users must copy the block code — via a convenient “copy” button — from the Gutenberg Hub website and then paste it in the editor. It is not an ideal situation, and I have been asking Kamal whether he would consider building a template inserter for months now.

\n\n\n\n

This time around, he preemptively said, “And, by the way, I am already working on adding a Template Inserter in my EditorPlus plugin. That will allow users to browse and insert these templates directly from Gutenberg without leaving the website.”

\n\n\n\n

He knew the question was coming. No need for me to ask again. He was unable to share a current screenshot of what the inserter looks like, but he is asking for feedback on what people expect of the user experience and interface.

\n\n\n\n

“Earlier, I created a template inserter similar to other blocks plugins, but later I changed my mind and thought that I should integrate with the Gutenberg Patterns API and load the templates into the ‘patterns’ panel in the block inserter,” he said. “But, I am having a few issues and thinking about going back to the original idea to have a Templates button on the top toolbar that opens a popup window to browse and filter templates that users can insert on a click.”

\n\n\n\n

For now, it is still early. However, at least it is on the long-term roadmap and being worked on.

\n\n\n\n

The Landing Page Templates

\n\n\n\nTesting the photography template (with minor adjustments).\n\n\n\n

At the moment, Gutenberg Hub offers 22 landing page templates. The “page” terminology may not mean “full page.” It simply depends on the active theme. Some themes have an open-canvas type of template that allows users to create the entire page via the editor. However, that is not a common feature, so these page templates will be confined to the post content area in most cases.

\n\n\n\n

The templates also work better with themes that have at least a full-width or no-sidebar option. End-users will want a lot of breathing room to use the templates and tinker with their designs.

\n\n\n\n

Kamal has built templates that stretch across a variety of industries. From restaurants to gyms to education to fashion, there is a lot to choose from right now. He promises more are on the way and at least a 23rd template in the next few days.

\n\n\n\n

“For the niches, I did some research from the top WordPress and HTML marketplaces and found the following most common or popular niches,” he said. “I think I will stick with these niches unless I get some more recommendations.”

\n\n\n\n

In comparison, Redux Templates offers access to over 1,000 sections and templates. Of course, there are trade-offs, such as some of those being commercial and the plugin typically requiring other third-party plugins. While quantity is not the only thing to look at, it proves there are miles of landscape that Gutenberg Hub’s templates have not yet explored. But, it is merely the beginning.

\n\n\n\n

Gutenberg Hub’s full-page templates are not quite as plug-and-play as its blocks and section templates. This is not so much a fault from the developer’s end. It is an issue of the platform, which is constantly being updated, and the range of support from current themes. End-users will start seeing some of the current limitations of the system when a layout does not quite look right with one theme but does with another. Or, if their theme has not been updated to support a new feature, such as the Social Links block, the typical horizontal menu design will likely be a normal vertical list of links instead.

\n\n\n\n

These are not insurmountable issues. Gutenberg and themes need more time to mature before projects like Gutenberg Hub’s landing templates are perfect or at least as close to perfect as can be expected.

\n\n\n\n

There are some things that Gutenberg Hub could improve with its templates. With several that I tested, I needed to switch specific blocks to be full width. This should be set up as the default with templates that are clearly meant to be full width in the example screenshots available on the site. It is a minor issue, but correcting this in the editor fixed several layout issues I was having when using the templates.

\n\n\n\n

Monetization Plans

\n\n\n\n

The second question that Kamal has not been prepared to answer fully over the past several months is how he will monetize Gutenberg Hub. Eventually, developers need some return on their investment when building tons of free tools. Many would do it all for free as long as their bills somehow got paid, but the reality is that there will come a tipping point where their projects need funding for long-haul maintenance.

\n\n\n\n

Kamal said he has laid the groundwork for funding but has not finalized anything yet. Currently, he is working on three ideas:

\n\n\n\n
  • Creating a pro version of his EditorPlus plugin.
  • Offering premium templates and blocks but is looking for a talented designer to work with.
  • Using ads specific to Gutenberg users, but he is not a fan of going this route or ads in general.
\n\n\n\n

He is open to feedback on how to best monetize the website and its projects. However, he said he is unwilling to compromise on giving away current and future free templates and tools.

\n\n\n\n

Future Gutenberg Projects

\n\n\n\n

Kamal said he does not have any new Gutenberg-related projects in the pipeline. The current plan is to work on what he has already created, which is a large ecosystem of Gutenberg tools that somehow work together.

\n\n\n\n

Outside of blocks, templates, and plugins, he is beginning to write more free tutorials on the Gutenberg Hub blog and focusing on creating videos around the project, including a new tutorial series for beginners.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 22 Sep 2020 21:05:19 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:48;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"WPTavern: WordPress Mobile Engineers Propose Dual Licensing Gutenberg under GPL v2.0 and MPL v2.0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105025\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:239:\"https://wptavern.com/wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-mobile-engineers-propose-dual-licensing-gutenberg-under-gpl-v2-0-and-mpl-v2-0\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6556:\"

During a Q&A session at WordCamp Europe 2020 online, Matt Mullenweg mentioned that Gutenberg contributors were considering dual licensing for embedding Gutenberg in mobile apps, along with the requirement that they would need to get an agreement from all contributors. WordPress mobile engineer Maxime Biais has just published a proposal for discussion, recommending dual licensing the editor under GPL v2.0 and MPL v2.0.

\n\n\n\n

“The GPL v2.0 license is a blocker for distributing the Gutenberg library in proprietary mobile apps,” Biais said in the corresponding GitHub issue. “Currently the only known users of Gutenberg on mobile are the WordPress mobile apps which are under GPL v2.0 (WordPress for AndroidWordPress for iOS). Mobile apps under the GPL v2.0 are not common and this limits Gutenberg usage in many apps.

\n\n\n\n

“Rich text editor libraries in the mobile space are lacking. There is no well known open source rich text editor for Android or iOS. We believe that Gutenberg could be a key library for many mobile apps, but that will never happen with the GPL v2.”

\n\n\n\n

Mobile app developers are limited by the GPL, because it requires the entire app to be distributed under the same license. The team is proposing dual licensing under MPL v2.0, a weaker copyleft license that is often considered to be more “business-friendly.” It allows users to combine the software with proprietary code. MPL v2.0 requires the source code for any changes to be available under the MPL, ensuring improvements are shared back to the community. The rest of the app can be distributed under any terms with the MPL v2.0 code included as part of a “larger work.”

\n\n\n\n

“The idea here is to keep some of the WordPress-specific modules under the GPL v2.0 only; some of them are not needed and not relevant for using Gutenberg in another software. Ideally, there would be a different way of bundling the project for being used in WordPress or in a non-GPL software,” Biais said.

\n\n\n\n

The GitHub ticket has several comments from developers who hope to be able to use the editor in their own projects. Radek Pietruszewski, tech lead for a collaborative todo app called Nozbe Teams, has been requesting a relicensing of Gutenberg since October 2019.

\n\n\n\n

“Our tech stack is essentially React on web and React Native on iOS and Android,” Pietruszewski said. “We’re a tiny company, and so we share >80% of app’s codebase between these 3 platforms.

\n\n\n\n

“Our app sorely lacks a WYSIWYG editor. We had a working implementation on web, but we decided to scrap it, because there was no way to port it on iOS and Android. There are pretty much no viable rich text editors for iOS or Android, yet alone both. But even then, shipping three completely separate, but somehow compatible editors would be a vast amount of work.”

\n\n\n\n

When Peitruszewski originally made his case to the mobile team, he identified Gutenberg/Aztec as a basic infrastructure that has the potential to enable many different apps:

\n\n\n\n

And that infrastructure is sorely lacking. There are very few rich text editor libraries on both iOS and Android — and most of them suck. And if you want an editor that has a shared API for both platforms… you’re stuck. There are no options – Gutenberg is the only game in town (and it’s really good).

And it’s very hard to create this infrastructure. WYSIWYG editors are very hard, and it takes entire teams years to develop them (and they still usually suck). Almost no-one has the resources to develop it just for themselves, and if they do, they’re unwilling to open-source it.

\n\n\n\n

Automattic’s mobile app engineers have struggled to get regular contributions to the apps, despite them being open source. Dual licensing Gutenberg could open up a new world of contributors with the editor being used more widely across the industry.

\n\n\n\n

“While we might not be big enough to be able to tackle a challenge of developing a rich text editor from scratch, we’re big enough to contribute features and bug fixes to open source projects,” Pietruszewski said.

\n\n\n\n

Matt Mullenweg was the first comment on Biais’ post in favor of the change:

\n\n\n\n

I think Gutenberg has a chance to become a cross-CMS standard, giving users a familiar interface any place they currently have a rich text box. There are hundreds and hundreds of engineers at other companies solving similar problems in a proprietary way, it would be amazing to get them working together but a huge barrier now is supporting Gutenberg in mobile apps, which every modern web service or CMS has. (Hypothetically, think of Mailchimp as a possible consumer and collaborator here, but it could be any company, SaaS, or other open source CMS.)

\n\n\n\n

Unless any major blockers come up in further discussion, this dual licensing change appears to be on track to move forward. Biais noted that a similar license change has already happened on Aztec-Android and Aztec-iOS. The last hurdle is gaining the approval of all the original code contributors or rewriting the code for those who decline to give approval.

\n\n\n\n

Once Gutenberg can be used under the MPL v2.0, the editor will gain a broader reach, with people already on deck wanting to use it. Other companies and projects that are normally outside WordPress’ open source orbit will also have the opportunity to enrich Gutenberg’s ecosystem with contributions back to the project. At the same time, the MPL 2.0 protects Gutenberg from companies that would try to re-release the code as a closed-source project.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 22:59:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:49;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:124:\"WPTavern: GitHub to Use ‘Main’ Instead of ‘Master’ as the Default Branch on All New Repositories Starting Next Month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=105014\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:269:\"https://wptavern.com/github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month?utm_source=rss&utm_medium=rss&utm_campaign=github-to-use-main-instead-of-master-as-the-default-branch-on-all-new-repositories-starting-next-month\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4844:\"

In August, GitHub announced that it would change the “master” branch name for all new repositories created on the platform to “main” starting October 1. The date is less than two weeks away, and WordPress developers need to be prepared for the change if they use the service for version control or project management.

\n\n\n\n

The larger tech and web development community began conversations through various venues in June, a time in which the Black Lives Matter was gaining more traction in the U.S. and worldwide. The discussion centered on removing any terminology that could be discriminatory or oppressive to specific groups of people. This ongoing discussion has shown that there is a deep division over whether such changes are necessary or even helpful.

\n\n\n\n

The WordPress community is dealing with this division itself. Aaron Jorbin proposed a change at the same time to rename the default branch name on WordPress-owned repositories. Through discussion on his post and elsewhere, the community landed on “trunk,” which keeps WordPress projects in line with its SVN roots.

\n\n\n\n

“To close the circle on this, a decision was made in June and earlier today (August 19),” wrote Helen Hou-Sandí, a lead WordPress developer, in the comments of the original proposal. “I updated the default branch name for new GitHub repositories under the WordPress organization to be trunk after GitHub enabled early access to that feature.”

\n\n\n\n

As evidenced by the comments on the Tavern’s coverage of the proposal and those on the original post, the WordPress development community as a whole did not support this decision.

\n\n\n\n

Jorbin has updated several of WordPress’s repositories and switched them to use trunk instead of master. However, there are still some lingering projects yet to be updated, including the primary WordPress and WordPress Develop repositories. He left a comment with an updated list in June. There is no public word on whether the existing, leftover projects will be changed.

\n\n\n\n

WordPress Developer Preparations

\n\n\n\nCustomizing the default branch for a user’s GitHub repositories.\n\n\n\n

GitHub is merely changing the default branch name for new repositories starting on October 1. This change does not affect existing repositories. Individual users, organization owners, and enterprise administrators can customize the default branch via their account settings now before the switch is made. Owners can also change the default branch name for individual repositories.

\n\n\n\n

The biggest thing that developers need to watch out for is their tooling or other integrations that might still require the master branch. There may be cases where an alternative default branch name will break workflows. If planning to use a different branch name, the best thing to do right now is to spin up the tools you use on a test repository. If something breaks, check to see whether the particular tool you are using will be getting an update. In most cases, this should not be a problem because customized default branch names will be an industry standard.

\n\n\n\n

The great thing about how GitHub is rolling out this feature is that it offers a choice. Those who believe that “master” is oppressive can change the branch name to something they feel is more inclusive. For those who believe otherwise, they can keep their master branch. But, everyone can use the branch name they prefer.

\n\n\n\n

For existing repositories, GitHub is asking that developers be patient for now. The company is investing in tools to make this a seamless experience later this year. There are a few technical hurdles to clear first.

\n\n\n\n

Developers should read the full GitHub guide on setting the default branch for more information.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 21 Sep 2020 20:39:55 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Justin Tadlock\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:8:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Thu, 22 Oct 2020 16:31:31 GMT\";s:12:\"content-type\";s:8:\"text/xml\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:13:\"last-modified\";s:29:\"Thu, 22 Oct 2020 16:15:08 GMT\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:4:\"x-nc\";s:9:\"HIT ord 2\";s:16:\"content-encoding\";s:4:\"gzip\";}}s:5:\"build\";s:14:\"20200501142607\";}','no'),(133,'_transient_timeout_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603427491','no'),(134,'_transient_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1603384291','no'),(135,'_transient_timeout_dash_v2_88ae138922fe95674369b1cb3d215a2b','1603427491','no'),(136,'_transient_dash_v2_88ae138922fe95674369b1cb3d215a2b','','no'),(139,'theme_mods_twentytwenty','a:1:{s:18:\"custom_css_post_id\";i:-1;}','yes'),(140,'recently_activated','a:0:{}','yes'); /*!40000 ALTER TABLE `wp55_options` ENABLE KEYS */; UNLOCK TABLES; @@ -249,7 +251,7 @@ CREATE TABLE `wp55_posts` ( LOCK TABLES `wp55_posts` WRITE; /*!40000 ALTER TABLE `wp55_posts` DISABLE KEYS */; -INSERT INTO `wp55_posts` VALUES (1,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

\n','Hello world!','','publish','open','open','','hello-world','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost:9999/?p=1',0,'post','',1),(2,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:

\n\n\n\n

Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)

\n\n\n\n

...or something like this:

\n\n\n\n

The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.

\n\n\n\n

As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!

\n','Sample Page','','publish','closed','open','','sample-page','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost:9999/?page_id=2',0,'page','',0),(3,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','

Who we are

Our website address is: http://localhost:9999.

What personal data we collect and why we collect it

Comments

When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.

An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.

Media

If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.

Contact forms

Cookies

If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.

If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.

When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.

If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.

Embedded content from other websites

Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.

These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.

Analytics

Who we share your data with

How long we retain your data

If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.

For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.

What rights you have over your data

If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.

Where we send your data

Visitor comments may be checked through an automated spam detection service.

Your contact information

Additional information

How we protect your data

What data breach procedures we have in place

What third parties we receive data from

What automated decision making and/or profiling we do with user data

Industry regulatory disclosure requirements

','Privacy Policy','','draft','closed','open','','privacy-policy','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost:9999/?page_id=3',0,'page','',0),(4,1,'2020-10-22 16:31:28','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2020-10-22 16:31:28','0000-00-00 00:00:00','',0,'http://localhost:9999/?p=4',0,'post','',0),(5,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','publish','open','open','','simple_view','','','2020-10-22 16:32:47','2020-10-22 16:32:47','',0,'http://localhost:9999/?p=5',0,'post','',0),(6,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','inherit','closed','closed','','5-revision-v1','','','2020-10-22 16:32:26','2020-10-22 16:32:26','',5,'http://localhost:9999/5-revision-v1',0,'revision','',0); +INSERT INTO `wp55_posts` VALUES (1,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

\n','Hello world!','','publish','open','open','','hello-world','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost/?p=1',0,'post','',1),(2,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','\n

This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:

\n\n\n\n

Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)

\n\n\n\n

...or something like this:

\n\n\n\n

The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.

\n\n\n\n

As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!

\n','Sample Page','','publish','closed','open','','sample-page','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost/?page_id=2',0,'page','',0),(3,1,'2020-10-22 16:31:15','2020-10-22 16:31:15','

Who we are

Our website address is: http://localhost.

What personal data we collect and why we collect it

Comments

When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.

An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.

Media

If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.

Contact forms

Cookies

If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.

If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.

When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.

If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.

Embedded content from other websites

Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.

These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.

Analytics

Who we share your data with

How long we retain your data

If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.

For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.

What rights you have over your data

If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.

Where we send your data

Visitor comments may be checked through an automated spam detection service.

Your contact information

Additional information

How we protect your data

What data breach procedures we have in place

What third parties we receive data from

What automated decision making and/or profiling we do with user data

Industry regulatory disclosure requirements

','Privacy Policy','','draft','closed','open','','privacy-policy','','','2020-10-22 16:31:15','2020-10-22 16:31:15','',0,'http://localhost/?page_id=3',0,'page','',0),(4,1,'2020-10-22 16:31:28','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2020-10-22 16:31:28','0000-00-00 00:00:00','',0,'http://localhost/?p=4',0,'post','',0),(5,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','publish','open','open','','simple_view','','','2020-10-22 16:32:47','2020-10-22 16:32:47','',0,'http://localhost/?p=5',0,'post','',0),(6,1,'2020-10-22 16:32:26','2020-10-22 16:32:26','\n

Some content

\n','Simple View','','inherit','closed','closed','','5-revision-v1','','','2020-10-22 16:32:26','2020-10-22 16:32:26','',5,'http://localhost/5-revision-v1',0,'revision','',0); /*!40000 ALTER TABLE `wp55_posts` ENABLE KEYS */; UNLOCK TABLES; @@ -423,7 +425,7 @@ CREATE TABLE `wp55_users` ( LOCK TABLES `wp55_users` WRITE; /*!40000 ALTER TABLE `wp55_users` DISABLE KEYS */; -INSERT INTO `wp55_users` VALUES (1,'test','$P$BDzpK1XXL9P2cYWggPMUbN87GQSiI80','test','test@gmail.com','http://localhost:9999','2020-10-22 16:31:15','',0,'test'); +INSERT INTO `wp55_users` VALUES (1,'test','$P$BDzpK1XXL9P2cYWggPMUbN87GQSiI80','test','test@gmail.com','http://localhost','2020-10-22 16:31:15','',0,'test'); /*!40000 ALTER TABLE `wp55_users` ENABLE KEYS */; UNLOCK TABLES; @@ -555,7 +557,7 @@ CREATE TABLE `wp_options` ( LOCK TABLES `wp_options` WRITE; /*!40000 ALTER TABLE `wp_options` DISABLE KEYS */; -INSERT INTO `wp_options` VALUES (1,'siteurl','http://localhost:9999','yes'),(2,'home','http://localhost:9999','yes'),(3,'blogname','Datadog Test WP DB','yes'),(4,'blogdescription','','yes'),(5,'users_can_register','0','yes'),(6,'admin_email','test@gmail.com','yes'),(7,'start_of_week','1','yes'),(8,'use_balanceTags','0','yes'),(9,'use_smilies','1','yes'),(10,'require_name_email','1','yes'),(11,'comments_notify','1','yes'),(12,'posts_per_rss','10','yes'),(13,'rss_use_excerpt','0','yes'),(14,'mailserver_url','mail.example.com','yes'),(15,'mailserver_login','login@example.com','yes'),(16,'mailserver_pass','password','yes'),(17,'mailserver_port','110','yes'),(18,'default_category','1','yes'),(19,'default_comment_status','open','yes'),(20,'default_ping_status','open','yes'),(21,'default_pingback_flag','0','yes'),(22,'posts_per_page','10','yes'),(23,'date_format','F j, Y','yes'),(24,'time_format','g:i a','yes'),(25,'links_updated_date_format','F j, Y g:i a','yes'),(26,'comment_moderation','0','yes'),(27,'moderation_notify','1','yes'),(28,'permalink_structure','/%postname%','yes'),(29,'rewrite_rules','a:93:{s:11:\"^wp-json/?$\";s:22:\"index.php?rest_route=/\";s:14:\"^wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:21:\"^index.php/wp-json/?$\";s:22:\"index.php?rest_route=/\";s:24:\"^index.php/wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:17:\"^wp-sitemap\\\\.xml$\";s:23:\"index.php?sitemap=index\";s:17:\"^wp-sitemap\\\\.xsl$\";s:36:\"index.php?sitemap-stylesheet=sitemap\";s:23:\"^wp-sitemap-index\\\\.xsl$\";s:34:\"index.php?sitemap-stylesheet=index\";s:48:\"^wp-sitemap-([a-z]+?)-([a-z\\\\d_-]+?)-(\\\\d+?)\\\\.xml$\";s:75:\"index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]\";s:34:\"^wp-sitemap-([a-z]+?)-(\\\\d+?)\\\\.xml$\";s:47:\"index.php?sitemap=$matches[1]&paged=$matches[2]\";s:47:\"category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:42:\"category/(.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:23:\"category/(.+?)/embed/?$\";s:46:\"index.php?category_name=$matches[1]&embed=true\";s:35:\"category/(.+?)/page/?([0-9]{1,})/?$\";s:53:\"index.php?category_name=$matches[1]&paged=$matches[2]\";s:17:\"category/(.+?)/?$\";s:35:\"index.php?category_name=$matches[1]\";s:44:\"tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:39:\"tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:20:\"tag/([^/]+)/embed/?$\";s:36:\"index.php?tag=$matches[1]&embed=true\";s:32:\"tag/([^/]+)/page/?([0-9]{1,})/?$\";s:43:\"index.php?tag=$matches[1]&paged=$matches[2]\";s:14:\"tag/([^/]+)/?$\";s:25:\"index.php?tag=$matches[1]\";s:45:\"type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:40:\"type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:21:\"type/([^/]+)/embed/?$\";s:44:\"index.php?post_format=$matches[1]&embed=true\";s:33:\"type/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?post_format=$matches[1]&paged=$matches[2]\";s:15:\"type/([^/]+)/?$\";s:33:\"index.php?post_format=$matches[1]\";s:12:\"robots\\\\.txt$\";s:18:\"index.php?robots=1\";s:13:\"favicon\\\\.ico$\";s:19:\"index.php?favicon=1\";s:48:\".*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\\\\.php$\";s:18:\"index.php?feed=old\";s:20:\".*wp-app\\\\.php(/.*)?$\";s:19:\"index.php?error=403\";s:18:\".*wp-register.php$\";s:23:\"index.php?register=true\";s:32:\"feed/(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:27:\"(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:8:\"embed/?$\";s:21:\"index.php?&embed=true\";s:20:\"page/?([0-9]{1,})/?$\";s:28:\"index.php?&paged=$matches[1]\";s:41:\"comments/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:36:\"comments/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:17:\"comments/embed/?$\";s:21:\"index.php?&embed=true\";s:44:\"search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:39:\"search/(.+)/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:20:\"search/(.+)/embed/?$\";s:34:\"index.php?s=$matches[1]&embed=true\";s:32:\"search/(.+)/page/?([0-9]{1,})/?$\";s:41:\"index.php?s=$matches[1]&paged=$matches[2]\";s:14:\"search/(.+)/?$\";s:23:\"index.php?s=$matches[1]\";s:47:\"author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:42:\"author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:23:\"author/([^/]+)/embed/?$\";s:44:\"index.php?author_name=$matches[1]&embed=true\";s:35:\"author/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?author_name=$matches[1]&paged=$matches[2]\";s:17:\"author/([^/]+)/?$\";s:33:\"index.php?author_name=$matches[1]\";s:69:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:64:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:45:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$\";s:74:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true\";s:57:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:81:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]\";s:39:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$\";s:63:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]\";s:56:\"([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:51:\"([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:32:\"([0-9]{4})/([0-9]{1,2})/embed/?$\";s:58:\"index.php?year=$matches[1]&monthnum=$matches[2]&embed=true\";s:44:\"([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:65:\"index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]\";s:26:\"([0-9]{4})/([0-9]{1,2})/?$\";s:47:\"index.php?year=$matches[1]&monthnum=$matches[2]\";s:43:\"([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:38:\\([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:19:\"([0-9]{4})/embed/?$\";s:37:\"index.php?year=$matches[1]&embed=true\";s:31:\"([0-9]{4})/page/?([0-9]{1,})/?$\";s:44:\"index.php?year=$matches[1]&paged=$matches[2]\";s:13:\"([0-9]{4})/?$\";s:26:\"index.php?year=$matches[1]\";s:27:\".?.+?/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\".?.+?/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\\.?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\".?.+?/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"(.?.+?)/embed/?$\";s:41:\"index.php?pagename=$matches[1]&embed=true\";s:20:\"(.?.+?)/trackback/?$\";s:35:\"index.php?pagename=$matches[1]&tb=1\";s:40:\"(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:35:\"(.?.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:28:\"(.?.+?)/page/?([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&paged=$matches[2]\";s:35:\"(.?.+?)/comment-page-([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&cpage=$matches[2]\";s:24:\"(.?.+?)(?:/([0-9]+))?/?$\";s:47:\"index.php?pagename=$matches[1]&page=$matches[2]\";s:27:\"[^/]+/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\"[^/]+/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\"[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\"[^/]+/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"([^/]+)/embed/?$\";s:37:\"index.php?name=$matches[1]&embed=true\";s:20:\"([^/]+)/trackback/?$\";s:31:\"index.php?name=$matches[1]&tb=1\";s:40:\"([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:35:\"([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:28:\"([^/]+)/page/?([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&paged=$matches[2]\";s:35:\"([^/]+)/comment-page-([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&cpage=$matches[2]\";s:24:\"([^/]+)(?:/([0-9]+))?/?$\";s:43:\"index.php?name=$matches[1]&page=$matches[2]\";s:16:\"[^/]+/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:26:\"[^/]+/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:46:\"[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:22:\"[^/]+/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";}','yes'),(30,'hack_file','0','yes'),(31,'blog_charset','UTF-8','yes'),(32,'moderation_keys','','no'),(33,'active_plugins','a:1:{i:0;s:19:\"datadog/datadog.php\";}','yes'),(34,'category_base','','yes'),(35,'ping_sites','http://rpc.pingomatic.com/','yes'),(36,'comment_max_links','2','yes'),(37,'gmt_offset','0','yes'),(38,'default_email_category','1','yes'),(39,'recently_edited','','no'),(40,'template','twentytwentythree','yes'),(41,'stylesheet','twentytwentythree','yes'),(42,'comment_registration','0','yes'),(43,'html_type','text/html','yes'),(44,'use_trackback','0','yes'),(45,'default_role','subscriber','yes'),(46,'db_version','53496','yes'),(47,'uploads_use_yearmonth_folders','1','yes'),(48,'upload_path','','yes'),(49,'blog_public','0','yes'),(50,'default_link_category','2','yes'),(51,'show_on_front','posts','yes'),(52,'tag_base','','yes'),(53,'show_avatars','1','yes'),(54,'avatar_rating','G','yes'),(55,'upload_url_path','','yes'),(56,'thumbnail_size_w','150','yes'),(57,'thumbnail_size_h','150','yes'),(58,'thumbnail_crop','1','yes'),(59,'medium_size_w','300','yes'),(60,'medium_size_h','300','yes'),(61,'avatar_default','mystery','yes'),(62,'large_size_w','1024','yes'),(63,'large_size_h','1024','yes'),(64,'image_default_link_type','none','yes'),(65,'image_default_size','','yes'),(66,'image_default_align','','yes'),(67,'close_comments_for_old_posts','0','yes'),(68,'close_comments_days_old','14','yes'),(69,'thread_comments','1','yes'),(70,'thread_comments_depth','5','yes'),(71,'page_comments','0','yes'),(72,'comments_per_page','50','yes'),(73,'default_comments_page','newest','yes'),(74,'comment_order','asc','yes'),(75,'sticky_posts','a:0:{}','yes'),(76,'widget_categories','a:0:{}','yes'),(77,'widget_text','a:0:{}','yes'),(78,'widget_rss','a:0:{}','yes'),(79,'uninstall_plugins','a:0:{}','no'),(80,'timezone_string','','yes'),(81,'page_for_posts','0','yes'),(82,'page_on_front','0','yes'),(83,'default_post_format','0','yes'),(84,'link_manager_enabled','0','yes'),(85,'finished_splitting_shared_terms','1','yes'),(86,'site_icon','0','yes'),(87,'medium_large_size_w','768','yes'),(88,'medium_large_size_h','0','yes'),(89,'wp_page_for_privacy_policy','3','yes'),(90,'show_comments_cookies_opt_in','1','yes'),(91,'admin_email_lifespan','1690204371','yes'),(92,'disallowed_keys','','no'),(93,'comment_previously_approved','1','yes'),(94,'auto_plugin_theme_update_emails','a:0:{}','no'),(95,'auto_update_core_dev','enabled','yes'),(96,'auto_update_core_minor','enabled','yes'),(97,'auto_update_core_major','enabled','yes'),(98,'wp_force_deactivated_plugins','a:0:{}','yes'),(99,'initial_db_version','53496','yes'),(100,'wp_user_roles','a:5:{s:13:\"administrator\";a:2:{s:4:\"name\";s:13:\"Administrator\";s:12:\"capabilities\";a:61:{s:13:\"switch_themes\";b:1;s:11:\"edit_themes\";b:1;s:16:\"activate_plugins\";b:1;s:12:\"edit_plugins\";b:1;s:10:\"edit_users\";b:1;s:10:\"edit_files\";b:1;s:14:\"manage_options\";b:1;s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:6:\"import\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:8:\"level_10\";b:1;s:7:\"level_9\";b:1;s:7:\"level_8\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;s:12:\"delete_users\";b:1;s:12:\"create_users\";b:1;s:17:\"unfiltered_upload\";b:1;s:14:\"edit_dashboard\";b:1;s:14:\"update_plugins\";b:1;s:14:\"delete_plugins\";b:1;s:15:\"install_plugins\";b:1;s:13:\"update_themes\";b:1;s:14:\"install_themes\";b:1;s:11:\"update_core\";b:1;s:10:\"list_users\";b:1;s:12:\"remove_users\";b:1;s:13:\"promote_users\";b:1;s:18:\"edit_theme_options\";b:1;s:13:\"delete_themes\";b:1;s:6:\"export\";b:1;}}s:6:\"editor\";a:2:{s:4:\"name\";s:6:\"Editor\";s:12:\"capabilities\";a:34:{s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;}}s:6:\"author\";a:2:{s:4:\"name\";s:6:\"Author\";s:12:\"capabilities\";a:10:{s:12:\"upload_files\";b:1;s:10:\"edit_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;s:22:\"delete_published_posts\";b:1;}}s:11:\"contributor\";a:2:{s:4:\"name\";s:11:\"Contributor\";s:12:\"capabilities\";a:5:{s:10:\"edit_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;}}s:10:\"subscriber\";a:2:{s:4:\"name\";s:10:\"Subscriber\";s:12:\"capabilities\";a:2:{s:4:\"read\";b:1;s:7:\"level_0\";b:1;}}}','yes'),(101,'fresh_site','1','yes'),(102,'user_count','1','no'),(103,'widget_block','a:6:{i:2;a:1:{s:7:\"content\";s:19:\"\";}i:3;a:1:{s:7:\"content\";s:154:\"

Recent Posts

\";}i:4;a:1:{s:7:\"content\";s:227:\"

Recent Comments

\";}i:5;a:1:{s:7:\"content\";s:146:\"

Archives

\";}i:6;a:1:{s:7:\"content\";s:150:\"

Categories

\";}s:12:\"_multiwidget\";i:1;}','yes'),(104,'sidebars_widgets','a:4:{s:19:\"wp_inactive_widgets\";a:0:{}s:9:\"sidebar-1\";a:3:{i:0;s:7:\"block-2\";i:1;s:7:\"block-3\";i:2;s:7:\"block-4\";}s:9:\"sidebar-2\";a:2:{i:0;s:7:\"block-5\";i:1;s:7:\"block-6\";}s:13:\"array_version\";i:3;}','yes'),(105,'cron','a:7:{i:1674663182;a:1:{s:34:\"wp_privacy_delete_old_export_files\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"hourly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:3600;}}}i:1674695582;a:4:{s:18:\"wp_https_detection\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_version_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:17:\"wp_update_plugins\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_update_themes\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674695658;a:1:{s:21:\"wp_update_user_counts\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674738782;a:2:{s:30:\"wp_site_health_scheduled_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"weekly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:604800;}}s:32:\"recovery_mode_clean_expired_keys\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738858;a:2:{s:19:\"wp_scheduled_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:25:\"delete_expired_transients\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738859;a:1:{s:30:\"wp_scheduled_auto_draft_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}s:7:\"version\";i:2;}','yes'),(106,'widget_pages','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(107,'widget_calendar','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(108,'widget_archives','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(109,'widget_media_audio','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(110,'widget_media_image','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(111,'widget_media_gallery','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(112,'widget_media_video','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(113,'widget_meta','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(114,'widget_search','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(115,'widget_recent-posts','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(116,'widget_recent-comments','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(117,'widget_tag_cloud','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(118,'widget_nav_menu','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(119,'widget_custom_html','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(121,'recovery_keys','a:0:{}','yes'),(122,'https_detection_errors','a:1:{s:20:\"https_request_failed\";a:1:{i:0;s:21:\"HTTPS request failed.\";}}','yes'),(123,'_site_transient_update_core','O:8:\"stdClass\":4:{s:7:\"updates\";a:1:{i:0;O:8:\"stdClass\":10:{s:8:\"response\";s:6:\"latest\";s:8:\"download\";s:59:\"https://downloads.wordpress.org/release/wordpress-6.1.1.zip\";s:6:\"locale\";s:5:\"en_US\";s:8:\"packages\";O:8:\"stdClass\":5:{s:4:\"full\";s:59:\"https://downloads.wordpress.org/release/wordpress-6.1.1.zip\";s:10:\"no_content\";s:70:\"https://downloads.wordpress.org/release/wordpress-6.1.1-no-content.zip\";s:11:\"new_bundled\";s:71:\"https://downloads.wordpress.org/release/wordpress-6.1.1-new-bundled.zip\";s:7:\"partial\";s:0:\"\";s:8:\"rollback\";s:0:\"\";}s:7:\"current\";s:5:\"6.1.1\";s:7:\"version\";s:5:\"6.1.1\";s:11:\"php_version\";s:6:\"5.6.20\";s:13:\"mysql_version\";s:3:\"5.0\";s:11:\"new_bundled\";s:3:\"6.1\";s:15:\"partial_version\";s:0:\"\";}}s:12:\"last_checked\";i:1674652480;s:15:\"version_checked\";s:5:\"6.1.1\";s:12:\"translations\";a:0:{}}','no'),(126,'_site_transient_timeout_theme_roots','1674654196','no'),(127,'_site_transient_theme_roots','a:3:{s:15:\"twentytwentyone\";s:7:\"/themes\";s:17:\"twentytwentythree\";s:7:\"/themes\";s:15:\"twentytwentytwo\";s:7:\"/themes\";}','no'),(128,'_site_transient_update_themes','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1674652481;s:7:\"checked\";a:3:{s:15:\"twentytwentyone\";s:3:\"1.7\";s:17:\"twentytwentythree\";s:3:\"1.0\";s:15:\"twentytwentytwo\";s:3:\"1.3\";}s:8:\"response\";a:0:{}s:9:\"no_update\";a:3:{s:15:\"twentytwentyone\";a:6:{s:5:\"theme\";s:15:\"twentytwentyone\";s:11:\"new_version\";s:3:\"1.7\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentytwentyone/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentytwentyone.1.7.zip\";s:8:\"requires\";s:3:\"5.3\";s:12:\"requires_php\";s:3:\"5.6\";}s:17:\"twentytwentythree\";a:6:{s:5:\"theme\";s:17:\"twentytwentythree\";s:11:\"new_version\";s:3:\"1.0\";s:3:\"url\";s:47:\"https://wordpress.org/themes/twentytwentythree/\";s:7:\"package\";s:63:\"https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip\";s:8:\"requires\";s:3:\"6.1\";s:12:\"requires_php\";s:3:\"5.6\";}s:15:\"twentytwentytwo\";a:6:{s:5:\"theme\";s:15:\"twentytwentytwo\";s:11:\"new_version\";s:3:\"1.3\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentytwentytwo/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentytwentytwo.1.3.zip\";s:8:\"requires\";s:3:\"5.9\";s:12:\"requires_php\";s:3:\"5.6\";}}s:12:\"translations\";a:0:{}}','no'),(130,'_site_transient_timeout_browser_894dc60a4e148f4652615ed246d3e298','1675257258','no'),(131,'_site_transient_browser_894dc60a4e148f4652615ed246d3e298','a:10:{s:4:\"name\";s:6:\"Chrome\";s:7:\"version\";s:9:\"109.0.0.0\";s:8:\"platform\";s:9:\"Macintosh\";s:10:\"update_url\";s:29:\"https://www.google.com/chrome\";s:7:\"img_src\";s:43:\"http://s.w.org/images/browsers/chrome.png?1\";s:11:\"img_src_ssl\";s:44:\"https://s.w.org/images/browsers/chrome.png?1\";s:15:\"current_version\";s:2:\"18\";s:7:\"upgrade\";b:0;s:8:\"insecure\";b:0;s:6:\"mobile\";b:0;}','no'),(132,'_site_transient_timeout_php_check_ce267f3653936506950ae9448202043a','1675257259','no'),(133,'_site_transient_php_check_ce267f3653936506950ae9448202043a','a:5:{s:19:\"recommended_version\";s:3:\"7.4\";s:15:\"minimum_version\";s:6:\"5.6.20\";s:12:\"is_supported\";b:1;s:9:\"is_secure\";b:1;s:13:\"is_acceptable\";b:1;}','no'),(135,'_site_transient_timeout_community-events-1de8873aa0984c1dbee47981d08b0def','1674695662','no'),(136,'_site_transient_community-events-1de8873aa0984c1dbee47981d08b0def','a:4:{s:9:\"sandboxed\";b:0;s:5:\"error\";N;s:8:\"location\";a:1:{s:2:\"ip\";s:10:\"172.21.0.0\";}s:6:\"events\";a:1:{i:0;a:10:{s:4:\"type\";s:8:\"wordcamp\";s:5:\"title\";s:15:\"WordCamp Torino\";s:3:\"url\";s:33:\"https://torino.wordcamp.org/2023/\";s:6:\"meetup\";N;s:10:\"meetup_url\";N;s:4:\"date\";s:19:\"2023-04-14 00:00:00\";s:8:\"end_date\";s:19:\"2023-04-15 00:00:00\";s:20:\"start_unix_timestamp\";i:1681423200;s:18:\"end_unix_timestamp\";i:1681509600;s:8:\"location\";a:4:{s:8:\"location\";s:12:\"Turin, Italy\";s:7:\"country\";s:2:\"IT\";s:8:\"latitude\";d:45.050238;s:9:\"longitude\";d:7.669286;}}}}','no'),(137,'_transient_timeout_feed_9bbd59226dc36b9b26cd43f15694c5c3','1674695664','no'),(138,'_transient_feed_9bbd59226dc36b9b26cd43f15694c5c3','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:52:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:8:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"The latest news about WordPress and the WordPress community\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:13:\"lastBuildDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 11:56:32 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"en-US\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"generator\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"https://wordpress.org/?v=6.2-alpha-55136\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:5:\"image\";a:1:{i:0;a:6:{s:4:\"data\";s:11:\"\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:3:\"url\";a:1:{i:0;a:5:{s:4:\"data\";s:29:\"https://s.w.org/favicon.ico?2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:5:\"width\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"32\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:6:\"height\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"32\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}s:4:\"item\";a:10:{i:0;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"The Month in WordPress – December 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"https://wordpress.org/news/2023/01/the-month-in-wordpress-december-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:18:\"month in wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14191\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:339:\"Last month at State of the Word, WordPress Executive Director Josepha Haden Chomphosy shared some opening thoughts on “Why WordPress” and the Four Freedoms of open source. In this recent letter, she expands on her vision for the WordPress open source project as it prepares for the third phase of Gutenberg: “We are now, as […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"rmartinezduque\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:12820:\"\n

Last month at State of the Word, WordPress Executive Director Josepha Haden Chomphosy shared some opening thoughts on “Why WordPress” and the Four Freedoms of open source. In this recent letter, she expands on her vision for the WordPress open source project as it prepares for the third phase of Gutenberg:

\n\n\n\n
\n

“We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.”

\nJosepha Haden Chomphosy
\n\n\n\n

December brought with it a time for reflection—a time to look back, celebrate, and start planning new projects. Read on to find out what 2023 holds for WordPress so far.

\n\n\n\n
\n\n\n\n

WordPress is turning 20!

\n\n\n\n

2023 marks the 20th anniversary of WordPress’ launch. The project has come a long way since the first release as it continues to advance its mission to democratize publishing. From its beginnings as a blogging platform to a world-leading open source CMS powering over 40% of websites.

\n\n\n\n

Join the WordPress community in celebrating this important milestone. As the anniversary date approaches, there will be events, commemorative swag, and more.

\n\n\n\n
\n

Stay tuned for updates.

\n
\n\n\n\n
\n\n\n\n

WordPress 6.2 is scheduled for March 28, 2023

\n\n\n\n

Work on WordPress 6.2, the first major release of 2023, is already underway. It is expected to launch on March 28, 2023, and will include up to Gutenberg 15.1 for a total of 10 Gutenberg releases.

\n\n\n\n

The proposed schedule includes four Beta releases to accommodate the first WordCamp Asia and avoid having major release milestones very close to this event.

\n\n\n\n
\n

Read more about the 6.2 schedule and release team.

\n
\n\n\n\n
\n\n\n\n

What’s new in Gutenberg

\n\n\n\n

Two new versions of Gutenberg have shipped in the last month:

\n\n\n\n
    \n
  • Gutenberg 14.8 was released on December 21, 2022. This version features a reorganized Site Editor interface with a Browse Mode that facilitates navigation through templates and template parts. In addition, it includes the ability to add custom CSS via the Style panel and a Style Book that provides an overview of all block styles in a centralized location.
  • \n\n\n\n
  • Gutenberg 14.9 became available for download on January 4, 2023. It introduces a new “Push changes to Global Styles” button in the Site Editor, which allows users to apply individual block style changes to all blocks of that type across their site. Other features include typography support for the Page List block, and the ability to import sidebar widgets into a template part when transitioning from a classic theme.
  • \n
\n\n\n\n
\n

Learn how Gutenberg’s latest releases are advancing the Site Editor experience to be more intuitive and scalable.

\n
\n\n\n\n
\n\n\n\n

Team updates: WordPress big picture goals, new Incident Response Team, and more

\n\n\n\n\n\n\n\n
\n

Check out the 2022 State of the Word Q&A post, which answers submitted questions that Matt could not address at the live event.

\n
\n\n\n\n
\n\n\n\n

Feedback & testing requests

\n\n\n\n\n\n\n\n
\n

Have thoughts for improving the Five for the Future contributor experience? This post calls for ideas on how this initiative can better support the project and the people behind it.

\n
\n\n\n\n
\n\n\n\n

WordPress events updates

\n\n\n\n\n\n\n\n
\n

Would you like to be a speaker at WordCamp Europe 2023? Submit your application by the first week of February.

\n
\n\n\n\n
\n\n\n\n
\n\n\n\n

Have a story we should include in the next issue of The Month in WordPress? Fill out this quick form to let us know.

\n\n\n\n

The following folks contributed to this edition of The Month in WordPress: @cbringmann, @laurlittle, @rmartinezduque.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"WP Briefing: Episode 47: Letter from the Executive Director\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/episode-47-letter-from-the-executive-director/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:11:\"wp-briefing\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14175\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:114:\"Hear from WordPress Executive Director Josepha Haden Chomphosy on her vision for the open source project in 2023. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:60:\"https://wordpress.org/news/files/2023/01/WP-Briefing-047.mp3\";s:6:\"length\";s:1:\"0\";s:4:\"type\";s:0:\"\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8912:\"\n

On episode forty-seven of the WordPress Briefing podcast, Executive Director Josepha Haden Chomphosy shares her vision and current thinking for the WordPress open source project in 2023. Rather read it? The full letter is also available.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

Show Notes

\n\n\n\n

make.WordPress.org/core
Join the 6.2 Release!
Submit Topics for the Community Summit!

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress Briefing, the podcast where you can catch quick explanations of the ideas behind the WordPress open source project, some insight into the community that supports it, and get a small list of big things coming up in the next two weeks. I’m your host, Josepha Haden Chomphosy. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:40] 

\n\n\n\n

Last month at State of the Word, I shared some opening thoughts about why WordPress. For me, this is an easy question, and the hardest part is always knowing which lens to answer through. Though I always focus on the philosophical parts of the answer, I know that I often speak as an advocate for many types of WordPressers.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:00] 

\n\n\n\n

So as we prepare ourselves for the start of a new year, I have a few additional thoughts that I’d like to share with you, my WordPress community, to take into the year with you. 

\n\n\n\n

Firstly, the Four Freedoms. If you have already listened to State of the Word, you have heard my take on the philosophical side of open source and the freedoms it provides.

\n\n\n\n

But if you didn’t, then the TL;DR on that is that open source provides protections and freedoms to creators on the web that I really think should just be a given. But there are a couple of other things about the Four Freedoms, and especially the way that WordPress does this kind of open source-y thing that I think are worth noting as well.

\n\n\n\n

One of those things is that WordPress entrepreneurs, those who are providing services or designing sites, building applications, they have proven that open source provides an ethical framework for conducting business. No one ever said that you aren’t allowed to build a business using free and open source software, and I am regularly heartened by the way that successful companies and freelancers make the effort to pay forward what they can.

\n\n\n\n

[Josepha Haden Chomphosy 00:02:02]

\n\n\n\n

Not always for the sole benefit of WordPress, of course, but often for the general benefit of folks who are also learning how to be entrepreneurs or how to kind of navigate our ecosystem. And the other thing that I love about the Four Freedoms and the way that WordPress does it is that leaders in the WordPress community, no matter where they are leading from, have shown that open source ideals can be applied to the way we work with one another and show up for one another.

\n\n\n\n

As a community, we tend to approach solution gathering as an us-versus-the-problem exercise, which not only makes our solutions better, it also makes our community stronger. 

\n\n\n\n

As I have witnessed all of these things work together over the years, one thing that is clear to me is this: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, but open source methodologies represent a process that can change the way we approach our work and our businesses.

\n\n\n\n

[Josepha Haden Chomphosy 00:03:01] 

\n\n\n\n

The second big thing that I want to make sure you all take into the year with you is that we are preparing for the third phase of the Gutenberg project. We are putting our backend developer hats on and working on the APIs that power our workflows. That workflows phase will be complex. A little bit because APIs are dark magic that binds us together, but also because we’re going to get deep into the core of WordPress with that phase.

\n\n\n\n

If you want to have impactful work for future users of WordPress, though, this is the phase to get invested in. This phase will focus on the main elements of collaborative user workflows. If that doesn’t really make sense to you, I totally get it. Think of it this way, this phase will work on built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and things like programmable editorial, pre-launch checklists.

\n\n\n\n

[Josepha Haden Chomphosy 00:04:00] 

\n\n\n\n

So phases one and two of the Gutenberg project had a very ‘blocks everywhere’ sort of vision. And phase three and, arguably, phase four will have more of a ‘works with the way you work’ vision.

\n\n\n\n

And my final thought for you all as we head into the year is this, there are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was State of the Word 2013, where Matt dreamed on stage of a true WYSIWYG editor for WordPress. Some say it was State of the Word 2016, where we were all encouraged to learn JavaScript deeply. For a lot of us though, it was at WordCamp Europe in 2018 when the Gutenberg feature plugin first made its way to the repo.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like it’s been a long time because it has been a long time. But I can also confirm that it takes many pushes to knock over a refrigerator. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:00] 

\n\n\n\n

For early adopters, both to the creation of Gutenberg as well as its use, hyperfocus on daily tasks makes it really hard to get a concept of scale.

\n\n\n\n

And so I encourage everyone this year to look out toward the horizon a bit more and up toward our guiding stars a bit more as well. Because we are now, as we ever were, securing opportunity for those who come after us because of the opportunity that was secured for us by those who came before us. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:33] 

\n\n\n\n

That brings us now to our small list of big things. It’s a very small list, but two pretty big things. The first thing on the list is that the WordPress 6.2 release is on its way. If you would like to get started contributing there, you can wander over to make.WordPress.org/core. You can volunteer to be part of the release squad. You can volunteer your time just as a regular contributor, someone who can test things — any of that. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

We’ll put a link in the show notes. And the second thing that I wanted to remind you of is that today is the deadline to submit topics for the Community Summit that’s coming up in August. That comes up in the middle of August, like the 22nd and 23rd or something like that. 

\n\n\n\n

We’ll put a link to that in the show notes as well. If you already have chatted with a team rep about some things that you really want to make sure get discussed at the community summit, I think that we can all assume that your team rep has put that in. But if not, it never hurts to give it a second vote by putting a new submission into the form.

\n\n\n\n

And that, my friends, is your small list of big things. Thank you for tuning in today for the WordPress Briefing. I’m your host, Josepha Haden Chomphosy, and I’ll see you again in a couple of weeks.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:49:\"Letter from WordPress’ Executive Director, 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/letter-from-wordpress-executive-director-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14180\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:127:\"If Phases 1 and 2 had a \"blocks everywhere\" vision, think of Phase 3 with more of a “works with the way you work” vision. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:5903:\"\n

Last month at State of the Word, I shared some opening thoughts about “Why WordPress.” For me, this is an easy question, and the hardest part is knowing which lens to answer through. The reasons that a solopreneur will choose WordPress are different than the reasons a corporation would. And while artists and activists may have a similar vision for the world, their motivations change their reasons, too. That’s why I always focus on the philosophical parts of the answer because I know that I am speaking as an advocate for many types of WordPressers. I have a few other reasons, too, which you may not be aware of as you use our software every day.

\n\n\n\n

Why WordPress?

\n\n\n\n

Most importantly, the Four Freedoms of Open Source. If you have already listened to State of the Word, you have heard my thoughts on the philosophical side of open source and the freedoms it provides. If you didn’t, then the tl;dr on that is that open source provides protections and freedoms to creators on the web that should be a given. There’s an extent to which the idea of owning your content and data online is a radical idea. So radical, even, that it is hard for folks to grasp what we mean when we say “free as in speech, not free as in beer.” Securing an open web for the future is, I believe, a net win for the world especially when contrasted to the walled gardens and proprietary systems that pit us all against one another with the purpose of gaining more data to sell.

\n\n\n\n

A second reason is that WordPress entrepreneurs (those providing services, designing sites, and building applications) have proven that open source offers an ethical framework for conducting business. No one ever said that you cannot build a business using free and open source software. And I am regularly heartened by the way successful companies and freelancers make an effort to pay forward what they can. Not always for the sole benefit of WordPress, but often for the general benefit of folks learning how to be an entrepreneur in our ecosystem. Because despite our competitive streaks, at the end of the day, we know that ultimately we are the temporary caretakers of an ecosystem that has unlocked wealth and opportunity for people we may never meet but whose lives are made infinitely better because of us.

\n\n\n\n

And the final reason is that leaders in the WordPress community (team reps, component maintainers, and community builders) have shown that open source ideals can be applied to how we work with one another. As a community, we tend to approach solution gathering as an “us vs. the problem” exercise, which not only makes our solutions better and our community stronger. And our leaders—working as they are in a cross-cultural, globally-distributed project that guides or supports tens of thousands of people a year—have unparalleled generosity of spirit. Whether they are welcoming newcomers or putting out calls for last-minute volunteers, seeing the way that they collaborate every day gives me hope for our future.

\n\n\n\n

As I have witnessed these three things work together over the years, one thing is clear to me: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, open source methodologies represent a process that can change the way we approach our work and our businesses. 

\n\n\n\n

WordPress in 2023

\n\n\n\n

As we prepare for the third phase of the Gutenberg project, we are putting on our backend developer hats and working on the APIs that power our workflows. Releases during Phase 3 will focus on the main elements of collaborative user workflows. If that doesn’t make sense, think of built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and programmatic editorial and pre-launch checklists.

\n\n\n\n

If Phases 1 and 2 had a “blocks everywhere” vision, think of Phase 3 with more of a “works with the way you work” vision. 

\n\n\n\n

In addition to this halfway milestone of starting work on Phase 3, WordPress also hits the milestone of turning 20 years old. I keep thinking back to various milestones we’ve had (which you can read about in the second version of the Milestones book) and realized that almost my entire experience of full-time contributions to WordPress has been in the Gutenberg era.

\n\n\n\n

I hear some of you already thinking incredulous thoughts so, come with me briefly.

\n\n\n\n

There are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was at State of the Word 2013 when Matt dreamed of “a true WYSIWYG” editor for WordPress. Some say it was at State of the Word 2016 where we were encouraged to “learn Javascript deeply.” For many of us, it was at WordCamp Europe in 2017 when the Gutenberg demo first made its way on stage.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like a long time because it has been a long time. I can also confirm that it takes many pushes to knock over a refrigerator. For early adopters (both to the creation of Gutenberg and its use), hyper-focus on daily tasks makes it hard to get a concept of scale.

\n\n\n\n

So I encourage you this year to look out toward the horizon and up toward our guiding stars. We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.

\n\n\n\n

Rather listen? The abbreviated spoken letter is also available.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14180\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:43:\"WordPress is Turning 20: Let’s Celebrate!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"https://wordpress.org/news/2023/01/wordpress-is-turning-20-lets-celebrate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 21:38:49 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:6:\"Events\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:4:\"WP20\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14155\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"2023 marks the 20th year of WordPress. Read on to learn about how WordPress is celebrating this milestone.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Dan Soschin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:1476:\"\n

2023 marks the 20th year of WordPress. Where would we all be without WordPress? Just think of that! While many technologies, software stacks, and fashion trends have come and gone throughout the past two decades, WordPress has thrived. This is due to the fantastic work and contributions of the WordPress community, comprised of thousands of contributors; and millions of users who have embraced the four freedoms of WordPress and the mission to democratize publishing.

\n\n\n\n

Let’s celebrate!

\n\n\n\n

Throughout the beginning of 2023, leading up to the official anniversary date of WordPress’s launch (May 27, 2003), a number of different events will celebrate this important milestone, reflect on the journey, and look toward the future.

\n\n\n\n

Please join in!

\n\n\n\n

Over the next few months, be sure to check WordPress’s official social media accounts along with the official anniversary website for updates on how you can be involved in this exciting celebration by contributing content, collecting cool anniversary swag, and much more. 

\n\n\n\n

Use the hashtag #WP20 on social media so the community can follow along.

\n\n\n\n

If you have something planned to celebrate that you would like to be considered for inclusion on the official website, please use this form to share the details.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14155\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"WP Briefing: Episode 46: The WP Bloopers Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wordpress.org/news/2022/12/episode-46-the-wp-bloopers-podcast/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 31 Dec 2022 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:11:\"wp-briefing\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14123\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:115:\"This episode of the WP Briefing features all the Josepha bloopers our little elves have stored away over the year. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:60:\"https://wordpress.org/news/files/2022/12/WP-Briefing-046.mp3\";s:6:\"length\";s:1:\"0\";s:4:\"type\";s:0:\"\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9636:\"\n

This episode of the WP Briefing features all the Josepha bloopers our little elves have stored away over the year.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress Briefing, the podcast where you can normally catch quick explanations of the ideas behind the WordPress open source project with the hope that deeper understanding creates deeper appreciation.

\n\n\n\n

But on today’s bonus episode, instead of catching quick explanations, you’ll catch some quick bloopers. 

\n\n\n\n

The end of the year is a time when many people and many cultures gather together, and whether you observe traditions of light or faith, compassion, or celebration from everyone here at the WordPress Briefing Podcast, we’re wishing you a happy, festive season and a very happy New Year.

\n\n\n\n

Sit back, relax, and enjoy some of the laughs and outtakes from recording the WP Briefing over the year.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress. This is the thing I’ve done 25 times, and I know how to do it for reals.

\n\n\n\n

Welcome to WordPress Briefing, episode 20. Oh no, 7? 27? 26? Episode 27. I know how many things I’ve done.

\n\n\n\n

Ooh, neat. This is Josepha recording episode 46 of the WP Bonus Briefings. Not because we’ve had 46 bonus Briefings, but because this is the 46th one and it is a bonus, it will also have a fancy name. But right now, I’m just calling it the bonus. It’s gonna be quick. Here I go. 

\n\n\n\n

Group them into two big buckets, themes, uh, themes and tools. Mmm, I’m gonna have to redo the whole thing! No! I thought I could save it, and I didn’t save it. I had a typo in my script, and then I messed it up. I, it said into you big buckets instead of into two big buckets. 

\n\n\n\n

[Josepha Haden Chomphosy 00:02:00] 

\n\n\n\n

I’m gonna start over from the target release date because I kind of smeared it all together, um, despite what I intended to do.

\n\n\n\n

And gives everyone, no. What is this ringing of phones? Oh, I was doing so well. Where was I? Let’s see if I can just pick it up.

\n\n\n\n

All righty, live from my closet. It’s episode 20, the WordPress Briefing, WP Briefing. So I have a title for this, and when I started writing it, I really had every intention of writing it to the title. And then what I wrote doesn’t fit the title at all, but does really hang together well. And so we’re gonna have to come up with a new title, but at the moment, it’s called So Many Ways to WordPress.

\n\n\n\n

Here in a minute, you will see why it doesn’t fit. Also, at the end, I feel like I get very, like, angry nerd leader.

\n\n\n\n

[Josepha Haden Chomphosy 00:03:00]  

\n\n\n\n

And so I may, I may at the end, give that a second go and see if there’s a way that I can soften it a little bit, but, I, I don’t know that I can soften it. I feel very strongly about it. So, maybe I am just an angry nerd leader.

\n\n\n\n

Oh, okay. I’ll get us started now that I apparently have filled the room with apologies, not the room, the closet. 

\n\n\n\n

We’ll figure out something very catchy as a title or as an alternative. Very descriptive, and people will click on it because they must know, but we’ll figure out the title later.

\n\n\n\n

@wordpress.org. However, I don’t know why I decided to do an invitation to email me in the middle of that. I’m gonna start from the top of that paragraph. I just got too excited by the opportunity to get mail.

\n\n\n\n

I gotta slow it down. I’m like the fastest talker, had too much coffee. Okay, slowing it down now. 

\n\n\n\n

Huh? What am I saying? No, no, that’s what I’m saying. It’s fine. I, I can do this. 

\n\n\n\n

[Josepha Haden Chomphosy 00:04:00]

\n\n\n\n

Hold on. Oww. Sorry. I was adjusting my microphone, and then it fell down. I happened to be holding it at the time, so it didn’t, like, slam down, I think, and hurt your ears and so I apologize. Good thing I stopped so it didn’t just, like, slam down in the middle of a recording.

\n\n\n\n

That’s all right. I’m gonna give myself that win, even though it’s a hollow one. All right. Trying again. Starting right there, at now since.

\n\n\n\n

This year, it starts on October 18th, 2001. That’s the year? No, 2021. That’s the year. Oh man. I’m doing such a great job of this.

\n\n\n\n

Um, I’m recording this slightly before, um, you’re hearing it? What, how am I gonna start this? Hold on. I don’t know how to start this. All right. I’m, I can do it.

\n\n\n\n

Oh, I’m so glad I remembered. We had guests that could have been so embarrassing.

\n\n\n\n

Now for me, the trade-offs work well. How many times can I say now?

\n\n\n\n

[Josepha Haden Chomphosy 00:05:00] 

\n\n\n\n

Do I just start every sentence with now now? Is this just how I do things? Uh, now, now, now, now. I’m gonna start all over again because I’m in my head about the words in my mouth now. So.

\n\n\n\n

In some near timeframe, some near timeframe. This is not a thing that people say, Dustin, I’m sorry. That’s not a thing people say. I’m just gonna retry that one sentence to sound like I speak with other human beings sometimes.

\n\n\n\n

Today is the start of… I can do these things.

\n\n\n\n

This was a terrible ending. I need to just finish that last part. I’m gonna redo the part where I started with my name and not the name of the podcast. Um, and we’ll do that.

\n\n\n\n

And if you’re supporting or building anything to hand off to clients, you know that timely, easy to ship changes on a site are considered a vital part of any overarching brand and marketing strategy. Wow. It’s like, I don’t know what words are right there. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

I tripped over my own tongue a lot. I’m gonna sit, I’m gonna do that paragraph again because I didn’t do a very good job of it.

\n\n\n\n

I’ll do a better job.

\n\n\n\n

I literally digress, and now I don’t know. I am in my thing. What was I saying? Oh, there we go. 

\n\n\n\n

Topher DeRosia, who founded Word not WordPress. Holy moly. That was a, I knew I was gonna say that, and I was like, don’t say that when you actually get around to saying this, but here I am, and I did it. Even though I knew I was gonna do it and I told myself not to. Doing it again. Right from there.

\n\n\n\n

Not which audiench segment. Oh man. Audiench is not a word, folks. I was on a roll. I’m gonna start right from the primary thing.

\n\n\n\n

I don’t even remember how I started this podcast. What is the last thing I said? I said, here we go. All right. 

\n\n\n\n

Kind of covered some interesting ground, and so, oh no, this is not where I’m gonna start it. I know exactly where I’m gonna start it. Okay. I’m really ready now. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:07:00] 

\n\n\n\n

I suddenly, I’m gonna pause right here because I suddenly got really worried that I didn’t actually hit record. Oh my gosh. I did. Woo. I’m all over the place. Okay. We’ll now continue. Wait, did I? Oh my goodness. I did, super sorry.

\n\n\n\n

Of the WordPress Briefing. I’m gonna do some singing in the middle of some talking, but I keep trying to talk myself out of the singing, so I’m gonna go ahead and do the singing, and then I’ll do the talking before I talk myself out of the singing. Here I go, probably.

\n\n\n\n

I added a word. That was so good. I’m gonna start again. I’m gonna get some water, and then I’m gonna start again. Not again. Again. Just from the ‘and finally.’

\n\n\n\n

I don’t know how I finish my show. Y’all, I do this literally every week. I never know how to finish my show. Here we go.

\n\n\n\n

I don’t know why I shouted at you from the other side of the tiny closet. I apologize. I’m gonna start again from ‘and finally.’

\n\n\n\n

Tada we did it.

\n\n\n\n

[Josepha Haden Chomphosy 00:08:00] 

\n\n\n\n

Ha. I hate it. I hate the whole podcast. It’s gonna be fine. 

\n\n\n\n

Done. Nailed it.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

With that, I’m your host, Josepha Haden Chomphosy. Merry Christmas from me. Happy holidays to you, and we’ll see you again in the new year.

\n\n\n\n

Done.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14123\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"WP Briefing: Episode 45: State of the Word Reflections\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"https://wordpress.org/news/2022/12/episode-45-state-of-the-word-reflections/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 22 Dec 2022 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:11:\"wp-briefing\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14070\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"Josepha reflects on this year\'s State of the Word address here on the WP Briefing podcast. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:60:\"https://wordpress.org/news/files/2022/12/WP-Briefing-045.mp3\";s:6:\"length\";s:1:\"0\";s:4:\"type\";s:0:\"\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:14564:\"\n

In the forty-fifth episode of the WordPress Briefing, WordPress Executive Director Josepha Haden Chomphosy discusses highlights from this year’s State of the Word address.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

References

\n\n\n\n

LearnWP
WordPress Playground
ICYMI: State of the Word Recap
Take the 2022 WordPress Survey!
Exploring WordPress Certifications
Community Summit WordCamp Site
Submit Topics for the 2023 Community Summit
20th Anniversary– Stay Tuned for Updates
Check Out Style Variations and the 2023 Theme

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello, everyone, and welcome to the WordPress Briefing, the podcast where you can catch quick explanations of the ideas behind the WordPress open source project, some insight into the community that supports it, and get a small list of big things coming up in the next two weeks. I’m your host, Josepha Haden Chomphosy. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:39]

\n\n\n\n

Last week, WordPress hosted its annual State of the Word. As usual, this was delivered by our project co-founder Matt Mullenweg and represented a year-long labor of love from the WordPress community as a whole. There are many things I love about State of the Word, but consistently the thing I love the most is being able to shine spotlights on the great work of our global network of contributors.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:02] 

\n\n\n\n

Since that presentation goes by at the speed of light, I wanted to highlight a few things as well. First things first, I wanted to highlight that we had nearly 1,400 contributors, and by nearly, I mean just one too few. We had 1,399 contributors. So that is a big deal in general, but it’s an especially big deal to me because that’s before we start looking at any contributions that aren’t specifically tied to a release. 

\n\n\n\n

You may be wondering what those non-release contributions are. An incomplete list of those contributions would include organizing WordPress events, training others how to use WordPress, the myriad podcasts, articles, and newsletters that make up the WordPress media community, and any participant in a call for testing. Not to mention the unglamorous ways to contribute, like reviewing themes or reviewing plugins.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:58] 

\n\n\n\n

Things like patching security vulnerabilities and the bazillion things that Meta does to make sure that our community has all the tools that it needs to function. So I want to echo, once again, the huge, huge thanks that Matt already shared in State of the Word, and thank all of you for showing up for our project and for each other this way.

\n\n\n\n

The next thing I wanted to be sure to highlight was LearnWP. It was briefly noted that 12,000 learners had found their way to courses on learn.wordpress.org, and then during the Q&A, there was a related question about certifications in WordPress. 

\n\n\n\n

The need for certifications has been a regular topic in our project, and I mentioned that there are two different ongoing discussions at the moment. One of those discussions is happening directly on the make.wordpress.org/training site, so I’ll share a link in the show notes for that.

\n\n\n\n

But I’ve also been personally chatting on and off with Training team reps and other members of the community about what makes that so hard. In case you have not heard my whole spiel about what makes it difficult, it’s the logistics and our speed of iteration, and public perception. 

\n\n\n\n

[Josepha Haden Chomphosy 00:03:05]

\n\n\n\n

So not exactly a small set of hurdles. I’ll be doing a more complete post on this in the New Year so that we can get some solid documentation of the state of things and not let it be lost forever in this podcast. But I do know that it is something that we are very interested in as a community and something that, historically, I have really been resistant to.

\n\n\n\n

Not because I think it’s a bad idea, but because as someone who’s looking out for our operations side of things and our logistics side of things, it is not clear how we’re gonna get that done. Like I said, in the New Year, keep an eye out for a big, big post that takes a look at the benefits versus the costs and everything that we can do to help make those match each other a bit better.

\n\n\n\n

And then the last thing I wanted to highlight was the WordPress Playground. Okay, so this was the last thing that Matt mentioned, and I want to be sure that it’s clear what’s going on with this project because when I first heard about it, I very nearly lept from my chair! 

\n\n\n\n

[Josepha Haden Chomphosy 00:04:03] 

\n\n\n\n

It was such a remarkably big deal. Okay, so the WordPress Playground uses technological magic called ‘web assembly.’ I don’t know what it is, but it’s magic. And when I say magic, I mean that this tool makes it possible to run WordPress, an instance of WordPress, including a theme and a handful of plug-ins entirely inside your browser as a logged-in admin.

\n\n\n\n

You don’t need a server. You don’t need to select a host. You don’t need to download anything at all. You don’t need to know what your domain’s going to be. You simply select the theme you want to test. Add some dummy content and see how all of the posts and pages function as though we’re a real live WordPress site running on your favorite top-tier host.

\n\n\n\n

Then when you close the tab, it’s gone forever. Poof. Just like that. Now, this is a brand new project. It’s brand new to us and has a long way to go. So if working on that sounds cool, stop by the Meta Playground channel in the Making WordPress Slack. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:09] 

\n\n\n\n

But this, in my mind, changes the way that we stage sites.

\n\n\n\n

It could change the way we determine whether a theme or plugin is right for us. And arguably, it can become a stress-free way to introduce new or undecided users to WordPress’s admin area so that they can tell what they’re getting into. So when I say that this is a mind-blowing thing, and when I say that it is powered by magic, like it is astounding, it is astounding.

\n\n\n\n

And the applications for our users as a whole, I think, are untapped yet, and potentially even the applications for our learners and future learners of WordPress– equally untapped. I’m very excited to see what we can do with this project in the future. So stop by the Meta channel. Stop by Meta Playground.

\n\n\n\n

See what’s going on over there. We would love to have you. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

So those are my highlights of the day for State of the Word. Like I said, there are a few things I want to do more of a deep dive on in the text, so keep an eye out on make.wordpress.org/projects for most of those. But right now, let’s make some time for the small list of big things.

\n\n\n\n

[Josepha Haden Chomphosy 00:06:17] 

\n\n\n\n

Today I actually have kind of like a big list of big things. But I pretended it was small, so you didn’t turn off the podcast. So the first thing that I have is that in case you missed State of the Word, if you didn’t have a Watch Party to go to, or you didn’t know it was happening and so you didn’t really tune in at the time, I’m going to drop in a link of the recording.

\n\n\n\n

It’s gonna probably start right when everything gets going. And so you shouldn’t have to scrub through anything. If you end up on one of the recordings that includes like the whole live stream, there is jazz for the first 30 minutes, and just, you know, skip through that.

\n\n\n\n

[Josepha Haden Chomphosy 00:07:00]

\n\n\n\n

The second thing on my big list of big things is our annual community survey. So Matt mentioned this in State of the Word, and he pointed out that one of the things that makes WordPress and open source in general so effective is that we have a way to communicate with people who are using our software and we make every effort to be responsive to it.

\n\n\n\n

So the annual survey that we send out, it used to be quite big, and we’ve cut it down to 20 questions. If you want, you can think of it as like a census, so have your type of work and how long you’ve been working in WordPress, and what you wish to do with WordPress– have all those things be counted so we have a good idea of the type of person who’s currently using WordPress, and we can account for your needs and wants.

\n\n\n\n

But also, if you want to think of it more as an opportunity to share the things that were especially useful for you in the project this year or especially valuable for you as a contributor, this is also an excellent place to do that.

\n\n\n\n

[Josepha Haden Chomphosy 00:08:01] 

\n\n\n\n

There’s a QR code running around on the internet somewhere, but I’ll also put a link in the show notes. If you do not know where the show notes are, by the way, they are at wordpress.org/news/podcast, and you’ll be able to get to the survey.

\n\n\n\n

The third thing on my big list of big things is that next year we’re hosting a community summit. So if you’ve never been to a community summit, Matt mentioned that it is an opportunity for the best and most prolific contributors that we have to show up and discuss the things that are the biggest problems for the WordPress project right now.

\n\n\n\n

But we also want to make sure that we are making space for the voices that we know that we are missing from the community as well as contributors who look like they are probably excellent future stewards of this open source project that we are taking care of together. And so there is a whole website for that.

\n\n\n\n

[Josepha Haden Chomphosy 00:08:55] 

\n\n\n\n

I believe it’s communitysummit.wordcamp.org. Right now, there is a form up asking for topics that you want to be able to discuss while we are there, but it’s taking place, if I recall correctly, on August 22nd and 23rd of 2023.

\n\n\n\n

Number four on my big list of big things is that next year is WordPress’s 20th anniversary. So on May 27th of next year, WordPress will officially be 20 years old. So on our 10th birthday, anniversary rather, and our 15th anniversary, we pulled together some parties all across the world. 

\n\n\n\n

We had some images, some logos, and things that were specific to the celebration that we printed into stickers and that folks put on, like, mugs and backpacks and cakes and stuff. So if you want to learn more about that, keep an eye out in the community channel in making WordPress Slack. They will keep you posted on how to one, find any of those logos and designs so that your local community can join in the celebrations.

\n\n\n\n

[Josepha Haden Chomphosy 00:10:03] 

\n\n\n\n

But they will also help you learn how to have any sort of WordPress celebration party that we’re doing there in May of 2023. 

\n\n\n\n

And then the final thing on my big list of big things, it was mentioned that on the 2023 theme that was shipped with a bunch of style variations and there was this really, I think, excellent illustrative video that Rich Tabor put together for us that shows that you can switch through style variations on a single theme and have a site that looks totally different.

\n\n\n\n

Now, that feels like that’s just a thing that should always have been in WordPress, but it is new this year. And so, if you have not yet had a chance to look at the 2023 theme, it is the default theme that shipped with 6.1. And so, if you have it on your website and just haven’t had a look at it yet, I encourage you to do that.

\n\n\n\n

[Josepha Haden Chomphosy 00:11:00]

\n\n\n\n

It’s a really interesting implementation that makes a single theme potentially look like an infinite number of other themes, and those style variations can be specific to the theme or can just kind of be around and about in the patterns that are also available in Core. 

\n\n\n\n

Give that a look. I think it’s super worthwhile.

\n\n\n\n

And that, my friends, is your big list of big things. Thank you for tuning in today for the WordPress Briefing. I’m your host, Josepha Haden Chomphosy, and I’ll see you again in the New Year.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14070\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"The Month in WordPress – November 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"https://wordpress.org/news/2022/12/the-month-in-wordpress-november-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Dec 2022 12:05:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:18:\"month in wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14124\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:317:\"WordPress enthusiasts tuned in last week for the State of the Word address to celebrate the project\'s yearly accomplishments and explore what 2023 holds. But that’s not the only exciting update from the past month. New proposals and ideas are already emerging with an eye on the year ahead—let’s dive into them!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"rmartinezduque\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:13931:\"\n

WordPress enthusiasts tuned in last week for the State of the Word address to celebrate the project’s yearly accomplishments and explore what 2023 holds. But that’s not the only exciting update from the past month. New proposals and ideas are already emerging with an eye on the year ahead—let’s dive into them!

\n\n\n\n
\n\n\n\n

Highlights from State of the Word 2022

\n\n\n\n

WordPress co-founder Matt Mullenweg delivered the annual State of the Word address on December 15, 2022, before a live audience in New York City. Most attendees joined the event via livestream or one of the 33 watch parties held across 11 countries.

\n\n\n\n

Josepha Haden Chomphosy, Executive Director of WordPress, kicked off this year’s event with an introduction to the Four Freedoms of open source and the importance of WordPress in ensuring “a free, open and interconnected web for the future.”

\n\n\n\n

Similar to past State of the Word events, Matt reflected on the project’s achievements over the past year, including Gutenberg’s adoption beyond WordPress, the steady progress in advancing the site editing experience, and the return to in-person events. In addition, he took the opportunity to remind everyone of the 2023 Community Summit and the 20th anniversary of WordPress coming up next year.

\n\n\n\n

Ahead of 2023, Matt announced new taxonomies in the WordPress.org theme and plugin directories to help users identify the extensions that best fit their needs and plans for Phase 3 of Gutenberg—Collaboration—among other notable updates.

\n\n\n\n

People who watched the State of the Word enjoyed a demo of WordPress Playground, an experimental project to explore, experiment, and build apps with a WordPress instance that runs entirely in the browser.

\n\n\n\n
\n

Missed the event? Read the recap or watch the State of the Word recording and Q&A session on WordPress.tv.

\n
\n\n\n\n
\n\n\n\n

The 2022 WordPress Survey is open

\n\n\n\n

The annual WordPress survey helps project leadership and those who build WordPress understand more about the contributor experience, how the software is used, and by whom.

\n\n\n\n

This year’s survey will remain open through the end of 2022 and is available in English, French, German, Italian, Japanese, Russian, and Spanish.

\n\n\n\n
\n

Take the 2022 WordPress Survey to help make an impact on the project.

\n
\n\n\n\n
\n\n\n\n

What’s new in Gutenberg

\n\n\n\n

Two new versions of Gutenberg have shipped in the last month:

\n\n\n\n
    \n
  • Gutenberg 14.6, released on November 23, 2022, came with many refinements to core blocks. Notable highlights include a variation picker that allows users to choose a desired layout when a Group block is inserted on a page, a new list view for editing the Navigation block, and a keyboard shortcut to transform paragraph blocks into headings.
  • \n\n\n\n
  • Gutenberg 14.7, released on December 7, 2022, introduced an experimental tabbed sidebar, colors to help identify some block types in list view, and improvements to the Page List block to make it easier to manage page links in the content.
  • \n
\n\n\n\n
\n

Follow the “What’s new in Gutenberg” posts to stay on top of the latest enhancements.

\n
\n\n\n\n
\n\n\n\n

Team updates: Introducing the block editor in the support forums, a revamped Showcase page, and more

\n\n\n\n\n\n\n\n
\n

Curious about why WordPress has so many releases? Tune in to Episode 44 of WP Briefing to learn about the role of major and minor releases in the project.

\n
\n\n\n\n
\n\n\n\n

Feedback & testing requests

\n\n\n\n\n\n\n\n
\n

The Community Team is calling on WordPress contributor teams to suggest topics for the 2023 Community Summit by January 16, 2023.

\n
\n\n\n\n
\n\n\n\n

WordPress events updates

\n\n\n\n
    \n
  • The #WPDiversity working group organized several workshops during the past few months. Among other highlights, attendees of the Speaker Workshop for Women Voices in Latin America reported a 52% increase in self-confidence to speak in public. Stay tuned for the next events.
  • \n\n\n\n
  • The WordCamp Europe 2023 organizing team shared their content vision for next year’s flagship event in Athens, Greece.
  • \n\n\n\n
  • WordCamp Asia 2023 is just a few months away, scheduled for February 17-19, 2023, in Bangkok, Thailand. Organizers have announced the first recipient of the WordCamp Asia Diversity Scholarship, Awais Arfan.
  • \n\n\n\n
  • Three more WordCamps are happening in the next few months:\n\n
  • \n
\n\n\n\n
\n

WordCamp Europe 2023 is calling for sponsors and speakers.

\n
\n\n\n\n
\n\n\n\n
\n\n\n\n

Have a story we should include in the next issue of The Month in WordPress? Fill out this quick form to let us know.

\n\n\n\n

The following folks contributed to this edition of The Month in WordPress: @cbringmann, @webcommsat, @sereedmedia, and @rmartinezduque.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14124\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"State of the Word 2022: A Celebration of the Four Freedoms of Open Source\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"https://wordpress.org/news/2022/12/state-of-the-word-2022-recap/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Dec 2022 22:11:15 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:6:\"Events\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:17:\"state of the word\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14110\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:355:\"WordPress belongs to all of us, but really we’re taking care of it for the next generation.” Matt Mullenweg A small audience of WordPress contributors, developers, and extenders gathered on December 15 for the annual State of the Word keynote from WordPress co-founder Matt Mullenweg. Those who could not join in person joined via livestream […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Chloe Bringmann\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:5677:\"\n
\n

WordPress belongs to all of us, but really we’re taking care of it for the next generation.”

\nMatt Mullenweg
\n\n\n\n
\n\n
\n\n\n\n

A small audience of WordPress contributors, developers, and extenders gathered on December 15 for the annual State of the Word keynote from WordPress co-founder Matt Mullenweg. Those who could not join in person joined via livestream or one of 33 watch parties held across 11 countries, with more than 500 RSVPs.

\n\n\n\n
\"The
\n\n\n\n

Executive Director, Josepha Haden Chomphosy, introduced the event with a reminder of why so many of those gathered choose WordPress—the Four Freedoms of open source. As Haden Chomphosy noted, open source is an idea that can change our generation, and WordPress is one of the most consistent and impactful stewards of those freedoms.

\n\n\n\n

As with past State of the Word events, Matt reflected on the year’s accomplishments, learnings, and aspirations as the project moves into 2023. From Gutenberg concluding its second phase of site editing in preparation for phase three—Collaborative Workflows, to the reactivation of meetups and global WordCamps, to the introduction of a new theme and plugin taxonomy, to musings on the potential of machine learning, WordPress enters its 20th year continuing to define bleeding edge technology in thanks to the ecosystem’s vibrant community. 

\n\n\n\n

The one-hour multimedia presentation was followed by an interactive question and answer session where Matt fielded questions from the livestream and studio audience. All questions will be responded to in a follow-up post on Make.WordPress.org/project

\n\n\n\n

Discover everything that was covered by watching the official event recording and join the ongoing #StateOfTheWord conversation on Tumblr, Instagram, Facebook, Linkedin, and Twitter. For another way to get involved, consider sharing your experience with WordPress in the 2022 WordPress Community Survey.

\n\n\n\n\n\n\n\n

Referenced Resources 

\n\n\n\n\n\n\n\n

Special thanks to @laurlittle and @eidolonnight for review and collaboration.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14110\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"Share Your Experience: The 2022 WordPress Survey is Open\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:57:\"https://wordpress.org/news/2022/12/2022-wordpress-survey/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Dec 2022 16:00:19 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:6:\"survey\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14062\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:131:\"The 2022 WordPress survey is open for your input and available in English, French, German, Italian, Japanese, Russian, and Spanish.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Chloe Bringmann\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:4584:\"\n

Each year, members of the WordPress community (users, site builders, extenders, and contributors) provide valuable feedback through an annual survey. Key takeaways and trends that emerge from this survey often find their way into the annual State of the Word address, are shared in the public project blogs, and can influence the direction and strategy for the WordPress project.

\n\n\n\n

Simply put: this survey helps those who build WordPress understand more about how the software is used, and by whom. The survey also helps leaders in the WordPress open source project learn more about our contributors’ experiences.  

\n\n\n\n

To ensure that your WordPress experience is represented in the 2022 survey results, take the 2022 annual survey now.

\n\n\n\n\n\n\n\n

You may also take the survey in French, German, Italian, Japanese, Russian, or Spanish, thanks to the efforts of WordPress polyglot contributors. These are the most frequently installed languages based on the number of WordPress downloads. 

\n\n\n\n

The survey will be open through the end of 2022, and then WordPress plans to publish the results sometime in 2023. This year, the survey questions have been refreshed for more effortless survey flow, completion, and analysis. Some questions have been removed, while a few new ones are now present, reflecting the present and future of WordPress. If you’re looking for the analysis of the 2021 survey results, those will also be shared in early 2023.

\n\n\n\n

Spread the word

\n\n\n\n

Help spread the word about the survey by sharing it with your network, through Slack, or within your social media accounts. The more people who complete the survey and share their experience with WordPress, the more the project as a whole will benefit in the future.

\n\n\n\n

Security and privacy

\n\n\n\n

Data security and privacy are paramount to the WordPress project and community. With this in mind, all data will be anonymized: no email addresses nor IP addresses will be associated with published results. To learn more about WordPress.org’s privacy practices, view the privacy policy.

\n\n\n\n

Thank you

\n\n\n\n

Thank you to the following WordPress contributors for assisting with the annual survey project, including question creation, strategy, survey build-out, and translation:

\n\n\n\n

dansoschin, _dorsvenabili, angelasjin, arkangel, audrasjb, atachibana, bjmcsherry, chanthaboune, eidolonnight, fernandot, fierevere, fxbenard, jdy68, jpantani, laurlittle, nao, nielslange, peiraisotta, piermario, rmartinezduque, santanainniss.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14062\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:72:\"\n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"People of WordPress: Huanyi Chuang\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"https://wordpress.org/news/2022/11/people-of-wordpress-huanyi-chuang/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 30 Nov 2022 20:09:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:6:{i:0;a:5:{s:4:\"data\";s:9:\"Community\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:3;a:5:{s:4:\"data\";s:10:\"Interviews\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:4;a:5:{s:4:\"data\";s:9:\"HeroPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:5;a:5:{s:4:\"data\";s:19:\"People of WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=13562\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:144:\"The latest People of WordPress story features Huanyi Chuang, from #Taiwan, on his journey to become a digital marketer and front end developer. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Abha Thakor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:11689:\"\n

This month we feature Huanyi (Eric) Chuang, a front end developer from Taiwan, who helps connect local groups to WordPress and the worldwide open source community. He is part of the team helping to make the first WordCamp Asia a success in 2023.

\n\n\n\n

The People of WordPress series shares some of the inspiring stories of how people’s lives can change for the better through WordPress and its global network of contributors.

\n\n\n\n
\"Huanyi
\n\n\n\n

Discovering WordPress and the benefit of child themes

\n\n\n\n

Huanyi’s first footsteps in WordPress began in 2017 when he worked for a firm that built blogs and developed ad content for clients.

\n\n\n\n

After building a few sites using the platform, he discovered child themes and through them opened up a world of possibilities for his clients. To this day, he uses child themes to deliver truly custom designs and functionality for clients.

\n\n\n\n

Later in his career, Huanyi moved into digital marketing, integrating sites with massive ad platforms like Google and Facebook. This led him to learn to work with tracking code and JavaScript. He also began his learning journey in HTML, CSS, and PHP, to be able to improve his development skills and customize child themes.

\n\n\n\n

Meetups bring together software users to learn together

\n\n\n\n
\"Huanyi
Huanyi pictured in Australia during one of his travels meeting a koala bear.
\n\n\n\n

When Huanyi had a problem with a client’s site, he looked to WordPress meetups near where he lived in Taipei to help find the solutions.

\n\n\n\n
\n

“When I encountered an issue with the custom archive pages, a local meetup announcement showed up on my WordPress dashboard.”

\nHuanyi Chuang
\n\n\n\n

At the meetup, he met more experienced WordPress users and developers there, who answered his questions and helped him learn.

\n\n\n\n

“When I encountered an issue with the custom archive pages, a local meetup announcement showed up on my WordPress dashboard. That was my original connection with the local community,” Huanyi said.

\n\n\n\n

The WordPress community gave Huanyi a chance to connect with people, feed his curiosity about the software, and join a circle of people he could share this interest.

\n\n\n\n

At first, he thought meetups were an opportunity to source new clients, and he took his business cards to every event. However, he soon found that these events offered him the opportunity to make friends and share knowledge.

\n\n\n\n

From then on, Huanyi started focusing more on what he could give to these events and networks, making new friends, and listening to people. This led him to share as a meetup speaker his own commercial website management experience.

\n\n\n\n

The road to WordCamp

\n\n\n\n

It was going to his first meetup and then getting involved with WordCamps that changed Huanyi’s whole relationship with WordPress.

\n\n\n\n
\"Huanyi
\n\n\n\n

In 2018, he took the step to help as an organizer, having joined the Taoyuan Meetup in Taiwan. He played several parts across the organizing team, and the welcoming feeling he got in every situation encouraged him to get more involved.

\n\n\n\n

He recalls meeting new friends from different fields and other countries, which gave him a great sense of achievement and strengthened his passion for participating in the community.

\n\n\n\n

When the team started this meetup, numbers were much lower than in the group in the city of Taipei, but they were not disheartened and gradually grew the local WordPress community.

\n\n\n\n

They created a pattern of ‘multiple organizers,’ which spread the workload and grew friendships. 

\n\n\n\n
\n

“Being connected to and from meetups is the most valuable part of the community. Having these friends makes me gather more information. We share information and benefit from others’ information, and thus we gain more trust in each other. With such credibility, we share more deeply and build deeper relations.”

\nHuanyi Chuang
\n\n\n\n

Before the pandemic, the meetup met every month and grew to become the second largest meetup in Taiwan. Huanyi also contributed to the WordPress community as an organizer of WordCamp Taipei 2018 in the speaker team and lead organizer of WordCamp Taiwan 2021.

\n\n\n\n

So why should you join the community?

\n\n\n\n

According to Huanyi, you will always have something to take home with you. It might be new information or experiences. It might be plugins or theme ideas. But most of all, it is the chance to meet fascinating people and make new friends.

\n\n\n\n
\n

Huanyi’s message to other contributors:
“Keep participating, and you will find more you can achieve than you expect.”

\n
\n\n\n\n

He added that long-term participation will ‘let you feel the humanity behind the project’.

\n\n\n\n

Localize: the road ahead for WordPress

\n\n\n\n
\"Huanyi
\n\n\n\n

Huanyi believes WordPress has the power to break down the barriers between designers, project managers, developers, marketers, writers, and publishers. In Taiwan, he said WordPress is ‘a common protocol’ that lets people from all of these disciplines work and communicate together more easily than they ever have before.

\n\n\n\n

That is why he works on and encourages others to localize plugins today. He believes localization of the software is the foundation for the extension of the WordPress community as it enables people to ‘Flex their Freedom’ in a language they speak!

\n\n\n\n

He has helped to organize online events around previous WordPress Translation Day events.

\n\n\n\n

Huanyi said: “I think it’s important to localize WordPress because its very concept of ‘open source’ means that people can access it freely. In another way, free from the monopoly of knowledge and speech. To achieve it, it’s important that people can access it with their own language.

\n\n\n\n

“Localization is the foundation of the extension of WordPress community because it helps people using different languages to access the project and lowers the hurdle to understand how things work.”

\n\n\n\n

Share the stories

\n\n\n\n

Help share these stories of open source contributors and continue to grow the community. Meet more WordPressers in the People of WordPress series.

\n\n\n\n

Contributors

\n\n\n\n

Thank you to @no249a002 for sharing his adventures in WordPress.

\n\n\n\n

Thank you to Abha Thakor (@webcommsat), Mary Baum (@marybaum), Meher Bala (@meher), Chloe Bringmann (@cbringmann), Surendra Thakor (@sthakor), Adeeb Malik (@adeebmalik) for research, interviews, and contributing to this feature article.

\n\n\n\n

The People of WordPress series thanks Josepha Haden (@chanthaboune) and Topher DeRosia (@topher1kenobe) for their support.

\n\n\n\n
\"HeroPress
\n

This People of WordPress feature is inspired by an essay originally published on HeroPress.com, a community initiative created by Topher DeRosia. It highlights people in the WordPress community who have overcome barriers and whose stories might otherwise go unheard. #HeroPress

\n
\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"13562\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}s:27:\"http://www.w3.org/2005/Atom\";a:1:{s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:4:\"href\";s:32:\"https://wordpress.org/news/feed/\";s:3:\"rel\";s:4:\"self\";s:4:\"type\";s:19:\"application/rss+xml\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:44:\"http://purl.org/rss/1.0/modules/syndication/\";a:2:{s:12:\"updatePeriod\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"\n hourly \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:15:\"updateFrequency\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"\n 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:4:\"site\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"14607090\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:11:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Wed, 25 Jan 2023 13:14:24 GMT\";s:12:\"content-type\";s:34:\"application/rss+xml; charset=UTF-8\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:25:\"strict-transport-security\";s:11:\"max-age=360\";s:6:\"x-olaf\";s:3:\"⛄\";s:13:\"last-modified\";s:29:\"Thu, 19 Jan 2023 12:00:00 GMT\";s:4:\"link\";s:63:\"; rel=\"https://api.w.org/\"\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:16:\"content-encoding\";s:4:\"gzip\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20211220193300\";}','no'),(139,'_transient_timeout_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1674695664','no'),(140,'_transient_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1674652464','no'),(141,'_transient_timeout_feed_d117b5738fbd35bd8c0391cda1f2b5d9','1674695670','no'),(142,'_transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:16:\"WordPress Planet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"en\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WordPress Planet - http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:50:{i:0;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:169:\"HeroPress: Becoming A Better Me with Core Contribution – কোর কন্ট্রিবিউশন এবং জীবনের নতুন অধ্যায়\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=5055\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:160:\"https://heropress.com/essays/becoming-a-better-me-with-core-contribution/#utm_source=rss&utm_medium=rss&utm_campaign=becoming-a-better-me-with-core-contribution\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:25361:\"\"Pull\n

এই নিবন্ধটি বাংলায় পাওয়া যায়

\n\n\n\nHere is Robin reading his own story aloud.\n\n\n\n
\n\n\n\n

Few years back, my daily life started with 10am waking up and going to the office without having breakfast (lazy me). Then doing a 9 hours job with a pretty simple routine and without any major engagement with others.

\n\n\n\n

At present, I wake up with tons of Slack messages and end my day with various in person short/long meetings with my fellow colleagues / mates around the world.

\n\n\n\n

I used to scroll Facebook, you know. But now WordPress Slack has become Facebook to me. How things got changed and became more enjoyable. 

\n\n\n\n

Lucky Me \"😊\"

\n\n\n\n

Hello World : How it all started

\n\n\n\n

I wasn’t supposed to be an engineer in the first place. I was brought up in Cumilla, Bangladesh. Finished my School and College in my hometown. Everyone wanted me to be a Doctor. It is very common here in our country that parents want their child to be a doctor. I completed my 3 months preparation for the Medical exam but later I ended up in Engineering.

\n\n\n\n
\n\n\n\n

I have spent 5 years in Sylhet, a heavenly place to live in. Oh! How I miss Sylhet these days. It has been a few years since I had breakfast (khichuri) in Pach Bhai restaurant (a very popular restaurant in Sylhet) and had tea in chachar tong (a famous tea stall in Modina Market, Sylhet). These days I don’t go out at night but during my Sylhet life, midnight tea was a much desired thing for us and of course that tea from a tong (small tea stall in the roads).

\n\n\n\n\n\n\n\n

My five years at SUST (Shahjalal University of Science and Technology) was a blessing to me. It helped me to become a better person and better me. Sust was full of energy. Seniors and Juniors. Lal Tong (tea stall in our campus). There were almost 300 plus students in our department and we knew personally almost 90 percent of our seniors and juniors. That bond is still alive in Dhaka (most of us living here with our job). Everyone helps each other to get a job or with the recommendation for the best jobs. Almost in every software farm I see SUST CSE seniors or juniors.

\n\n\n\n

Thanks God I got a chance to live those fine memorable years in SUST and Sylhet.

\n\n\n\n

Hello Dolly : Meeting WordPress

\n\n\n\n

My first meeting with WordPress was in my 2nd job. I was facing difficulties with my earlier professional career but as soon as I met WordPress, I just fell for her (WordPress). I found it really easy to adopt and it has a pretty huge community I must say. There were tons of documentation in Codex (but frankly I couldn’t understand at first). Now the documentation (https://developer.wordpress.org/) is much better and much more user friendly. I was amazed with the term Code is Poetry. It felt like I was writing poems instead of doing jobs.

\n\n\n\n

I really enjoyed my early career with WordPress. I wanted to do all by myself (that’s what we call Full Stack these days, LOL).

\n\n\n\n

I used to write markups from design (PSD to Html, that’s write). Then converting that into WordPress. And the training phase which was given to me was really a learning experience. I still keep in my mind that, “You can take unlimited divs. It won’t cost you money”, LOL. I was struggling with CSS opacity. But as soon as I started using it It became Pani(water, means easy) later.

\n\n\n\n

In my earlier life with WordPress I wasn’t aware of the active community and contribution to the project. I did many theme and site customization. Fixed bugs for clients. Built features as per their needs. But I was missing something.

\n\n\n\n

I was missing the large community of WordPress and the inner beauty of the Open Source project.

\n\n\n\n

Code is Poetry : WordPress Core Contribution

\n\n\n\n

My life at WPDeveloper was a blessing to me. It is where I started meeting the large community and the exciting activities of this wonderful community of WordPress. It feels like I truly belong to this community. Everyone is so close and so helpful to each other. 

\n\n\n\n

I have started joining meetups. Taking meetups, yes that’s correct. Started networking with similar minded people. It felt great to see so many people who love the same thing that you love. Such a blessing community.

\n\n\n\n

After joining WordPress slack and attending a few meetings, I found it is actually helping me to improve my skills. I saw how they manage their projects, how they think, how they fix. So many things to learn. I got addicted \"😀\" I started browsing channels often. 

\n\n\n\n

I started attending all the meetings of almost all the Make WordPress teams (that’s funny but I did). I was enjoying my life. 

\n\n\n\n

Slowly I started contributing to the Core WordPress. I do complex tasks in my regular job life but at core a simple task accomplishment gives so much pleasure. 

\n\n\n\n

Everytime I see my name in the commit description it feels good.

I didn’t stop after doing my first contribution to the core. I continued and I checked almost all tickets and figured out what I can fix or help to fix. I got PR reviews from WordPress experts. Their every single suggestion helped me to know the WordPress and Coding standards better. Now I do practice those coding standards in my regular job tasks.

\n\n\n\n

In WordPress 6.1 I contributed to 20 plus core tickets and that was a pretty good number in Bangladesh. These days I take online workshops in the Make Learn team, in person workshops in our Dhaka community. Also taking in house (within company) workshops to show how to join Release Parties and attend meetings and write team meeting notes. 

\n\n\n\n

By the way, I am Marketing Team Representative for the year of 2023. I am excited and looking forward to it. Also a Training Team Faculty member. 

\n\n\n\n

I don’t think all of these would be possible without being an active contributor to the project. Thank you everyone who helped me in this wonderful journey \"😊\"

\n\n\n\n

Life Is Beautiful : Living Success

\n\n\n\n

When I was writing this essay, I became one of the Release Leads of WordPress 6.2 (Test Co-Lead).

\n\n\n\n

It is unbelievable for me even after the declaration. I keep checking that P2 blog post just to make sure I am truly there, funny I know. 

\n\n\n\n

Recently I took contributor days in our office and it felt like there was only one topic in the town and that is “Let’s Do Core Contribution”. It became trending here, loving it \"😊\"

\n\n\n\n

Thanks to WordPress and the community. Due to my outstanding contribution in Core, I recently got selected for the prestigious #YoastCareFund and here I am sharing my stories with our HeroPress friends.

\n\n\n\n

Oh! I am living my dream life. Just one thing is missing. Ronaldo isn’t in UCL and is getting older. I know \"😀\"

\n\n\n\n

WordPress Core Contribution helped me to become a better developer, a better me. It removes your fear of losing your job and instead you will fall in love with your job and definitely you will enjoy every minute of your coding life.

\n\n\n\n

Thank You WordPress.
Code is Poetry and you are the book full of Poems.
I can’t stop reading you \"😊\"

\n\n\n\n

কোর কন্ট্রিবিউশন এবং জীবনের নতুন অধ্যায়

\n\n\n\n

এইতো কয়েক বছর আগেও, আমার ডেইলি রুটিন ছিল সকাল ১০ টায় ঘুম থেকে ওঠা এবং নাস্তা না করে অফিসে যাওয়া (আলসেমির কারণে দেরি হয়ে যেত এবং বাসায় নাস্তা করা হত না)। তারপর ৯ ঘণ্টার অফিস শেষ হত গতানুগতিক কাজ দিয়ে।

\n\n\n\n

বর্তমানে, আমার ঘুম থেকে উঠেই দেখি স্ল্যাক ভর্তি মেসেজ এবং দিন শেষ হয় ছোট বড় বেশ কিছু টিম কোলাবোরেশান এবং মিটিং এর মাধ্যমে। 

\n\n\n\n

আমি ছিলাম ফেসবুক পাগল, ইংরেজিতে এডিক্টেড \"😀\"। কিন্তু এখন WordPress Slack হয়ে গেছে ফেসবুক আমার কাছে। কীভাবে ইন্টারেস্ট পরিবর্তিত হয় এবং পরিবর্তনটা উপভোগও করছি।

\n\n\n\n

Lucky Me \"😊\"

\n\n\n\n

Hello World : যেভাবে পথচলা শুরু

\n\n\n\n

প্রথমত আমার ইঞ্জিনিয়ার হবার কথাই ছিল না। আমার শৈশব কাটে কুমিল্লায়। স্কুল এবং কলেজ এলাকাতেই ছিল। সবার চাইছিল আমি যেন ডাক্তার হই।আমাদের দেশে এটা খুব কমন যে বাবা মা চায় তাদের ছেলেমেয়েরা যেন ডাক্তার হয়। আমি মেডিকেলের জন্য তিন মাস প্রিপারেশান নেয়ার পরেও ভাগ্যক্রমে চান্স পেয়ে যাই ইঞ্জিনিয়ারিং এর জন্য।

\n\n\n\n

সিলেটে ছিলাম পাঁচ বছর। আহা সিলেট, Where Heaven touches the Earth <3  

\n\n\n\n
\n\n\n\n

সিলেট নাম শুনলেই থমকে যাই।সে কবে গেলাম।কতদিন পাঁচ ভাইয়ের খিচুরি খাই না, কতদিন মদিনা মার্কেটের চাচার টং দেখি না। কতদিন মাঝ রাতে বের হয়ে টং এর চা খাই না। 

\n\n\n\n

আহা সিলেট!  

\n\n\n\n\n\n\n\n

SUST (Shahjalal University of Science and Technology) এর ৫ বছর ছিল আমার জন্য ব্লেসিং। আমাকে পরিণত করেছিল সাস্ট। সাস্ট ছিল এনার্জিতে ভরপুর।সিনিয়র জুনিয়রদের সম্পর্ক। লাল টং। ৩০০ এর বেশি স্টুডেন্ট ছিল আমাদের ডিপার্টমেন্টে। যাদের মধ্যে ৯০ ভাগই ছিল আমাদের ভাই ব্রাদার। অলমোস্ট সবাইকেই চিনতাম আমরা। বর্তমানে আমরা সবাই ঢাকায় কোন না কোন জবে আছি। দেখা কম হলেও সম্পর্ক এখনও আগের মতই। সবাই সবাইকে জবে হেল্প করছে। জবের বাইরে হেল্প করছে।ঢাকার মোটামোটি সব ফার্মে গেলেই দেখা যায় SUST CSE থেকে কেউ না কেউ আছে।

\n\n\n\n

আল্লাহর কাছে শুকরিয়া সিলেট এবং সাস্টে পরার সুযোগ হয়েছিল।

\n\n\n\n

Hello Dolly : WordPress এর সাথে পরিচয়

\n\n\n\n

WordPress এর সাথে আমার প্রথম পরিচয় যখন আমি আমার দ্বিতীয় জবে জয়েন করি। ক্যারিয়ারের শুরুতে আমার খাপ খাওয়াতে একটু সমস্যা হচ্ছিল। যখনই WordPress এর সাথে পরিচয় তখন থেকেই ফিদা হয়ে গেলাম।এটার ব্যবহার বিগিনার হিসাবে তখন আমার কাছে অনেক সহজ এবং উপকারী ছিল।অনেক বড় একটা কমিউনিটি। রিসোর্স অনেক। যদি Codex ছিল বেশ কঠিন বুঝার জন্য। কিন্তু বর্তমানে ডকুমেন্টেশান (https://developer.wordpress.org/) অনেক ভাল এবং সহজ হয়েছে। প্রথম যখন Code is Poetry শুনেছি এবং দেখেছি আমার অনেক পছন্দ হয়েছিল। মনে হচ্ছিল কোড না যেন কবিতা লিখতেসি।

\n\n\n\n

ক্যারিয়ারের শুরুতে আমি WordPress বেশ উপভোগ করেছি। চাইতাম সব নিজে নিজে করব (যাকে আমরা বলি এখন Full Stack, লোল)। 

\n\n\n\n

শুরু হয়েছিল PSD to Html দিয়ে যা আসলে আমাদের অনেকের ক্ষেত্রেই মিলে যাবে। তারপর তা WordPress এ কনভার্ট করতাম। শুরুতে আমাকে একটা ট্রেনিং দেয়া হয়েছিল যা ছিল খুবী কার্যকর।

\n\n\n\n

আমার এখনও একটা কথা মনে আছে “যত বেশি div নিবা। div নিতে টাকা লাগে না”, লোল।  

\n\n\n\n

আমার CSS opacity নিয়ে সমস্যা হচ্ছিল। কিন্তু যখনই কাজ শুরু করে দিয়েছি আস্তে আস্তে সব পানি (ইংরেজিতে Water, মানে সহজ) হয়ে গেসে। 

\n\n\n\n

প্রথমদিকে আমি WordPress কমিউনিটি নিয়ে ততটা অবগত ছিলাম না। অনেক থিম কাস্টমাইজেশান এবং সাইট কাস্টমাইজেশান করেছি। Bug ফিক্স করেছি অনেক ক্লায়েন্টদের জন্য। ফিচার তৈরি করেছি তাদের চাহিদা অনুযায়ী। কিন্তু কি যেন একটা মিসিং ছিল। 

\n\n\n\n

WordPress Open Source Project এবং WordPress এর বড় একটা কমিউনিটির সাথে যে তখনও আমার পরিচয় হয়ে উঠেনি। 

\n\n\n\n

Code is Poetry : WordPress Core Contribution

\n\n\n\n

WPDeveloper ছিল আমার জন্য ব্লেসিং। এখানে আসার পর থেকেই আমি WordPress এর বড় কমিউনিটির সাথে পরিচিত হই এবং দেখতে থাকি তাদের একের পর এক চমৎকার উদ্যোগ।

\n\n\n\n

মনে হচ্ছিল যেন এটাই এতদিন মিসিং ছিল। সবাই এত আন্তরিক এবং সাহায্য করার জন্য কতটা উদগ্রীব। 

\n\n\n\n

আমি meetup জয়েন করা শুরু করলাম। meetup নেয়াও শুরু করলাম, হা ঠিক শুনেছেন।লোল। 

\n\n\n\n

সবার সাথে নেটওয়ার্কিং হল।দেখে খুব ভাল লাগল যে একই চিন্তা ধারার সবাই একসাথে।

\n\n\n\n

Such a blessing community.

\n\n\n\n

WordPress স্ল্যাক জয়েন করি এবং মিটিং এটেন্ড করা শুরু করি। দেখি যে এটা আসলেই আমাকে সাহায্য করছে আমার স্কিল বাড়াতে।দেখতে পেলাম কিভাবে তারা প্রজেক্ট মেনেজ করে, কিভাবে চিন্তা করে, কিভাবে বাগ ফিক্স করে। কত কিছু শিখার। এডিক্টেড হয়ে গেলাম \"😀\"। চ্যানেলগুলো প্রায়ই ব্রাউজ করতে থাকতাম।

\n\n\n\n

সব টিমের মিটিং জয়েন করতে শুরু করলাম (ফানি বাট সত্য)। সবকিছু ভালই লাগছিল। 

\n\n\n\n

আস্তে আস্তে কোর কন্ট্রিবিউশান শুরু করলাম। যদিও অফিসে কমপ্লেক্স কাজগুলাই আমরা করতাম। কিন্তু যখন একটা ছোট খাটো কোর কন্ট্রিবিউশান করি তখন মনে অনেক আনন্দ কাজ করে। যতবার কমিটে আমার নাম দেখি ততবারই ভাল লাগে। আহা।

\n\n\n\n

প্রথম কন্ট্রিবিউশানের পর আমি থেকে থাকি নাই। কন্টিনিউ করেছি। প্রতিদিন টিকেট গুলো ব্রাউজ করতাম। খুঁজে দেখতাম কোনটা করতে পারব। WordPress expert দের কাছ থেকে রিভিউ পেতে থাকলাম যখনই PR দিতাম।তাদের প্রতিটা সাজেশান আমার পরবর্তিতে বেশ কাজে দিয়েছে। নিজের অফিসের কাজেও তখন সেগুলো ব্যবহার করতে থাকলাম।

\n\n\n\n

WordPress 6.1 এ আমি ২০ এর অধিক টিকেট ফিক্স করতে সাহায্য করেছি। যা বাংলাদেশের জন্য বেশ ভাল একটা নাম্বার। এখন আমি Make Learn টিমের জন্য অনলাইন ওয়ার্কশপ বানাই। ইন পারসন ওয়ার্কশপ নেই আমাদের ঢাকা কমিউনিটির জন্য। ইন হাউজ ওয়ার্কশপ নেই কলিগদের জন্য। দেখাতে সাহায্য করি কিভাবে রিলিজ পার্টিতে জয়েন করতে হয়, কিভাবে টেস্ট রিপোর্ট লিখতে হয়, কিভাবে মিটিং নোট নিতে হয়।

\n\n\n\n

ভালো কথা, আমি এখন Marketing Team Representative ২০২৩ সালের জন্য। এটা আমি বেশ উপভোগ করছি। এবং সাথে আমি Training Team Faculty মেম্বারও। 

\n\n\n\n

আমার মনে হয় না কোর কন্ট্রিবিউশান ছাড়া আমার এই দায়িত্বগুলো পাওয়া পসিবল হত । সবাইকে অনেক ধন্যবাদ আমাকে সাহায্য করার জন্য \"😊\"। 

\n\n\n\n

Life Is Beautiful : সফলতা

\n\n\n\n

যখন আমি এটি লিখছি ততদিনে আরেকটি সুখবর পেয়ে গেছি। আমি এখন WordPress 6.2 এর একজন Release Lead (Test Co-Lead).

\n\n\n\n

একদম অবিশ্বাস্য। প্রায়ই P2 blog post গিয়ে চেক করে দেখি আমার নামটা আছে কিনা, হাস্যকর শুনাবে জানি। 

\n\n\n\n

কিছুদিন আগে কন্ট্রিবিউটর ডে আয়োজন করেছি। মনে হচ্ছিল যেন শহরজুড়ে একটাই ডায়লগ,

\n\n\n\n

“Let’s Do Core Contribution”। ট্রেন্ডিং হতে দেখে বেশ ভালই লাগে \"😊\"

\n\n\n\n

WordPress এবং কমিউনিটিকে অনেক ধন্যবাদ। কিছুদিন আগে #YoastCareFund পাই করে আউটস্ট্যান্ডিং কন্ট্রিবিউশানের জন্য। এবং আজ HeroPress বন্ধুদের সাথে সব শেয়ার করছি।

\n\n\n\n

একেই বুঝে বলে লিভিং ড্রিম লাইফ। একটা জিনিসই শুধু মিসিং। রোনাদোকে আর হয়ত ইউসিএলে দেখা যাবে না \"😀\"

\n\n\n\n

WordPress Core Contribution আমাকে ভাল ডেভেলপার হতে সাহায্য করেছে।জব হারানোর ভয় বাদ দিয়ে জবকে এঞ্জয় করা এবং কোডিং এর প্রতিটা মুহুর্ত উপভোগ করতে সাহায্য করে কোর কন্ট্রিবিউশান। 

\n\n\n\n

Thank You WordPress.
Code is Poetry and you are the book full of Poems.
I can’t stop reading you \"😊\"

\n

The post Becoming A Better Me with Core Contribution – কোর কন্ট্রিবিউশন এবং জীবনের নতুন অধ্যায় appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 25 Jan 2023 02:00:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:25:\"A H M Nazmul Hasan Monshi\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:55:\"WPTavern: Yoast SEO 20.0 Introduces New Admin Interface\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141380\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:66:\"https://wptavern.com/yoast-seo-20-0-introduces-new-admin-interface\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2764:\"

Yoast SEO version 20.0 was released today with a new admin settings interface that also reorganizes the menu to into four main sections: General, Content types, Categories and Tags, and Advanced.

\n\n\n\n\n\n\n\n

In this update, the plugin did not add new features and settings but rather moved them to better match user workflows. The new sidebar menu should result in fewer clicks in accessing the most used settings.

\n\n\n\n

The individual settings pages are also sporting the new design, which is lighter and brighter than the previous screens. With such a large number of settings to re-learn, Yoast SEO has also added a quick search to assist users in finding settings pages faster.

\n\n\n\n\n\n\n\n

“We felt that the default WordPress admin design no longer suited us,” Yoast founder Joost de Valk said. “Our product team was itching to take our experience to the next level. WordPress’ interface was holding us back a bit, as the admin interface outside Gutenberg hasn’t progressed for years.”

\n\n\n\n

The new settings UI was built with Yoast SEO’s React component library, which the company has open sourced and made available on its website.

\n\n\n\n

Reaction to the new design was mostly positive, although some users are not keen on plugins building their own UI in the admin. If all plugins did this, the WordPress admin would become a wild buffet of disparate interfaces that add cognitive load to site management.

\n\n\n\n

“It was… surprising so I’ll reserve real judgement until I use it a while,” WordPress developer Jon Brown said. “First impression though was ‘this needs an advanced mode that hides all the useless banner images and text and just goes back to a list with toggles.’ It’s pretty, but feels overwhelming.”

\n\n\n\n

 The Yoast SEO plugin and the new settings UI work with WordPress version 6.0 or higher. Users who are struggling to adapt to the new settings pages can reference Yoast SEO’s documentation, which has a video and guide to navigating the new interface.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 24 Jan 2023 21:43:57 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"Do The Woo Community: The WP Community Collective with Sé Reed and Courtney Robertson\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74360\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"https://dothewoo.io/the-wp-community-collective/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:422:\"

Sé and Courtney share all things to do with the new WP Community Collective, a source for supporting contributions and initiatives.

\n

>> The post The WP Community Collective with Sé Reed and Courtney Robertson appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 24 Jan 2023 10:36:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WPTavern: Awesome Motive Acquires Thrive Themes\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141347\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"https://wptavern.com/awesome-motive-acquires-thrive-themes\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1769:\"

Awesome Motive has acquired Thrive Themes, its second acquisition of 2023 following the Duplicator plugin deal that was announced earlier this month.

\n\n\n\n

Thrive’s premium plugin suite reports more than 200,000 users. This includes Thrive Architect, a visual drag and drop page builder, an LMS course builder, and other marketing-focused plugins for generating leads, creating quizzes and testimonials, and doing A/B testing.

\n\n\n\n

In 2013, Thrive Themes co-founders Shane Melaugh and Paul McCarthy began their company with early products Hybrid Connect, Viral Quiz Builder, and WP Sharely. Ten years later the product suite has grown to nearly a dozen conversion-focused tools that Thrive Themes sells for $299/year.

\n\n\n\n

Although the co-founders will not be joining Awesome Motive, the team that is currently maintaining and supporting the plugin is being acquired. In the Thrive Themes announcement, Melaugh said the company’s products will not be rebranded or replaced. No price hikes are planned for existing customers and Awesome Motive plans to honor legacy memberships.

\n\n\n\n

“It has always been our policy to reward loyal customers and that will not change,” Melaugh said.

\n\n\n\n

“I’ve been watching Thrive Themes from the sidelines for a long time anyway. So my stepping away changes nothing on that front.

\n\n\n\n

“It will still be the same people building the products, and the roadmap we laid out for 2023 and beyond won’t change because of this acquisition.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 24 Jan 2023 02:57:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"WPTavern: WP Migrate 2.6 Introduces Full-Site Exports and Import to Local\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141320\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:84:\"https://wptavern.com/wp-migrate-2-6-introduces-full-site-exports-and-import-to-local\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3672:\"

WP Migrate, formerly known as WP Migrate DB and recently acquired by WP Engine, has long since expanded beyond its initial release as a database migration tool. Users may be familiar with the push/pull workflow of installing the plugin on two sites and migrating database, media, themes, and plugin changes back and forth. The most recent 2.6 release expands the plugin’s capabilities to include full-site exports for integration with Local, a popular free WordPress development tool, also owned by WP Engine.

\n\n\n\n

This new remote-to-local workflow is included in both the free WP Migrate plugin and the pro version. The full-site exports bundle the database, media, themes, plugins, and other files into a ZIP archive, which can be seamlessly imported into Local.

\n\n\n\n\n\n\n\n

After clicking Export inside WP Migrate, users are taken to the next screen where they can configure what is included in the export file. This ZIP archive can be dragged and dropped into the Import screen in Local.

\n\n\n\n\n\n\n\n

The WP Migrate team collaborated with the Local team to match environments as closely as possible when exporting for Local import.

\n\n\n\n

“Each site exported with WP Migrate includes a wpmigrate-export.json file which contains metadata such as the PHP and MySQL versions that were last used on the site,” WP Migrate Product Manager Kevin Hoffman said. “During the import, Local reads this file and attempts to match the environment to that of the exported site, so the local site works (and breaks!) just like its remote counterpart.”

\n\n\n\n

In this migration scenario, the WP Migrate plugin can be included in the list of plugins so it is activated on the Local site, speeding up the workflow for setting up a local development site. Previously this required configuring plugins, add-ons, and license keys across both environments.

\n\n\n\n

“In the last year, we really embraced our new identity as a full-site migration solution,” Hoffman said. “One of the goals we set for ourselves was to handle the migration of an entire site from within WP Admin without ever having to touch cPanel, phpMyAdmin, or FTP. This new workflow is the culmination of those efforts delivered as a free end-to-end solution for the WordPress community.”

\n\n\n\n

Customers who have purchased the pro version may still opt for pushing and pulling directly between sites, but this new workflow makes it easier for users (both free and paid) to set up a local development environment for the first time.

\n\n\n\n

“When we realized how much simpler we could make the remote-to-local workflow by embracing full-site exports, we reached out to the Local team who helped make it happen,” Hoffman said.

\n\n\n\n

The WP Migrate team is looking at expanding the integration beyond matching the WordPress, PHP, and MySQL versions to give users the ability to predefine migration profiles for pushing local sites back to the remote host.

\n\n\n\n

“When configuring an export, we could also let users set up one-click admin access in Local,” he said. “Imagine dropping a ZIP into Local and landing in WP Admin without ever having to log in. There are lots of possibilities, and I’m sure more will pop up as the community starts to use it.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 23 Jan 2023 22:39:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:84:\"WPTavern: WordPress Community Team Proposes Adopting GitHub to Improve Collaboration\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141302\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"https://wptavern.com/wordpress-community-team-proposes-adopting-github-for-improved-collaboration\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4432:\"

Although GitHub is primarily used for code collaboration, WordPress’ Community team is considering adopting the platform to standardize their project management tools.

\n\n\n\n

Contributing to open source can already be challenging but when it requires signing up for multiple services in order to access the team’s many spreadsheets, trello boards, Slack groups, and other modes of communication, onboarding new contributors becomes needlessly difficult.

\n\n\n\n

A new proposal, authored by Community team rep Leo Gopal, outlines the benefits of using GitHub as a central communication tool. These benefits include improved collaboration and communication using the platform’s commenting system and the ability to track progress and assign tasks.

\n\n\n\n

Gopal contends that standardizing on GitHub would increase transparency and accountability while supporting better organization with tools like issues, labels, milestones, and project boards.

\n\n\n\n

“By adopting GitHub for project management and issue tracking, the Community Team will standardize our way of working, making it easier for new team members to get up to speed and enabling more effective cross-team collaboration,” Gopal said. “This standardization also makes it easier for Community Team members to track progress, identify issues and make data-driven decisions.”

\n\n\n\n

Other Make teams, such as Learn, Hosting, Meta, Marketing and more, are already successfully using GitHub to manage communication and prioritize projects. Gopal proposes the Community team learn from their efforts and adopt these tooling methods for a quarter as an experiment.

\n\n\n\n

“If after the first Quarter the consensus is that this does not suit our team, we will revert back to initial project and tracking practices and explore more,” Gopal said.

\n\n\n\n

A few participants in the resulting discussion have concerns about transparency and losing track of conversations, as they would not be linked to WordPress.org profiles.

\n\n\n\n

“The truth is that I am unsure about it,” Weglot-sponsored Community team contributor Juan Hernando said. “I think the community team is not particularly technical, and using GitHub may pose certain barriers we didn’t have so far. Maybe for many people opening an issue, requesting a pull request, or similar is their everyday life, but for others, it can be a bit blocking.

\n\n\n\n

“I’m also afraid that discussions will move from this Make site to GitHub, and we shouldn’t lose the spirit of owning our content (linked to our .org profile) and lose the use of this space for decision-making and public discussions like this one.”

\n\n\n\n

Gopal addressed this concern stating that there would be no code and that users who can work with Trello boards will have no problem adopting GitHub’s tools for planning.

\n\n\n\n

“Trello was used for planning and often forgotten until time for reviews or recaps,” Gopal said. “There was no way other teams would know what we are working on or add to the conversation unless they dug up our trello boards AND if we took their suggestion and weighed it in.”

\n\n\n\n

Gopal said using GitHub would allow the team to incorporate advantages like automations, assignments, and inter-team collaboration with advanced reporting capabilities. Overall, GitHub has the potential to increase the visibility of their work for those collaborating across teams.

\n\n\n\n

Milana Cap, who uses GitHub to help organize the Documentation team for reporting issues and automating tasks, recommended adopting the platform and shared how the Docs team is using it.

\n\n\n\n

“All the other benefits: version control, precise contribution tracking, all sorts of project management tools etc., can not be found all in one tool other than GitHub, and I can not recommend it enough – for everything,” Cap said.

\n\n\n\n

Discussion is still open on the proposal and Gopal has published a Proposal Poll for Community Team members to give their feedback on standardizing communications on GitHub.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 21 Jan 2023 04:32:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:103:\"WPTavern: Gutenberg 15.0 Introduces “Sticky” Position Block Support, Adds “Paste Styles” Option\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141268\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:101:\"https://wptavern.com/gutenberg-15-0-introduces-sticky-position-block-support-adds-paste-styles-option\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3623:\"

Gutenberg 15.0 was released this week with some exciting new features for working with blocks and an improved UI for managing controls in the inspector panel. This release marks the end of the block inspector tabs experiment, which is now stabilized in the plugin.

\n\n\n\n

Users will notice that some blocks will now have separate tabs in the inspector for displaying settings and design controls, and optionally a list view tab that is included in the “off canvas navigation editor” experiment. Taking the block inspector tabs out of experimentation paves the way for the Navigation block’s off-canvas editor to become the default experience.

\n\n\n\nimage credit: Gutenberg 15.0 release post\n\n\n\n

Version 15.0 introduces a new “paste styles” feature that works in a similar way to the “paste” or “paint” formatting function in Microsoft Word or Google Docs. Users can click on any block, select “Copy block” from the menu in the block settings panel and then paste those styles onto another block using the “Paste Styles” menu item.

\n\n\n\n\n\n\n\n

When using this feature, users may have to give the browser additional permissions in order to read from the clipboard. If permissions are denied, Gutenberg will display a warning snackbar to notify the user.

\n\n\n\n

Another major feature in this release is the ability for users to give blocks “sticky” positioning on the page. This will keep the block in the viewport even when scrolling down the page. The sticky/fixed positioning sticks the block to the top of the direct parent block. It can be previewed on the frontend and equally as well inside the editor.

\n\n\n\nvideo credit: Follow-up tasks for Sticky positioning\n\n\n\n

Gutenberg contributors concluded that although sticky positioning will be valuable for headers, footers, and creative instances, it is not likely to be used frequently. For this reason, it is de-emphasized in the UI. This is the first iteration of the sticky positioning feature, and contributors are tracking a list of follow-up tasks to improve it.

\n\n\n\n

A few other important changes in this release include the following:

\n\n\n\n
    \n
  • Edit block style variations from global styles (46343)
  • \n\n\n\n
  • Constrain image sizing to the width of the container (45775)
  • \n\n\n\n
  • Allow resizing the Site Editor’s sidebar and frame (46903)
  • \n\n\n\n
  • Activate copy/cut shortcut in the site editor (45752)
  • \n
\n\n\n\n

If you want to take advantage of these new features before they land in WordPress core, you will need to have the Gutenberg plugin installed. Check out the 15.0 release post to visually explore the highlights with more videos and links to all the pull requests for the release.

\n\n\n\n


\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 21 Jan 2023 00:37:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"Post Status: Launching a WordPress Product in Public: Session 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146618\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://poststatus.com/launching-a-wordpress-product-in-public-session-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:77120:\"

Corey Maass and Cory Miller go live to discuss the creation and launch of a WordPress product they have partnered to build. Crop.Express originated as a solution to a common problem Maass experienced. Miller loved the idea and wondered how to build this into a plugin to solve problems within the WordPress workflow. This is a candid conversation about the evolution of partnering to develop a WordPress product.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

In this episode, Corey Maass and Cory Miller discuss the origin of the WordPress product they are creating. Together they explore the benefits of partnership, the challenges of being a creator, and what it takes to build viable solutions. This is only the beginning of their process and partnership, but it’s loaded with experience and insight from the journeys they have had within WordPress that brought them to this moment, as well as takeaways they’ve discovered with their new undertaking.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • The Power of Partnering: Many entrepreneurs aren’t interested in partnership. But they create an opportunity to own and contribute the things you do well alongside someone who has other skill sets, strengths, and experiences. Partnerships offer space to practice open dialogue while showing respect and gaining perspective. They are a great solution for all the things you can’t do, don’t want to do, or shouldn’t do. 
  • \n\n\n\n
  • Build for a Need: Sometimes we create things believing we have brilliant ideas that will attract an audience. But where problems exist, so do the needs for solutions. You can trust if you have a problem, other people likely have the same problem and need a solution.
  • \n\n\n\n
  • Look to Make Things Easier: When you have to go out of your workflow to do a task, things feel frustrating and clunky. Finding ways to integrate tools within our natural workflow adds tremendous value to the user experience.
  • \n\n\n\n
  • Products Require Passion and Capacity: Yes, you may have the ability to create really cool, helpful things. But if you lack a sincere passion for the products you build or truly don’t have the time they require, they tend to fall flat somewhere along the way. You tap out at the end of your skillset or energy, and even though there may be real potential, the passion and time to carry things forward are missing.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n

\"🙏\" Sponsor: GoDaddy Pro

\n\n\n\n

Manage your clients, websites, and tasks from a single dashboard with GoDaddy Pro. Perform security scans, backups, and remote updates to many sites on any host. Check up on site performance, monitor uptime and analytics, and then send reports to your clients. GoDaddy Pro is free — and designed to make your life better.

\n
\n\n\n\n
\n\"GoDaddy\n
\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

Cory and Corey Episode 1
Cory Miller: [00:00:00] Hey everybody. Welcome to a cool series. Uh, my friend Corey and I have been talking about it for a couple months, a project, and we said, Hey, why don\'t we just broadcast this out, do it in public. And so this series is kind of called Launching a WordPress product in Public. This is session one we\'re gonna talk about.
First. I\'m gonna let Corey introduce himself in just a second, but we\'re gonna talk about the agenda is, um, kind of where we\'ve been, just to catch everybody up. And then second part, we\'re gonna talk about next steps for what we\'re doing. And we\'ll of course describe the project, uh, as we go. So, Corey, I think people know you, but let\'s, let\'s, uh, go ahead and share it.
Tell us more about, uh, who you are, what you\'ve done with WordPress.
Corey Maass: Of course. Uh, so I\'m Corey Moss, currently [00:01:00] residing in the northeast of the United States. Um, I\'ve been a developer and an entrepreneur for 25 years or so, and largely locked into the WordPress space for 10 years or more. It was the day job for a very long time, and I was pushing SaaS apps or BU building and pushing SaaS apps, uh, in evenings and weekends.
And then, I don\'t know, years ago at this point, I went to, uh, WordCamp in Atlanta, Georgia, and met a few WordPress entrepreneurs, including the, um, specifically the Ninja Forms guys down there. And suddenly a light bulb went off of like, oh, there\'s, you know, there\'s a lot more to WordPress products and the WordPress ecosystem than I realized.
And. It can be used to build SaaS apps, which I also do. Um, but [00:02:00] also these plugins that can be grown and built into pot, you know, sometimes, or potentially into, into businesses under themselves. So that really kind of got me started. And so, uh, around that time, I, I learned about the Post Status community.
Uh, I\'m, I am wearing the Post Status t-shirt underneath. It\'s just too cold. Um, being up here in the northeast. But, um, yeah, so it\'s been, you know, fun to be part of the community and fun to grow. Uh, I\'ve now grown and sold a couple of businesses or a couple of WordPress plugins. Um, and here we are about to launch.
Cory Miller: Yeah, I, I\'m trying to remember back when we actually met Corey, but I knew you were like this developer who loved to like launch stuff and you had the kbo, uh, plugin at that time. Mm-hmm. , and I remember talking through that and how passionate you were, you were about it. Um, so, and then we chatted the last year or so comparing notes and I\'m like, man,[00:03:00]
Corey and Cor, sorry, the broadcast system went off on my ears. Excuse me. Just one second. Okay. Whew. That was weird. I\'ve got hearing aids and my phone comes through and I was like, emergency broadcast system. Mm-hmm. Um, but anyway, um, so it was fun. We\'ve gotten to kind of get to know each other over the last year or so and member huddles and you shared this thing you were doing and I\'ve followed up and I was like, I need this, I want this.
Um, and it\'s funny too in parallel is how much stuff that we\'ve got in common or things were stages of life we\'re, we\'re going through. And so I think it was a couple months ago you mentioned on the huddle or, and then we started talking about it in Post Status dms, the project that we\'re launching in public today called Crop Express.
But um, you wanna share a little bit about that, how you came to it? And I can add a little, my perspective on it. Yeah, of course. This was your idea. Um, and I was like, oh my God, this has [00:04:00] to exist in the WordPress. Um, I need it because I need it. And that\'s a typically if I try to keep at the user level and I\'m like, if I like something and use something, I\'m like, maybe there\'s more people out there that would need it too.
But talk about the start of Crop Express.
Corey Maass: Well, before that, I want to fill in a couple of blanks. One, yeah. Uh, you and I met when you were the keynote speaker at, uh, what was it? Word? WordCamp, y\'all. The, the WordCamp in Bur Birmingham, Alabama. I have lots of friends in. Birmingham, England spelled the same, but pronounced very different.
So I have a hard time pronouncing Birmingham . Um, but anyway, um, I was living in Nashville at the time and drove down and uh, that\'s you And I went to lunch with a couple of other people and I, I, I must have had too much of the free coffee, cuz I remember talking your ear off while we were waiting for like barbecue or something [00:05:00] and then, You turned to me at one point you were being a very good listener, I have to say.
And then at one point you turned to me and are like, aren\'t you speaking in like four minutes ? And I looked down and realized that yes, indeed, my session was starting in minutes and I still hadn\'t gotten my food. Um, and so you and the folks we were with were nice enough to bring me my food halfway through the session.
Oh, chicken and waffles. I got chicken and waffles, the weird things you remember. Anyway, . Um, but yeah, I, you and I have, uh, kept in touch over the years and then, um, I think mostly caught up over on the huddles. Um, but I, I mean, I tell that cuz it\'s sort of a fun story and a little background, but I also, I think it\'s.
It\'s a great ex, uh, example of the longevity of a lot of the relationships that I\'ve had in WordPress, in the WordPress ecosystem, the [00:06:00] WordPress community. Um, you know, once in a while I, I get approached, I know you do too, of people who are like, you know, let\'s partner, or, I see you\'re doing a thing, let\'s do a thing together with no background, no context.
Um, and I, I\'m definitely not saying that people shouldn\'t reach out, always reach out. You know, you never know what good is gonna come from, from reaching out. Um, I love that people messaged me directly on Twitter and um, and in Post Status and stuff like that, but also, you know, the long-term. Being part of any, uh, any, uh, being part of the WordPress community and culminating these relationships and staying in touch with people over years.
Cuz at this point, I lived in Nashville like eight years ago, so you and I met eight years ago and I don\'t think talked really for five years Anyway, so that was one of the things that jumped out at me. So getting onto Crop Express. So yeah, I. I built a, [00:07:00] a conbon plug in a few years ago, sold that, um, have launched and been running a couple of others.
One I\'m about to sell. Um, and, and that might actually be something to talk about at another time because I, I built it because I could, um, very typical developer. I built it because I could, but I was never really passionate about it. And so at this point, I\'m, I\'m talking to some folks about, um, selling it because I\'ve just never been able to, man, I\'ve never been able to market it, meaning I\'ve never been able to make myself market it.
Um, and plugins and these businesses, to me are still side hustles. I\'ve never been able to grow them large enough to be the, you know, my primary source of income. And so I have clients and. Right now, I\'ve, I\'ve got clients who run, uh, a couple of pretty big sort of magazine style, pretty traditional blogs, but they\'re, you know, magazine style, full, beautiful, well-written, professionally written articles and [00:08:00] stuff like that.
And they are not technical at all. So they\'re, they\'re entrepreneurs, they\'re writers, they\'re content people. Um, but they. It\'s not that they don\'t understand, they\'re very smart people, but they\'re not experienced with, or they don\'t think in terms of like, oh, all images need to be squares, or all images need to be 16, nine, so that the site looks uniform and consistently good.
Um, and no matter what I did, I, I couldn\'t make it easy enough for them to crop their images consistently. I didn\'t want to get them into Photoshop, you know, other, and that cost of Fortune. Other free editors cost money, da, da da. So anyway, um, almost on a whim, over a weekend, I bought crop.express, the domain.
Um, Here\'s a industry secret. One of, one of my best kept secrets is the.express, um, what is it? Top level domain, [00:09:00] TLD. Um, there\'s so many words that have not been bought yet, so I actually own poll.express, crop.express reply.express. Um, screenshot.express is another project I\'m building out. Um, so if you, anybody listening, if you\'re looking for a good domain, I, I highly recommend it.
I keep wondering what I\'m doing wrong or like, are there companies that can\'t access this or something, you know? Yeah. But
Cory Miller: anyway, um, I think it\'s a hallmark of any, uh, tech entrepreneur in particular is to have like a too big of a. Portfolio that you have. That\'s very continuing. Well, that\'s too, yes. Um, I, I\'ve got way too many, um, my wife is always like, you should put some parking pages on this.
And I go, yeah, but it\'s a cool domain. What happens? I think there\'s two things. Uh, we definitely should, and we\'ll be talking about partnership along this whole way. Um, I\'ve had a good amount of experience with partners and like having [00:10:00] partners. Um, it\'s an anomaly in, in, I in a lot of the entrepreneurs I\'ve talked to is a lot of successful entrepreneurs go, no way.
I\'m not gonna partner with anybody. And I go, well, I kind of need to and want to. Um, but then, so I know we\'re gonna be. Some thoughts about the partnership and that\'s another thing is partnering in public is probably the subtext to this too on. Um, but as we\'ve talked, just real quick before we get back to the product, is, um, I\'m not a developer.
I should get the shirt. I\'m not a developer. Um, but I love products and I\'ve had a product business. Um, tried a bunch of products. I told you, I think yesterday I was like, my, my win rate is probably like in the one hundreds, uh, percentile. Um, we talked about baseball and I was like, you know, I\'m probably a strikeout king because I feel like I failed quite a bit.
But coming to someone, like it\'s an ideal match for me because I can, [00:11:00] you know, business and marketing, but it\'s not one you have to own in this partnership. I can own that and you contribute and obviously I can\'t even try to write code. Um, but I can contribute with product and, and experience and thoughts like that.
So now to the crop express. . Um, so when you shared this, I was like, yes. Because my experience in just talking about the user profile, I\'m so keen to the user profile cuz sometimes I think we come at it artificially and go, I have an idea. Let\'s go find a person for it. And I think some of the best ones come out of just, there\'s a need, and we talked about this, it\'s like, um, you hear the story is build it for your own itch or build it for yourself and all that kind of stuff.
We talked about Pi, PIP and Williamson yesterday, like he\'s a, he\'s the one I think of it\'s like, build it, build something for a need. Mm-hmm. for himself and grew into this great, uh, business called [00:12:00] EDD. Um, what struck me about this is I go, I have a. Like trying to find software that will crop, you know, I used to use, I was an early user of Photoshop, but I don\'t have Photoshop on my computer.
And I\'m like, well, I go to Mac preview and crop and export it out and then try to upload it to WordPress. So instantly I go, I need this. And then I thought, and we started having these discussions. I think other people do too. You know, the classic example I have just like your clients is my mom built a her own site about 10 years ago or so.
And we had a theme, don\'t cringe too much, but a theme that had rotating images in it at the top. Sure. And I tried to load the site . It was like, oh my God. She had 15 images all at like hop resolution. And this is something real quick. Uh, we both were like, this isn\'t something easy. It may be in WordPress, but it\'s not easy in WordPress.
And [00:13:00] my natural question was, If I have this problem, I bet you a lot of people have this problem. We talked, talked about images, we talked about agencies that turn sites over to clients and end up, why is this so slow? Or why isn\'t, you know, why doesn\'t this work? Right? And it\'s like, well, you loaded it native from your phone, , uh, the pick.
And so that was the thesis for me, for the, for the product is you already had the SaaS solution. I was like, yes. My question was, can I get it into a plugin where it\'s inside WordPress in my workflow?
Corey Maass: Yeah. And, and you helped, helped me turn that corner, honestly, cuz I, in a weekend I built. crop.express, which right now the website is the website.
It\'s exactly the first version that I built. Um, it\'s, it\'s not complicated. It\'s not well thought out, too well thought out. Like I have a, I\'ve been also working in product for years, and so I, I do [00:14:00] okay with going, oh, well, this, this will be intuitive enough that somebody could muddle through it. Um, but I really wanted to just solve the problem initially for my clients and yeah, threw it online.
I love doing this anyway. Start showing it to people, showed it to you, um, and you kept, you, you nudged me a couple of times in Post Status, like, how can we make this easier? And originally I was not thinking WordPress plugin, surprisingly. Um, I was thinking more. This is just a, a great little tool that people will use and it will hopefully, you know, maybe I could throw some ads on it or I, it will refer them to my other products.
Um, and so I was building a little Chrome extension and, and you\'re like, okay, that\'s a start. But you know what, if we really start to explore this and yeah, the conversations kind of flowed from there.
Cory Miller: And my premise with products, [00:15:00] particularly with WordPress or any tool is this, there\'s a workflow we all kind of have and you get in this system and when you have to veer out of that workflow, cropping an image, finding, cropping an image.
Yeah. So clunky within WordPress, and you have to go outside of that experience. You just added unnecessary time and energy for something frustration. When most times when I\'m creating content, I go, I want to get this out and edit it and press publish and put it out in the world. And anything that slows me down is a problem.
Um, So, you know, there\'s , our featured image on Post Status. I\'m not happy with it. We\'re still working on, on some of our design on the Post Status website. Uh, my personal side, I don\'t typically use images because of this. And so I think that was some of the, my, my perspective is like, there\'s enough use case here to say let\'s try it.
And I think what you and I go is like, we want to have, we wanna do something that is practical and useful [00:16:00] and then see where it goes. Um, we\'re not looking to get like mega rich on this or anything, but like, it\'s something we both have an interest in. Let\'s see where it, I\'m counting on it, man. . Hey, it would be nice to get me wrong.
Corey Maass: We, uh, we bought the Mega Millions ticket last night. You know, it\'s over a billion, but, uh, it hasn\'t been announced that we won this morning. So, you know, this is, this is the, the next best
Cory Miller: thing. Right. Yet, you haven\'t won yet. When we get some of that, carve off a little bit of that lottery money and we\'ll throw some, we\'ll do some cool, cool products.
Um, yeah. I, I\'m really addicted to products. I\'ve loved it for the longest time. Um, you said something earlier, you said I could build this and you did build things. Mm-hmm. , but the second part I wrote down was so interesting because it\'s, my experience too is I wasn\'t passionate about it. And I know when I\'ve gotten, um, those, that equation wrong is where I\'ve really failed miserably.
Um, the project I think about at Ithe was [00:17:00] called Exchange. It was e-commerce. I was passionate about a user experience that anybody could use, but I wasn\'t as passionate about the field. We just saw a big. I saw a big market potential there. WooCommerce was out there. It was the big, still is, the big behemoth.
And I go, man, it\'s really tough to like just create a new product in WordPress or, or in WooCommerce. Let\'s create an easier path to do that. Um, that didn\'t work. We didn\'t do it. And I think part of it was, I wasn\'t supremely passionate about the, the domain we\'re in. When we talk about this, I go, I have a, I have a lot of experience with images and cropping and content that\'s bulk of my career before I, themes and Post Status was, and communications work and newspapers, journalism.
And I\'m like, you know, it\'s a factor. Everybody wants an image on the site. And so what we decided was to start with the featured image [00:18:00] cropping, that making that experience, um, really smooth and easy.
Corey Maass: So that\'s the, yeah, I think the other thing to talk about here is as a developer, as a human being, I\'ve learned this lesson.
It\'s, it\'s just cuz you can doesn\'t mean you should. Um, and for I think people like you and I, I\'m speaking for you, but I, I hope I\'m right. We, we get excited about a lot of things. It\'s easy to, to dip a toe into a lot of things. Um, but then we end up taking on too much and we get overwhelmed and everything is, you know, what is it?
Do two things and you\'re doing two things half-assed instead of one thing, whole ass. Or, you know, and we\'re never gonna limit ourselves to one thing, let\'s be honest. But having, definitely having too many things. Um, and like I\'ve. Epic trips, um, you know, which is, I, [00:19:00] I was lucky enough to do, but I came home and people were like, was this amazing?
And I was like, I don\'t know why, but it wasn\'t. And I realized that it was like, just because I had the opportunity to take the trip, like I didn\'t, I, I wasn\'t in the right mindset. All I wanted to do was be home, you know? And so just cuz I could, um, doesn\'t mean I should have. And I, I keep trying, I try to think about that when I\'m taking classes or, you know, reading books or things like that.
Um, because time is precious, right? And, um, and we can only experience so much. So anyway, all that to say, um, yeah, with other products, I\'ve definitely built them, um, just because I could. And as a developer it\'s really dangerous because like, I look at that and I\'m like, oh, that\'d be really interesting to solve those problems.
Um, and then, uh, even as soon as you mentioned a WordPress plugin, uh, I was like, okay, well we need. X, y, z we need, you know, big da da da, and, [00:20:00] and that\'s great. Like a year from now, let\'s have all those bells and whistles and let\'s have all those features and, and, you know, and expand. Um, but of course, I\'m, again, I, I, I work, I have client work and w client work and family and obligations and stuff just as you do.
Um, and so you did a really good thing where we were chatting, we scratched our heads, and you were like, well, what if we, you know, what is the MVP here? And, and even that, I couldn\'t, I was like, well, da, da, da. And you were like, okay, featured image, one thing. Let\'s just start with that. Can we, and I, as soon as you said it, it clicked for me.
I was like, that\'s, that\'s the place to start. It\'s the one simple feature that, but it will solve the problem for a lot of people, and it will exemplify the problem we are trying to solve. . And so, and, and again, for me, it, it is tough at times as a developer, all [00:21:00] things are possible. Mm-hmm. , I mean, not literally, but, um, and that\'s, it\'s powerful but dangerous and I\'m, I\'m trying, you\'re, you are being, uh, non a not a developer and having a history of using this kind of thing is immensely valuable.
Um, keeping my feet grounded. And I\'m trying to do the same with thinking from the perspective of my clients, because again, they were the ones that inspired this, so what\'s gonna solve the problem for them? And that\'s where we, that\'s kind of where we\'ve landed and what we\'re getting pretty close to being able to launch.
Cory Miller: I, I think, um, the experience you talked about is like, everything is when , another shirt we should do, when everything is possible, everything sucks. Because when you have, when you\'re in the experience, I know this and I\'ve been. Uh, led teams of developers. I get it. Like, and I have the, I guess I\'ll say a gift in this sense of going, I don\'t know what all [00:22:00] is possible and it helps focus, but I think that\'s where, again, a partner comes in.
I struggle with this in different areas, um, where I\'m like, well, everything is possible. Everything sucks. And I, I lose focus in that. Um, and that\'s something I really enjoy being able to do is like, you worry about everything is possible and I can help just to ask questions. Um, and when we\'re, we talked about the MVP, I think about that iconic, um, like cartoon of this, the stages of an v mvp, how, how you start with an MVP and grow it.
And the one I like best, it feels a lot of theory and cool, like to try to plan this out like this, but it\'s like, what\'s the skateboard version of the. Bike or whatever, you know, the product becomes and it\'s not, uh, a skateboard. And then you add a seat and then you add handle bars to the skateboard and you try to build out.
And I\'m like, that\'s cool in theory. But [00:23:00] I think what this does is, the way we thought about this was what is a, a toe in the market that does solve that problem that can grow? Um, and, you know, marketing and technical and business questions come out of this. And I just saw one yesterday, uh, I can\'t remember his name on Twitter, but I replied to him.
He was trying to think like, where does this thing go? You know, like you start with the skateboard, but well, what if we want to do this with Crop Express and that with crop, you know? And, um, a lot of times, I think some of the best products have been part of grew organically instead of trying to say this is the end product, it was responding to customer needs and opportunities and grow out.
And sometimes maybe it grew into a little bit of a mess out here that we kind of had to make some hard decisions, um, with our ITM security product there for sure. And then backup Buddy over time. Um, we saw that, but it, I think it stays close to the customer [00:24:00] when somebody goes, I will pay money for this.
You go, oh, there\'s magic there because we, we might have something here. Um, and I, we decided, and we should talk about this decision too, we decided to release Crop Express as a free plug in first on the.org repo. We\'ll be talking about that experience as we go. We\'re not there yet, but we\'re really close to releasing the v mvp V1 in the repo.
Uh, and then, but what I like Corey, is we\'ve done this in a way to give us options or paths to go. We\'re not, we didn\'t try to build the bicycle and launch that as a premium product. We said, what time resources do we have? And that mvp all that went into this conversation you and I had of like this.
Okay, let\'s come down to if we can get this point, and that\'s in the stream of people\'s workflow. You know, you\'re firing and proposed headline, okay, I need my future. You\'re gonna go over here, click feature damage. And that\'s where [00:25:00] Crop Crop Express is gonna help you. And I don\'t, you know, you\'ve been great to navigate us technically, where we\'re not gonna hit a dead end on something.
Um, but that\'s the part of this adventure. You never know where you\'re gonna go with it. Right.
Corey Maass: And I\'ve, uh, you know, we\'ve already touched on a, a bunch of things that I see questions about all the time, like part of the MVP. Uh, I\'m, I\'m a, I\'m a good developer, but I have very limited experience with Gutenberg, um, excuse me, the block editor.
Um, and even, and so we, we are looking at doing a custom block down the road, version 1.2 or whatever. Um, but even to get, uh, just the, to, to work with just the featured image. Like I didn\'t have experience with the panels, uh, inside the block editor. And so I looked at it, I hacked at it for a [00:26:00] little while, and then I said, okay, you know what, I\'ve got a buddy who can help me out with this.
So, hired him for a couple of hours to get me over the hump. Um, you know, and so. There\'s that, there\'s again, the partnering, uh, you and I working together, um, which we haven\'t really flushed out, but we\'re kind of excited to do, um, launching something, putting something in, in the plugin directory is, is its own experience.
Um, and so yeah, I think there\'s, there\'s a lot of different things here that if nothing else, just getting that, you know, the tip of the iceberg. Um, or I\'m mixing metaphors here. But anyway, you know, just getting this thing out the door and, and starting, um, is, is where a lot of, uh, a lot of questions arise and there\'s, there\'s a lot of hurdles, you know, unto itself.
But, um, you know, I think the, one of the things that I really like about WordPress is that. It does require, or [00:27:00] WordPress plugins, WordPress products, it does require development, no question. Um, I don\'t think there\'s a big overlap yet enough of an overlap yet with like, no code products, um, services out there that, you know, people are building products against to then somehow get that into WordPress.
Um, but it doesn\'t have to be a huge lift. It doesn\'t have to be like, some of the best, um, plugins out. There are one single feature or, you know, single file, um, the, the plugin that we have so far that, that gets the featured image. Cropped and, and injected into a post is, is still basically just two files.
You know, it\'s not complicated. It\'s not this big convoluted thing. Um, I\'ve got, uh, from, you know, from a nerd perspective, like there\'s a couple of developer patterns that I\'m using, but there, there [00:28:00] aren\'t frameworks. We\'re using a library that, you know, does the cropping for us, cuz there\'s no way I\'m stepping into that quagmire.
Um, you know, but we\'ll grow from there. I mean, and I think that that\'s, that\'s the big difference. It\'s like, yes, we wanna launch something that is useful, um, and complete unto itself, but it can be, it can start as a feature and grow.
Cory Miller: How, how has this experience differed from your past product experiences?
Um, you know, you, you released, let\'s say the CommonBond different plugins on your own. I think, um, were, were similar problems and questions. That we\'ve talked about just in this, I don\'t know, month or so we\'ve actually gotten real serious about it. No, it\'s probably what, three, four weeks I think. . Yeah. Um, but like did you have similar things like that as a developer when you were doing like the combine?
Or did you just go, okay, this is what I want to build and you knew like the N V P V one V two kind of sorted [00:29:00] it out. How did those experience go in comparison to this one?
Corey Maass: Yeah, the con bond, I really, I wanted the name space. That\'s the thing that sticks in my mind. This was, you know, eight years ago now.
Um, so I don\'t, I don\'t remember everything, but we, same sort of experience. I was working at a startup and we needed a conbon solution. Um, Trello has. Rubbed me the wrong way. I don\'t know why. Um, and, and it was then that I was first starting to look at, so another, I\'ll give away another one of my secrets here is honestly, I often look for a, um, blue o, well, red Ocean SaaS solution or SaaS app that I can put into WordPress.
Um, and so with something like Trello, I was like, you know, we are, we are working in [00:30:00] WordPress, um, but we have to go over to Trello and, and do stuff. And for whatever reason, I didn\'t like Trello anyway. Um, and so that\'s part of what made me go, oh yes, if we had a CONBON board built into WordPress, so like posts were your cards or whatever, like, this makes sense anyway.
And so I cranked out a first version, very clunky and. Mostly just because I, I wanted to, I\'m trying to think if I had actually put a plugin in the repo before that. I don\'t, no. I had, I had, but years before. And so it was, it was really a new experience for me. Um, and I made all sorts of mistakes and I was listening to, like, one of the biggest ones was, um, I kept going back and forth.
Coming from, coming from a tra a, um, a, an a developer perspective outside of WordPress, [00:31:00] I wanted to do custom tables. And I was like, no. The word pressy way is you have to use the post tables. And I swear, the week after I released it, I heard an episode of, um, back when Pippin and Brad Ard had their podcast pippin\'s, like one of the greatest regrets of my life was using the post post table for e d D.
And that was like the beginning of when they were trying to release version three, which took them years to, to untangle, basically. I was like, crap. So right away I had to untangle my own thing, which thankfully only had 50 users or something, but I had to, you know, build a migration there and stuff like that.
Um, and then I think there\'s Go ahead. Go ahead, go ahead. Well just, you know, and, but there were, I I think maybe part of your question is like, There was, there were, I was solving bigger problems, you know? Um, whereas this, I think is like, I, I like, I mean, part of why [00:32:00] the, the light bulb went off when you were like, no, just featured image to start with.
So it just, it kept it focused, you know? And that\'s so much easier. Again, like I, I hacked away for a month or two months, you know, to get a working Now conbon board is a more complicated problem than, than what we\'re talking about. But, um, you know, but it, it, it was a much bigger lift to get it out the door, which I don\'t, I don\'t think is the right thing to do.
You know, you, you need, you need, especially talking about customers and clients and users, you need something. You need to get people using it as fast as possible.
Cory Miller: I, I think they\'re, I\'m seeing two paths that when you\'re launching a product, there\'s the technical path and the business path. Um, particularly if you want to monetize from it.
Um, but technical, I saw my teams for years. It was like, I, I always describe development as a, uh, an adventure and territory. You don\'t always know like, what\'s, what\'s gonna [00:33:00] come over the next hill. You could hit a swamp and end up drudging through a swamp or get sidetracked totally off on a minor bug. And so some of the things I started watching over the years is like, it, it\'s, it\'s a tough gig with the technical cuz you got a roadmap for potential.
You don\'t know where all the terrain\'s going cause you don\'t know where the business case is gonna come from, the use case. Um, and I just think it\'s like a blind expedition oftentimes. Like, so what we would do is, and we\'re doing this now too, is just kind of check in and see how we\'re going. And I valued having someone else external watching to at least kind of keep track.
And then I\'ll say this on the business side. Same thing. There\'s potential here. I see potential here from a business, business case. I don\'t know what it is. I\'m not even gonna be foolish enough to try to predict, but there\'s something here, I think. And um, because I don\'t predict anymore, by the way, Corey, because I\'m wrong most of the times when I try to predict, [00:34:00] oh, this is gonna be $20,000 a month, you know, MRR kind of product.
Yeah. I go, there\'s maybe a hope for those things, but I never predict or promise because if I get too mired in that, I start to get too f a little bit off of focus. Because some of the questions we\'ve talked about is, okay, free plugin, what do we do there? We felt it was, at least for our collaboration here, partnership, we want to do this.
We want this in the world, you know? Um, we think though putting it in the world has the potential for something that could grow into. Something We don\'t even, but I, I say this cuz we, we said, I love every time you say something like, Hey, I think we should do this. I\'m like, right on. We should be honest. We should be authentic and share the experience.
I think too, oftentimes in business and stuff, it\'s like, this is the way I felt when I left eye themes is like the pressure real or unreal. Hey, [00:35:00] Corey did this, oh, what\'s his next thing gonna be? And I was like, she, uh, let\'s see here. Um, I don\'t know. I followed the trail, um, and kept following that trail and trying to keep going on that trail for as long as we could.
Um, th this, I just like the fact that. One of the questions I try to ask myself before I begin any new venture or partnership is, what if it fails? What\'s the worst that can happen? You know? And what\'s great is we\'ve been talking about those things along where we manage it. I know when you hired the, the friend to help with some of that stuff, I was like, well, how much is that?
And, you know, do you need me to share it? And you\'re like, Hey, for now, let\'s just, I\'m gonna keep track of it. But, uh, to see where it goes and, um, I think that\'s healthy. That open dialogue and conversation where you respect each other, what each other knows. And know just because you\'re a developer doesn\'t mean you, you have a ton of insight and feedback [00:36:00] and perspectives to share on both business and marketing.
And, but it, it, it, I don\'t know. I see those two pasts. This is the one I\'ll tell you ahead of time, Corey is I\'ll struggle with, is when we get to the point we\'re like, okay, how much should we charge for? , it\'s oftentimes feels like this meandering thing of like, okay, and I\'ll need the same for you to go.
Sure. Hey, what if we do this? Um, because if everything\'s an option, everything sucks. .
Corey Maass: Yeah. I, so a couple of things that you touched on, like, it, this needs to exist in the world. I haven\'t found a better solution. So hiring somebody to get us over the hill immediately was worth it. And just like you said, if it, if it fails, if it never makes, uh, A dollar if you and I af after this call are like, yeah, I don\'t like you in the end it turns out, let\'s just call it, it\'s like, no, it was still money well spent.
You know, and I, I understand that I, I am in [00:37:00] the very fortunate position to have a, a little money that I can throw towards a project like this, but it\'s, it\'s very limited. And I, I think of this type of stuff as a hobby. Um, and there\'s been a lot of life choices that have gone into inclu, especially with, with my, my wife talking about like, okay, what is, if, if this is a hobby, what is an appropriate amount of money to spend on it?
Cuz there were times 20 years ago when I first started building SaaS apps that I was like, every spare dollar that I have is gonna go back into this without thinking about it. Um, because everything I ever think of is brilliant and every product I launch is undoubtedly gonna make me millions. Um, Spoiler alert.
None of it has yet, yet. Um, but uh, you know, yeah, we, we, we gotta start somewhere. Um, and, uh, I\'m with you. So I, I\'m also looking [00:38:00] forward to, like, I\'ve been, I met, it was, it was at a, it wasn\'t a WordCamp, it was like, um, what are they called? Free camp, or there\'s, there\'s conferences where it\'s like anybody can sign up to talk about anything.
Um, and it\'s sort of tech specific. But anyway, I met a young woman, uh, who was a developer and she had lucked onto a client who became a partner, um, who was an older guy who ran, I don\'t remember, an advertising agency, but he had access to an, a pool of customers, basically. And so he would tell her what to build.
and then he would sell it to his audience and they just kept cranking out products. And I was like, okay. Despite being an only child, and despite my first instinct being to do everything by myself, you know, there are things that I can\'t do. There [00:39:00] are things that I don\'t wanna do. and, and things that I shouldn\'t do.
So I\'m happy to weigh in on, you know, as, as your owning, marketing and your owning business, I, I want to weigh in, I want to have opinions, I want to make suggestions. And, you know, I think you and I have established that we, the expectation is that, you know, we, there\'s, there\'s going to be quite a bit of overlap in our concentric circles.
Um, but we, we each are gonna own a lane, which I think makes a huge difference. Um, and we\'re also able to sort of look over the cubicle wall to the other person and say, Hey, you know, like I, I touched on earlier, just cuz I can, doesn\'t mean I shouldn\'t, I\'m. Not going to want. There\'s going to be times where I, I\'m going, I\'m not going to want to build what I need to build.
Like there\'s a feature that every client is clamoring for. You are finally confident. You\'re like, they will all pay X number of dollars if you [00:40:00] just add this. And I\'m gonna be like, yeah, but we need a dark mode or some ridiculous thing that\'s just gonna be more fun to build. Um, and I think there\'s definitely going to be points where, you know, I, we\'re essentially going to need to be each other\'s bosses.
Um, and that\'s going to be interesting and going to be difficult at times. But I, but I think good, you know, you, you, you need other people. There are people out there that are, there are exceptions to this of course, but you know, I, I think we\'ve pretty well established that both you and I do better if nothing else.
Having a sounding board, having somebody else who\'s as invested, um, you know, and helps keeps us, keep us on the line we\'re supposed to be on.
Cory Miller: Yeah. On that note too, um, the partnership side of things where I, I\'ve been in circumstances where, okay, this is Mon Lane, that\'s your lane. [00:41:00] And sometimes, like you were really good to ask me what part of the development do you want to contribute to?
And I said, my strengths through trial and error. By the way, I think my contribution strengths are u UI experience, like how things flow. Um, I obsess over there cuz I want them to be as fast as possible. Mm-hmm. intuitive as possible. Knowing some of my, probably I\'m gonna have to freshen up on some things.
And the other is I said, you gotta be careful with me because I will share all of these things that I would love to see, but we\'ve like, But we gotta put \'em on a, a feature roadmap, A backlog somewhere. Because I said, and I told you this, I said, be careful cuz I\'ll come in and go, what about this, what about that?
And what I had to tell my team too, and I told you is like, please don\'t unless I go, can we get this in the next release? Please don\'t think that. Let\'s do this right now. And that\'s the [00:42:00] idea Fairy in me is mm-hmm. . Uh, but, and so an example of that was we have a square coop cropper. And I was like, okay, I\'m introducing the new customer story here, which is my own, every, the Posts newsletter has those little circles in them for all the, and I\'m like, that is a pain in the butt to do.
Now I flag that because I go, if I\'m the, uh, kind of a typical user, I don. Know how, how to crop that, you know, there\'s tools out there, right? But like I go, there\'s an experience if, if someone has that and I go, Hey, what about a circle cropper? And then I knew you were going to like chase it , and I was like, Hey, hey, hey.
Not for this one unless it\'s an easy thing. This was that back and forth I did with Right. All the developers I\'ve worked with too is just like, please don\'t say, please don\'t interpret that as, can we do this right now? Um, sometimes I\'ll be like, can we do this right now? Because I\'ll, I\'ll feel [00:43:00] like we got something here.
Um, but then you\'re like, okay. I was like, well,
Corey Maass: it\'s just cuz you can doesn\'t mean you should. Yeah. But there\'s also, you know, you and I, I, I also get the sense, we haven\'t talked about this, but I get the sense that we both trust our instincts pretty well, um, when it comes to product. You know, and I\'ve, I\'ve been, I.
Studying product, looking at product. Um, for years and years and years, I\'ve got, you know, books on architecture. And, uh, the, one of my favorite books about, about the Bowhouse School is sitting next to me. I mean, things like this and like, I nerd out about this stuff. And so, um, I\'m not saying I\'m an expert, I\'m not trained in any way, but like, I think I like a lot of people we know, you know, I, I, I love putting, I love loading an app and putting it in front of my mom.
You know, who\'s, who\'s not trained in any way. She has [00:44:00] a little bit of an artistic background. Um, but she is a power user. I mean, she, at this point, she doesn\'t even have a computer. She does everything on her iPad. Bless her heart, honestly, because. Trying to book tickets or, you know, I mean, things that she does on her iPad, I, I didn\'t think possible, um, even, which really is just in a browser and, and her fingertip, you know, but gets an unbelievable amount of stuff done.
But I love putting things in front of her and saying, you know, show me how you would muddle through this. Um, and, and anyway, so all of this to say that I, I trust my instinct a lot of the time, um, when, when somebody mentions a feature to me of like, oh, this is worth doing right now. Even if it, yes, it\'s not mission critical, you know, we haven\'t released yet, so technically any feature other than one feature is, is enough.
But I was like, not only [00:45:00] do, is there not a image cropper for WordPress the way that we want. Out there, but I really don\'t think any of \'em do circles. And again, my clients for most of their stories featured images are 16, nine or square. But for whatever reason, there\'s that, that now that browser pattern where avatars people are circles.
And so, you know, let me see if I can, I can crank this out and it\'s, and it\'s fun. Um, and sure enough, like, like you said, it, it wasn\'t a big lift, but yeah, I think, I think you and I will, we\'re just gonna have to figure that stuff out. Like everything, everything goes on a backlog. Everything gets discussed at least a little bit.
Um, but I also, you know, I don\'t, I don\'t think that there\'s harm in, you know, there\'s low hanging fruit, there\'s return on investment. There\'s lots of different ways to put it. [00:46:00] It\'s like, oh, well if we, you know, if we make all the buttons green, you know, is it, does the user benefit? No. You know, so just cuz it takes a minute isn\'t worth it.
But, you know, we\'re, we\'re just gonna have to, and, and I liked what you said too, of like, we, we are gonna have to, I guess this is the other, the other benefit of trying to get this thing out the door is like, get people using it, talk to people using it. Um, you know, being part of a, a community like Post Status, um, there\'s the great, um, advanced WordPress Facebook group.
Like there\'s, there\'s places that. You and I have been involved for a long time, kind of regardless of, of our actual position within those communities. But, you know, trying to add value or trying to Twitter to trying to just, you reply to tweets for months and then you hope that when you, you do something and you need somebody else to reply that, they will.
So it\'s like, let\'s get this thing out there. Let\'s see what people think. [00:47:00] Give it a try. Um, you know, and, and follow, follow our.
Cory Miller: This is where I struggle back and forth with product. But my typical mo, what I feel instinct is you, uh, there\'s product people that are just genius and gifted. They\'re like, here, you know?
And you\'re like, God, okay, cool. Uh, but for mere mortals, um, for me it\'s been put something enough out there, check some boxes. Okay, is this something you think we need? Like, does anybody even need it? Because I put those things out there, I\'m like, put \'em out there. Not necessarily products, but other things.
I\'m like, nobody\'s even asking for this. And a lot of the entrepreneurial books and stuff, it\'s like, okay, how you scientifically go down it? And I go, it\'s art and science. Yeah, it\'s a blend. It\'s this alchemy and magic of like, but I know the power of like putting something out there and that creates enough a ripple where you get a feedback loop and, um, [00:48:00] That was so helpful along the way when you get feedback like, I, I, we feel this is a good, this is a good V one, solve somebody\'s problem, that laser beam, you know, thing of what we\'re doing for it.
Um, but what I\'m most looking forward to the product is how people react when you hear those. Like, um, backup buddy was in development, uh, and then, I can\'t remember, 2009, 2010, and I, we were at, we had a little group thing where, and this, these two twin brothers ran an agency and I just, this wasn\'t something somebody told me.
I was just like, Hey. We\'re doing this thing and this plugin, and it helps you do, um, basically, uh, backup, restore, and migrate websites. By the way, those were not things that came from me. They came from Dustin Bolton and Christine and I themes, they\'re like, no, a backup needs to do these three things. Okay, okay, let\'s do it.
Sounds good to me. But I mentioned to them [00:49:00] the migrate, or what was it? The migrate side and just in passing, and they, their eyes lit up and they go, we pay somebody $300 to do, to do that now. Wow. Consider the time and everything. This is back in the day. And I was like, okay, I think we got something.
Because, you know, and then we just try to, okay, I think we\'re gonna keep going, keep doing, we obviously launch it, we\'re gonna launch it no matter what. But um, that\'s where I was like those moments where someone lights up and they\'re. Can I pay you now? The shut up pay, shut up. Let me pay you thing. Right? I was like, shut up.
You can take my money. Shut up and take my money. That\'s a magical moment. Um, I think times I\'ve tried to force it, um, and it\'s just, it\'s not, or create a category you hear that\'s not, and I\'m like, cool. Yeah. For those a hundred people out there that have that insane genius to create a category, most of us stumble into it.
Right. You know, um, the garage stories for startup [00:50:00] stories are always make me laugh. Cause I\'m like, what was the background? What was the context? I\'m like, that\'s a sexy headline. We started in a garage and here we are, apple. I\'m like, that\'s a sexy headline. Don\'t, and I like it. Don\'t get me wrong, but I\'m like, what Were all the actual moments, the places you got phenomenally lucky.
I know there\'s a big part of mine luck and every time I\'ve tried to time it and like, okay, I\'m gonna ride this thing, it just hasn\'t worked. And that\'s why I really like her direction with this. Um, Because we kind of had a fleeting thought of like, I think as I recall, like this could be a paid product.
Um, you know, I don\'t even know if we entertained much of starting with a paid, we\'re like, let\'s just do the free plugin. And I will say, remember actually, um, give you credit for this too, is I think I said, what about a Gutenberg block? Put it in editor. So upload image crop, boom, I\'m there. My workflow\'s fast, efficient.
And, [00:51:00] um, you, you looked into that, you chased a little bit of it and I said, Hey, there\'s some roadblocks here. And that\'s that collaboration of how we go, okay, featured image, what if we started right here? We want to grow potentially into that. You know, I think the idea in this, and we\'re, I think we\'re both verbal processors, but is the thesis is start here and it\'ll grow into.
Block, like the inline process where you\'re in the thing and you\'re having the same problem, I need to crop it, figure out right. Dimensions and all that. Um, so I don\'t know where I was going with that other than to say that was some of the background too of decisions and knowing like you could hit a dead end.
And I\'m waiting for that. I think we\'re putting ourselves out there with this to see if there\'s magic in this. Yeah. Journey.
Corey Maass: Yeah. A couple of things you said, um, stuck out to me. One is [00:52:00] like a lot, everybody builds products differently. Everybody b builds UI differently. WordPress has very soft wall, has a lot of walls, but they\'re very soft and there\'s a lot of discussion, often negative, often complaints about, um, The, the experience that a plugin provides.
And I think what\'s different about WordPress, right, is like often you\'ll, you\'ll go to Trello and you interact with Trello, and you go to Slack and you interact with Slack in WordPress, you\'re essentially interacting with numerous apps, really numerous UIs, side by side. Um, and the tolerance for terrible ui.
I mean, let\'s be honest, even WordPress is not great anymore. Um, the tolerance is high for what you can [00:53:00] get done. Uh, and so I think that that\'s, that\'s an, that\'s something that I hadn\'t really thought about, but it\'s like things you can get away with in WordPress as long as you can solve the problem. And so there\'s, there\'s a lot to be said for, bless you.
There\'s a lot to be said for. Solving the problem, um, and not getting caught up in the genius of a product. You know, cuz like you said, people, people wanna get it done and get out, you know, get on with their lives. Um, the other thing that I\'ve had a lot of luck with, so I think we should do this here, is talking about that feedback loop.
Um, with Conbon, I put myself on the homepage and had a, and, and had a nice. Response. Um, with, uh, there\'s an online game that I built during the pandemic that, that I\'ve told you about, um, called Mexican Train [00:54:00] in the web websites, Mexican train.online. So if anybody out there wants to play Mexican train, which is a Domino\'s game, but I built an online version, um, I put myself on the homepage and it\'s a game that is played by a lot of seniors and especially during the pandemic when everybody was really locked down.
And then even now a lot of seniors are still trying to stay inside, stay safe, stay more isolated than they were before. Um, and isolated being the word. They use the game to keep interacting with their friends, um, which is just amazing. Um, but they. Not only does every email that come in start with, Hey Corey, because I am on the homepage.
Um, but apparently when, like, there, there are groups of people that play every week and even every day and uh, they curse me when they get bad dominoes. They praise my name when they get good dominoes. Um, the picture is of me [00:55:00] eating cheezits cuz it\'s sort of as a joke, like, Cheezits are a guilty pleasure for me.
So a number of them actually like, go and buy Cheezits and eat Cheezits while they\'re playing because it\'s become a, you know, uh, a thing. Um, inside joke I guess is the, you know, uh, or whatever. Um, but there\'s the, that feedback loop is definitely there. Like, they talk to Corey, you know, and then even with.
Subsequent products that I\'ve built, me being on the homepage with a blurb about like why I started the Solve the Problem and stuff like that, has made a huge difference. And so I think as, at least early on, that\'s something that you and I should definitely replicate is, you know, as we\'re se I mean, we\'ll we\'ll send this to our friends and family.
Okay, that\'s easy, that\'s obvious. But, um, you know, maybe even building in a mechanism that\'s like, you know, Hey, it\'s your favorite. Corey and Corey, like, tell us what you think. What do you, you know, um, does this work for [00:56:00] you? Does this not work for you? That kind of thing. I usually don\'t think about explicitly collecting feedback until further down the road.
Um, usually wanting to focus on like paid customers and that kind of thing, but, you know, maybe it\'s something we start with sooner than later.
Cory Miller: I definitely think so, because, you know, so many times I\'ve put products out there and not really made that splash. Like, you know, they\'re like, okay, there\'s practical, they\'re doing this thing, um, that we set out to do, but I think you wanna have push, push it to have an opinion.
Mm-hmm. , you know, like the user to have a reaction to it, enough to say it sucks or it\'s awesome. Um, some, some way of that to see where you\'re at. I think both if you get it sucks and it\'s awesome. You\'ve got some validation there, you\'ve got something. Um, but putting things out there, that\'s [00:57:00] how I, my mo with products.
So 2006 or seven I think I, I launched, I did launch, I guess, uh, this is way back in Word Press was different, but I launched a theme and put my zip file. Uploaded it to.org. People downloaded it and I was like, this is crazy. I got a response from them, which I had a contact form up , you know, my website linked in the theme and stuff, and they\'re like, will you build blog for me?
And I was like, whoa. I\'m learning. I did this too because I wanted to do it and I\'m learning. But that\'s the magic that when you put something out there. Yeah. But I think there\'s this case for put something out there that kind of pushes a reaction. You know,
Corey Maass: and I think this will be an interesting point of conflict potentially, is uh, there\'s going to be a point where.
We\'re, we\'re going to see different paths and we\'re gonna want different features too. And so I think this is, that\'ll be an [00:58:00] interesting, you know, let\'s try to have that conversation on camera because it\'s there. There\'s points where I\'m dogmatic, like I\'ve got my, one of my other plugins is like, like I said, I, I often look at products that are out, out on, out in the wild and I repurposed them inside WordPress.
And so I\'ve, I\'ve got a plugin that\'s kind of like a link tree or a card or an About me where it builds very simple social focused landing pages. Like the link bio pages is kind of the, the phrase most people think of. And uh, and even like when I submitted it, the, the people reviewing the plugin were like, um, you\'ve kind of built WordPress inside WordPress.
And so I still get a lot of requests for features that are beyond. The point of the product, because it is within, like WordPress using the right theme or page builder, you can do literally anything. [00:59:00] So this is supposed to be very focused and people come in, come, come in and are like, well make it do this.
And I\'m like, that makes no sense. Like, go use WordPress. Um, and so I have found myself being more and more dogmatic about like, my own vision or, you know, certain vision for a product. Um, you know, and right now, like you and I have it easy, like we know it, it it\'s a one trick pony or one and a half if we do circles.
Um, you know, so what\'s, what\'s the next thing that I think that\'ll, and, and, you know, in a year down the road, I think that\'ll be interesting. Um, again, that, that backlog, you\'re probably gonna end up hearing more feedback than I am. Um, you know, uh, Product ownership might ha end up being a thing that we, we actually have to sort out.
So, and it\'ll be an interesting ride.
Cory Miller: Well, that\'s been a lot of the background, um, that we wanted to share and kind of catch you all up since we were, were launching [01:00:00] this live or in public. Um, but catching you up on some of the background, some of those key conversations. I hope people can use some of this to, uh, inform their own product journey.
Um, where we are today, where are we today, Corey, with the actual product? Sure. Um,
Corey Maass: yeah, and I just to add to what you just said, like as people watch this, there are a few people watching live. Um, my expectation, like most things recorded is, you know, more people are going to watch it on the playback. Um, but we are going to.
Looking at comments, and I think both of us are pretty easy to find. Um, you know, so, so as, as the, as the conversation gets started, you know, I encourage anybody listening, please ask us questions, you know, give including hard questions. You know, what do you want us to talk about? What do you want? What questions do you want our answers to?[01:01:00]
Um, not that we have the answers to all these problems, but you know, this is, we\'re doing this out loud, recorded on the internet, you know, so we\'re happy to talk about it. Um, and we\'re both pretty candid out, outspoken kind of people. So we\'re, we\'re happy to talk about prayer, pretty much anything. Um, but anyway, where are we at now?
Um, so I, with, again, with the, the help of a freelancer built, uh, a first version, I did the p h P. Um, he helped get the. JavaScript and React part of the, um, panel inside of the block editor integrated. Um, and then I took the, the cropping library that we\'re using, stuck that in. Um, and we\'ve, we\'ve gotten pretty far with that.
The, what, what we had been limited to for the last couple of weeks [01:02:00] is the selecting of an image. So, you know, nobody\'s, nobody\'s seen this yet. So talking through the flow real quick, you\'re opening up a, a new post in WordPress. There\'s, you know, the built-in featured image panel on the right. Um, we\'re essentially replacing.
It looks very similar to the built-in one intentionally, but when you click on it, instead of it opening the media library where you upload an image or select an image, it uploads a, uh, or excuse me, it opens a modal where it says What shape do you want a crop? Um, it does say, do you want a circle? Um, you select an image from your hard drive, it then opens the crop.
And one of the nice things about this kind of tech is that that image is not uploaded yet. And so it\'s all just in the browser until you say, okay, set this, you know, I\'ve moved the crop. I want it this part. Set that as the featured image and that\'s what gets uploaded. [01:03:00] Um, as of today, I got a poll request again from my freelancer who helped me get started with the media library, cuz this is the one thing.
I\'m, I\'m undermining you here, but you said, I really want circles. To me, I was like, that\'s a differentiator. We need circles. Um, to, from my perspective, I\'m saying also we need very basic media library integration. I think you originally suggested this as a nice to have, and I was like, no, you\'re right like this.
To launch with, you need to be able to select an image that has already been uploaded or select an image from your hard drive, crop it and set it. Um, and so we\'re, we\'re pretty much there. The media library is opening and you can select an image. Um, so I need to do a, a couple more hours of development, I think, to get it so that it\'ll save that essentially re cropped version of what is in your media library.
Um, [01:04:00] and then from a d a product standpoint, we\'re pretty much ready to go, um, on, on your list. Um, I know we have the readme.
Cory Miller: That\'s, it was like, Hey, Corey, you have 15 minutes of work to do. .
Corey Maass: That\'s not true. I mean, it, it is to get it in the repo because it\'s one of those, you know, no, nobody does it if a tree falls in the wo if a plugin gets committed to the repo and there\'s nobody there to hear it. Yeah. Um, you know, or, or security by obfuscation kind of thing.
But, you know, there\'s, it\'s the beginning of the marketing. How do we describe this thing? What do we even really, what do we call it? You know, is it, is it crop express? Is it crop express image cropper? Is it image, crop express, da da da da da. Like, just, we have the domain, but that\'s it. So there\'s,
Cory Miller: uh, it presents a lot of questions.
[01:05:00] Um, and I know we\'ve run outta time, um, but it presents a lot of questions because you go, there\'s wordpress.org plugin search that is, Pretty big, right? Um, the, these are some of the things coming outta my mind with the readme because it does turn into that plug-in repo section. Um, I\'ve seen a bunch throughout the years how people like, enough there to go.
Here it is. And then my balancing act is, let\'s get enough to show this is the value proposition, this is what it can do for you. Uh, and then just like everything iterate over time. Um, but I can\'t help but tell and admit to you. I think, oh, it\'s gotta be like side bki put a plugin on the repo. Like he knows he\'s a marketer, he\'s got all these talents, but he, he understands how to put a plugin, um, and showcase it, right?
And so I\'m battling that a little bit, but I go, okay, get enough to, so here\'s the value prop and that this is an active development and we want that [01:06:00] feedback loop back about what\'s next. But I think the read me is showing. Telling enough of what we\'re trying to do where someone goes, that is a problem. I have this plugin, will will solve it.
Now getting to that is gonna be, is gonna be fun, but I started on the Readme file from the Generate WP site you gave me. And um, that\'s where I\'ll honestly spin some wheels a little bit, cuz I\'ll try to be perfect. But I think the two outcomes there really are, you know, clearly understanding what this does.
So someone, mm-hmm can go, oh, I\'ve got this problem, or my client\'s got this problem. And then second is, we need a loop. We need to know these things. Even the things you go, we\'re never gonna do. I still want to have \'em up there. I still want to have \'em in our visibility because it just allows us to make better informed decisions as we over time hone in on, you know, A lot of the products we [01:07:00] released at I themes, it was years before we go, oh, that group right there, because you get enough of big sample size and you go, okay, convert Kit had a very similar, uh, fault, Nathan Berry.
He started out with one thought in mine, and then he saw it was this creators, you know, um, economy. And then he just, when he got that bead, he just, you know, doubled down on that. And I, I see, I see that similar here. I think we have pretty good profiles, like anyone that wants to make image cropping easier, um, and faster from a blogger to an agency doing work for clients, um, that\'s a big use case for me.
And I\'m like, there\'s, that\'s why I have some faith that there\'s something here that we can do in an advanced case, but it\'s just discovery to me, you know, so.
Corey Maass: Yeah. Well, and I think that\'s part of, I, I think you should take notes on your experience and then tell me about it. The next time we have a call, like [01:08:00] mm-hmm.
you are a, apparently you launched a pro a theme many years ago, , uh, but have it since. And so when I was like, okay, you go, go and do the read me. You were like, uh, I need some guidance. Like I, yes, I can write words, but tell me more about the Read me and what are the consequences of, you know, the, what I put in the read Me.
Um, and I think that that\'s, you know, you, here\'s a prime example of your experiencing something for the first time. You know, tell us about that experience and, and, and the thinking, some of the thinking that goes into it, like, it is, it is something that gets iterated on often, but there are consequences of, uh, you know, when we submit the plugin, the slug, the u r l is going to be locked.
You can. ask them to change it [01:09:00] once within, I don\'t know, the first couple of days or something. But then that\'s it. So, you know, cuz and you\'ll, and you\'ll see that with plugins on the repo that the U R L is W P S E O, but the product is Yost, you know? Right. Or things like that. Um, things that they\'ve had to change over time, but you can\'t change the slug.
Cory Miller: I know that firsthand too . Right. I sure think security was better WP security and, and it still is. I think. I don\'t think we That\'s right. Get there\'s, yeah. So that\'s right. Yeah. There are some foundational things that can\'t change over time, which is tough when you\'re doing new products as you don\'t.
Always know where it\'s gonna go or what the right, you know, do we need to say image cropping, you know, kind of thing. Whatever the, the kind of keywords are.
Corey Maass: Yep. So, yep. So, but I, I definitely think that\'s, that\'ll be a great experience for you to talk about and, and also a lot of the, the thinking that, that it makes you do will subsequently guide at least some of our early work [01:10:00] when we do put up a marketing site.
Cory Miller: Absolutely. Well, okay, so last question. We\'ll wrap this up since we, since we got over time. Um, but it\'s hard not to stop talking with you. I enjoyed this. Um, so by next Wednesday, um, what do you think is realistic for us to make progress on and we can start talking about that next. Because we\'re gonna be doing this, by the way, for the next five, six weeks, I think.
Um, there\'s a webinar, um, that was in the newsletter, the link to that. And then of course, if you\'re watching on YouTube, you can just come back to Post Status on YouTube. But Corey, what do you think, um, our next steps are, the progress we wanna make in this week interval?
Corey Maass: Yeah. I think the goal should be either we get this across the first finish line or past the first milestone or whatever of it.
Either we submit it to the [01:11:00] plug-in repo or it\'s, or it\'s ready to go and we can talk about that. But, you know, feature, feature complete as far as version one is concerned, um, and, and that, that read me, basically it\'s the whole zip file ready to go and be submitted and then we can either, Maybe we even, we could even submit it while we\'re on the, uh, you know, on the call and kind of talk about like that.
And then I think we\'ll end up talking about like, you know, whenever I\'ve submitted plugins, um, I\'ve, I\'ve never just had one like stamp done. Like there were questions asked or there were, um, code revisions that I needed to make based on, I know that they use a programmatic, um, I can\'t think of what it\'s called, but basically code sniffer, um, to, you [01:12:00] know, it basically some little AI that, that will flag variables that aren\'t escaped or things like that.
And, um, and then I\'ve also usually wound up having a conversation with a human being who\'s like, you know, what are your intents? What, what\'s your intention of this? Or, you know, why do you think we need this? Or whatever. And so if, you know, I think that\'ll be worth talking about too.
Cory Miller: Because the submission to the repo takes some time because it\'s gotta go in the review and all that stuff too.
So, um, I think about timing wise as well as like, once it\'s there, it\'s, we\'re gonna have just by nature of the review process, which is good. I, I, I get it. Um, it\'s gonna push us out some to actual, to actual launch. That\'s something to consider too.
Corey Maass: So, you know, so we can, I think let\'s, you know, let\'s regroup, um, today\'s Wednesday, you know, end of the week, beginning of the week kind of thing.
Um, and we can. basically just hit submit. Um, and [01:13:00] I th the last I heard the review process takes a couple of days and I, that, that fits with my experience. Mm-hmm. , um, you know, so maybe we\'ve heard if we submit Friday or Monday, we might have heard by Wednesday. Um, and then we\'ll have that to talk about, you know, or we can just submit on Wednesday and then the following week we definitely should have something to talk about.
We might not be live in the repo, but um, you know, we should have heard back. I know we\'ll hear back within a week. Yeah.
Cory Miller: Okay. Well, my intention is to carve out some time today. I think I\'ve got some buckets of time to finish, to read me at least get a draft that you can review and we can go back and forth, um, to have that, at least you not be waiting on that or me, so that sounds great.
Corey Maass: Yeah, I\'m.
Cory Miller: All right, Corey. Thanks, man. It\'s always fun talking through this stuff. Yeah, having a partner and a collaborator. And, uh, thanks everybody else for, uh, joining in as you can. Um, we\'re gonna be here Wednesdays 11:00 AM Central Standard time, um, [01:14:00] for the next five, six weeks throughout January and February.
As we talk, just share the progress we\'re making for this WordPress product called Crop Express. Thanks everybody. Thanks Corey. See ya. See ya.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 20 Jan 2023 20:00:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:6:\"Emilee\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"Do The Woo Community: AI Text, Art, and Code\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74344\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:41:\"https://dothewoo.io/ai-text-art-and-code/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:397:\"

I chat with Mark Westguard from WS Form about how we have both used AI with content, art and even WordPress. With some added thoughts of AI and WooCommerce.

\n

>> The post AI Text, Art, and Code appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 20 Jan 2023 12:12:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: WooCommerce Seeks to Improve Cart and Checkout Blocks Performance\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"https://wptavern.com/woocommerce-seeks-to-improve-cart-and-checkout-blocks-performance\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3517:\"

WooCommerce Blocks maintainers are asking the developer community to share feedback on any performance issues they are experiencing with the Cart and Checkout blocks.

\n\n\n\n

“We’re aware there is work to be done in this area and we want to improve,” WooCommerce developer Alex Florisca said.

\n\n\n\n

“We’re specifically interested in any performance related issues that may be stopping merchants or developers from adopting the Cart and Checkout blocks over the shortcode version.”

\n\n\n\n

The plugin’s repository has nine open issues categorized as related to performance. Most of them are not straight forward and require more research and testing. For example, an issue with running multiple blocks of product grids was reported as having increased response times of 4+ seconds. Contributors have proposed a few different ideas to address performance issues, such as experimenting with useSuspenseSelect to improve the perceived loading experience for various blocks and finding a way to track the performance of the Cart and Checkout blocks. Neither of these tickets have seen much movement yet.

\n\n\n\n

Store owners will not be eager to switch over to a checkout experience that is slower, so the WooCommerce team is seeking feedback that will help them make the cart and checkout blocks faster. So far, one user reported that due to a bug in a third-party plugin, he got a glimpse of what the block-based checkout adds to the JS asset payload.

\n\n\n\n

“I think this adds at least ~300 kB (compressed) JS payload (initial numbers, my measurement process is still ongoing),” Leho Kraav said.

\n\n\n\n

“We don’t plan to convert our classic theme to a block theme any time soon, but still, I feel uneasy about this direction.”

\n\n\n\n

Florisca followed up on this feedback with a few cursory benchmarks comparing the legacy shortcode checkout with blocks checkout and Shopify:

\n\n\n\n
Blocks CheckoutShortcode CheckoutShopify
Total Payload2.9MB935kb6.1MB
Total Transferred2.1MB1.3MB*3MB
Number of requests14477146
\n\n\n\n

“The number of requests has almost doubled for Blocks, which isn’t great so this is something that we can look into,” Florisca said. “I suspect the reason is because we rely on a few layers of abstraction on top – WooCommerce and WordPress, each with their packages and set ways of doing certain things. We can investigate if we can simply this.”

\n\n\n\n

The discussion on how to improve cart and checkout block performance is still open for more developers to give feedback, and investigations are ongoing. The good news is that WooCommerce maintainers are aware of how much weight the block-based checkout adds and are actively looking for ways to improve it for users.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 20 Jan 2023 03:53:58 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:10;a:6:{s:4:\"data\";s:13:\"\n\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WPTavern: WordCamp Europe 2023 Tickets Now on Sale\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141212\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:61:\"https://wptavern.com/wordcamp-europe-2023-tickets-now-on-sale\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2405:\"

WordCamp Europe announced the first batch of tickets on sale for the 2023 event that will be hosted in Athens, Greece, June 8-10. General tickets are € 50.00, a fraction of their true cost, which is heavily subsidized by sponsors. It includes admission to the two-day event, lunches, coffee, snacks, Contributor Day, a commemorative t-shirt, and an invitation to the After Party.

\n\n\n\n

WCEU is also offering micro-sponsorship tickets at € 150.00, which organizers say is closer to the real cost of attendance.

\n\n\n\n

Speaker applications are still open but will close soon in the first week of February. Applicants will be notified by the second week of March and organizers will announce the lineup in mid-April.

\n\n\n\n

WCEU is also seeking a host city for 2024. The minimum requirements are considerably less stringent than in previous years. Hosting the event is open to any team that has organized at least one successful in-person WordCamp in a European city in the last four years with a community that has been active during 2022. Organizers have also published an update to the selection process:

\n\n\n\n
\n

For this year, we have tweaked the selection process to concentrate more on the local community and the city instead of deep knowledge about how to organise a successful WordCamp Europe.

\n\n\n\n

The selection of the WordCamp Europe 2024 host city will be based on the overall evaluation of the application, instead of ranking different parts of it. We don’t ask your team to prepare a budget for the whole event, but estimated costs for the proposed venue(s) should be available.

\n
\n\n\n\n

Contributor Day registration for this year’s event is not yet open but will be free with the purchase of a conference ticket.

\n\n\n\n

At the time of publishing, only 257 tickets remain in this first round, but more batches will be released in the future. Register now to lock in your spot or sign up for email updates on the registration page to be notified of future ticket releases.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 19:37:51 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:11;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"Post Status: Interview With Product Lead Tiffany Bridge Of Nexcess — Post Status Draft 137\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146391\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"https://poststatus.com/interview-with-product-lead-tiffany-bridge-of-nexcess-post-status-draft-137/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:57251:\"

In this episode, Tiffany Bridge joins Cory Miller to talk about the latest innovations she and her team at Nexcess have created for beginner online store owners, simplifying WordPress for users, and the ongoing battles between centralization and decentralization.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

Tiffany Bridge has been working in WordPress almost since the beginning of WordPress. She is the Product Manager for WordPress eCommerce at Nexcess and talks with Cory Miller about their hosting services and products, specifically highlighting the benefits and capabilities of Store Builder. They dive into optimizing UX in WordPress, the benefits of open source, and more.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • WooCommerce Simplified with Store Builder. As you know, WordPress and WooCommerce love to hide settings in layers of menus. Nexcess saw the struggles people had in trying to set up eCommerce sites and created StoreBuilder as an easy tool to go from zero to having an online store. This removes the initial learning curve required to get started in Woo and sets up a DIY tool for merchants.
  • \n\n\n\n
  • A Platform to Grow with You: One of the great things about setting people up on WordPress and Woo as they start businesses is the flexibility available for future growth. If their model totally shifts, they can just uninstall a plugin and add another to obtain the functionality they need to sustain their business growth without the hassle of migration or the increased fees of a platform.
  • \n\n\n\n
  • Solving for What Users Shouldn’t Have to Know. Kadence and so many WordPress and WooCommerce plugins are designed for WordPress professionals. We are working to leverage the power of Kadence by creating a top-notch user experience for people who don’t know what things like a border radius or gutter are. These tools enhance and expand the power of WordPress, so creating solutions that lower the knowledge barrier to entry is the kind of work that moves WordPress forward.
  • \n\n\n\n
  • You Can Own Your Own Platform. Often people aren’t aware that this is an option. From Etsy to Twitter, controversies tend to increase demand for alternatives. Bringing more awareness to individual ownership on the web-for blogs, stores, or anything else-empowers people to show up online and conduct business on their terms.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n

\"🙏\" Sponsor: GoDaddy Pro

\n\n\n\n

Manage your clients, websites, and tasks from a single dashboard with GoDaddy Pro. Perform security scans, backups, and remote updates to many sites on any host. Check up on site performance, monitor uptime and analytics, and then send reports to your clients. GoDaddy Pro is free — and designed to make your life better.

\n
\n\n\n\n
\n\"GoDaddy\n
\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

Cory Miller: [00:00:00] Hey everybody. Welcome to back to Post Status Draft. This is an interview in the series of product people that we\'re doing with some of the great product companies in WordPress. And today I have my new friend Tiffany. Um, we get to talk a couple weeks back and I love her energy, her experience, her approach to WordPress overall. She\'s very distinguished, uh, experienced person in WordPress having done some cool stuff that I\'m gonna let her talk about. But we\'re gonna be talking about Nexus and Store builder today I think So, um, Tiffany, welcome to Draft podcast. Thanks Corey. You tell us what you do, what, what you do in WordPress now, and where, where you got to this.

\n\n\n\n

Tiffany Bridge: Okay. Well, so right now I am the product manager for WordPress e-commerce at Nexus, which is, uh, basically I kind of, uh, I have my hands in the entire experience [00:01:00] of using WordPress on our platform as a, as an e-commerce focused host. Um, that\'s a pretty wide swim lane, so I do a lot, a lot of different things.

\n\n\n\n

Um, the thing that I\'ve been focusing on is our store builder. Um, before Nexus I was, uh, I was at Automatic for a while doing, uh, I was on their special projects team, um, which works with, um, you know, interesting people and organizations to try and make sure they have a great experience on WordPress. So I did a lot of, sort of very bespoke projects there.

\n\n\n\n

Um, before that I freelanced. You know, was kind of doing what a lot of, uh, my colleagues are doing is just trying to, you know, help my clients have, um, you know, with by setting up like WordPress sites for them and things like that. And before that I was doing a lot of WordPress just kind of in personal projects.

\n\n\n\n

I started teaching myself WordPress in 2004. So, um, I\'ve been with WordPress almost as long as WordPress has been WordPress, which is, um, which is fun, like to see how far we\'ve. As a, as a community and as a, and as a piece of software. Right?

\n\n\n\n

Cory Miller: We\'re gonna have to [00:02:00] talk about that later. I\'m gonna come back to that cuz you, you predate me. I was just a blogger in 2006 on, on this cool thing called WordPress . Um, but you said this, uh, as part of you, I know you\'re so, you\'re so humble, but I want to act accentuate a part of this, like that special projects team you did at Automatic is known for doing. Big, glamorous, cool sites with potential big problems attached to them.

\n\n\n\n

And I can\'t remember what the code name for the team has called, but I knew about it for years. And then when we met a couple weeks ago, months ago, um, and you told me your background, I was like, you were on that team. Cuz it\'s very, um, I, I would say like, You know, a celebrity status in my sense, because I know I\'d go, I\'d go to this blog site of this cool site and realize it was on WordPress, or somebody would say, now this is on WordPress, and you kind of dig into the details and you go, it\'s that team at Automatic that was doing it, that you were a part of for such a long time.

\n\n\n\n

Tiffany Bridge: Yeah, I was there for, uh, well, it was just like, [00:03:00] it was a couple of years and, um, yeah, I mean I worked on some very, very cool projects and it\'s kind of like WordPress bootcamp, right? Like if you don\'t, whatever you think you know about WordPress, you will know more after, after like six months on that team.

\n\n\n\n

Um, because we solved like, Like every WordPress problem there is, right? Like you\'re, sometimes you\'re rescuing a site from a developer that maybe didn\'t do a great job. Sometimes you\'re converting a site that isn\'t on WordPress to WordPress. Um, like a, a project that I worked on that is very close to my heart that I can talk about is, um, I worked on the conversion of a list part from Expression Engine to WordPress, which was just an incredible experience.

\n\n\n\n

Um, I learned so much, and the a list part team was super great. So, um, yeah, like that was a, that was an intense couple of years. Like there\'s a lot, there\'s a lot that goes into those projects and our job was to kind of make it, it was like, you know, like the metaphor of the duck, right? Like you\'re, you\'re swimming seren except underneath, you\'re like furiously paddling

\n\n\n\n

And like that\'s, uh, [00:04:00] that\'s the special projects team.

\n\n\n\n

Cory Miller: Can you say this special code name for it? I wanna say stiff.

\n\n\n\n

Tiffany Bridge: Um, the, so, I mean, every team at Automatic has like an internal nickname, right? Like the, the, the name. Because the names of teams at Automatic have historically not been, um, they have, there, there isn\'t just like, oh, that\'s accounts payable.

\n\n\n\n

Like there\'s, that\'s not what any of the teams are called, right? They all have like clever names, , um, special projects team is, uh, the overarching team is called Team 51. There are a lot of, there are a lot of rumors about why that was chosen. Um, none of them are, all of them are more glamorous and interesting than the real reason it was chosen

\n\n\n\n

Um, but now team 51 is actually, like, when I was there it was like 13 people, but it\'s now like 40 some people and so there\'s lots of subteams and those subteams all have names and things like that as well. So, but the overarching team internally is called Team 51.

\n\n\n\n

Cory Miller: This is why I wanted to do these set of interviews cuz there\'s people behind, oftentimes behind the scenes with these vast experie.[00:05:00]

\n\n\n\n

Building the cool products that so many people use and why? I wanted to highlight your background. When we got to talk, I was like, oh, I\'ve gotta share this, because I think it\'s so compelling to see one, you\'ve been doing WordPress for a very long time. Two, you did it for with this like, very, uh, interesting team doing some cool projects that really put a great face on WordPress.

\n\n\n\n

Um, like a list apart. You know, so many people in our community know that like the back of their hands. Um, I wanna share that. Cause I think that that all formulates these compelling stories into today in your role at Nexus and what you\'re doing and formulates all this background. Like I remember at I themes, there\'s so many times we\'re building cool stuff, but people don\'t see inside the workshop, they don\'t see all this stuff.

\n\n\n\n

They don\'t know all the history and background, the care and passion that goes into this. And so that\'s one of the reasons I was doing this and why I wanted to like point it out, you know, , um, So, um, okay, so that brings us to [00:06:00] today, and now you\'re at Nexus doing store builder of many things. But I really wanna talk about store builder because I think it\'s really interesting.

\n\n\n\n

I know you\'ve been focusing on it, um, at Nexus and it, there\'s a big problem that I think it solves for my own work. , I shouldn\'t even say work, trying to use w this thing called WooCommerce, which is incredible. one I, I think I, I\'ve said at least, and you correct me, kept, but I\'m like WooCommerce is the default e-commerce software on the planet because it\'s used so broadly.

\n\n\n\n

I think it\'s growing faster still than WordPress and for good reason, but you can do anything and everything with it. And that presents a lot of complexity. Absolutely. Absolutely. What is the problem you\'re trying to solve with store builder?

\n\n\n\n

Tiffany Bridge: Sure. Oh, well. So as you say, like the more flexible and powerful something is, the more complicated it is.

\n\n\n\n

And you know, something that I learned, and this I think, especially I learned at, um, on special projects is that, [00:07:00] you know, setting up WordPress and WooCommerce, that\'s a different set of skills than just using them day-to-day. And the problem is that people who, like once you, once the, the site is set up right, people can learn to use it.

\n\n\n\n

It\'s not, it\'s not that hard to use, but getting to that point where you can just use it and run your business on it requires a ton of knowledge. And you know how WordPress. Is like, it likes to hide all of the settings, like in all of these different menus. And you have to, you have to kind of know what you\'re looking for in order to find it.

\n\n\n\n

Um, and that\'s a real, that\'s a real challenge for people. So the problem that we\'re trying to solve with store builder is this idea of like, okay, there\'s like five or six things you have to do in order to go from zero to a store. And we wanna like gather those all up in one place and just walk you through them in a very logical way.

\n\n\n\n

So, okay, first we\'re doing like what we call first time. Consider. You\'re setting like the name and address of the store and the name of the site. And then we wanna do look and feel. Um, so let\'s just get some pages into your site. Let\'s get some content into your site that you can edit and make your own.[00:08:00]

\n\n\n\n

Then we wanna, like, let\'s add a domain. We\'ve got this very cool, like we call it the Go Live wizard, where you just, um, where it like walks you through the process of, of connecting a domain right there from inside WP admin. And then we\'ve got, okay, great. Now it\'s time to add your products. Products we don\'t have a wizard for.

\n\n\n\n

We\'re just sort of surfacing a lot of help content to just help people make good choices as they\'re configuring their product, their products. And then it\'s like, great. Now let\'s connect your payment. Now let\'s set up your shipping. Hey, congratulations, you have a store. Is there more work to do on the site?

\n\n\n\n

Of course there is. There\'s always more work to do. But now we have gotten to a point where you have products and you can take payment and you can ship them, and your site has a domain name and therefore an SSL certificate. So here you are, now you\'re in business on the. And that\'s the problem that we\'re really trying to solve is just like, let\'s just get p get all of these, like things that you have to configure in front of people so they don\'t have to go hunting for.

\n\n\n\n

Cory Miller: And that\'s a huge problem I see that firsthand, um, is, you know, WordPress enabled me [00:09:00] to start a business, start a blog first, and then it evolved into a business. And that\'s the beauty of it. And I see that with, with commerce. Nearly any, uh, nuance thing you want to do, you can probably do it with WooCommerce.

\n\n\n\n

There\'s so many extensions, plug ons and addons and stuff. It from my experience, it seems like, you know, you get in and, and e-commerce just set aside from e-commerce is just complex because, okay, well you\'re selling in Europe and you need that and you need invoices or something like that. You\'re selling, you know, a digital good with a physical product and you want a free trial.

\n\n\n\n

I was just talking to somebody about that yesterday. The whole thing on e-commerce. And then you get to WooCommerce, great tool, awesome ecosystem and stuff. And I see this problem that you\'re trying to tackle over and over, uh, and I think it provides a huge need for those trying to build stores on the web.

\n\n\n\n

Um, tell me about who the product is really for. [00:10:00]

\n\n\n\n

Tiffany Bridge: So you know, this product is really for that sort of like merchant who is either setting up the site themselves or maybe they\'re working with somebody to set up, but they\'re not like hiring an agency to build them a site, right? Like they might have, they might have a buddy who\'s good with computers, or they might even have paid a freelancer, but it\'s really meant to be kind of, Right at that like level of the person who is actually gonna be running the business should be able to set up the store.

\n\n\n\n

That\'s always the goal that we\'re after, right? Is if you decide, if you\'re like knitting hats and selling them on Etsy and you decide you wanna get off of Etsy, like you should be able to do this. So it\'s, it\'s meant for people whose skill is whatever it is that their business is. Not building websites, and that\'s who we\'re really targeting with this.

\n\n\n\n

Now, that is a very complicated problem and there\'s a lot of layers to it. And so we are always in the process of trying to solve for that use case. I think, um, I don\'t know if you can ever be, you can never say. We\'ve solved it, right? Like there\'s always gonna be more to do. [00:11:00] Um, and that\'s what we\'re doing with Store Builder right now, but that\'s who, that\'s for.

\n\n\n\n

Like a lot of our other products, like we host, we have Manageable commerce hosting, manage WordPress hosting. What we like to say about those products is that we\'re the hosts that you graduate to, right? If you\'re coming to us, you\'ve probably already been somewhere else. Um, but with Store Builder, we\'re really focusing on people who probably don\'t already have a website, and that\'s, uh, that\'s who the product\'s for.

\n\n\n\n

Cory Miller: That\'s unique with Nexus, but I know Nexus is a brand company, has extensive experience with e-commerce too. And this offering is really interesting because one, you\'re tackling a big problem. Um, but two, you\'ve got a lot of experience on your team and the company that has really dealt with this, um, the e-commerce question for a long time.

\n\n\n\n

So.

\n\n\n\n

Tiffany Bridge: Yeah well, and it\'s such a privilege to be able to work with people who like really think about e-commerce, right? Like Nexus got its start doing Magento. And so like we have a lot of like all of our, you know, engineering and our operations, like, they understand like what an e-commerce site [00:12:00] needs. And so it\'s, it\'s been great to watch them kind of apply that knowledge to WordPress and w as well.

\n\n\n\n

Cory Miller: Excuse me. And I think this is. It\'s one thing to have a blog, you don\'t wanna have blog. Mm-hmm. , I didn\'t worry too much about downtime. Sure. When you have downtime or something happens and you can\'t get things done with your story, you\'re probably likely losing money. So Absolutely. I think that experience is, is key to highlight Mato Gun back to the, the days, you know, this big, big behemoth of an e-commerce platform that switched hands and

\n\n\n\n

.

\n\n\n\n

hear that background. Next is, So you, you said this, uh, just a second ago, but you talked about some of the things, like what you\'re trying to do, and you mentioned some, some key things in the last year or so, as you\'ve b led this project. Um, what are some of the things that, that stand out that you\'re, um, excited about, proud about that uh, you can share.

\n\n\n\n

Tiffany Bridge: You know, I think in terms of like actual product features, you know, I\'m so proud of that Go Live Wizard. Um, because like, [00:13:00] you know, what\'s this saying? Like it\'s always d n s, right? D n s is hard and that\'s. and that\'s such, and there\'s no way to talk about it in a way that isn\'t like technical, right? How do you connect a, a domain name to your site?

\n\n\n\n

Well, you\'ve gotta go change your name servers. Well, what\'s a name server? What\'s a cname? What\'s an a record? Um, people shouldn\'t have to know that, right? Like people shouldn\'t have to know that in order to get online, I think. Um, so it\'s been really fun to kind of build this cool tool that just walks people kind of through a decision tree.

\n\n\n\n

The first thing it asks you is, , do you have a domain name or do you need one? If you need one, it\'ll send you out to the Nexus checkout, or we\'re working on this feature where it\'ll send you out to the, the Nexus checkout. We\'re working on the feature where it brings you back, back into your store. Like right now, we can, we can send you out to our domain registration, but we, we have to rely on you to come back.

\n\n\n\n

We\'re working on a feature where we can move you out and then just bring you right back to where you left off. But you know, so that\'s the first question. And then like once you have it, it like it will actually validate whether your domain is ready to connect, right? It\'ll do all the queries to see like, [00:14:00] are your name servers set or do you have the C name set up?

\n\n\n\n

And it\'ll tell you. If not, it\'ll tell you what it is that you need to do. Um, And then, you know, you, as you proceed with it, it\'ll like set up the DNS zone in your portal and it will like do the, um, the find and replace on your database to make sure that like WordPress knows what domain it\'s supposed to be using and that all of your internal links are now referring to the correct domain.

\n\n\n\n

So like it does all of those like little things that, like on special projects, we have a whole checklist for, to make sure that a human does them well. Now we\'ve got like a. Um, so that, that does that, and that\'s, I actually tease my former coworkers sometimes and I\'m like, Hey, I\'m over here trying to replace special projects with a series of onboarding wizards.

\n\n\n\n

And they\'re like, yeah, good luck with that . I\'m like, Hey, look, I never said I like small problems. Right? . So, um, but so that, like, that feature is something that I\'m really, really proud of and, um, and excited about. And I\'m always telling people it\'s like the best single piece of store builder

\n\n\n\n

Cory Miller: is, is this different [00:15:00] from the wizard?

\n\n\n\n

You mentioned a bit ago.

\n\n\n\n

Tiffany Bridge: It\'s the same one. Okay. I mean, it\'s like the, like that\'s the, that\'s the one that I\'m most excited about. And, and I think it\'s the reason that I may, that we\'re able to do that one so beautifully is because you don\'t have to, like, there isn\'t like a third party that we\'re having to connect with.

\n\n\n\n

Um, you know, when you start getting into like payments and shipping, like suddenly you\'re dealing with other people\'s APIs and so there\'s a limit to what you can do. Um, but like where we\'re able to kind of control the experience, we\'re able to make it like really beautiful and functional.

\n\n\n\n

Cory Miller: I know I\'ve, I\'ve helped people.

\n\n\n\n

You know how it is, I\'m sure you get this too. It\'s like if they know you do WordPress or websites, you know, everybody has some kind of idea. And, um, there\'s platforms out there, but again, the power of WooCommerce and, and WordPress particularly to, to grow your business. But there\'s complexity that happens that, that I know you\'re wiring in as you think about and build, continue to build the.

\n\n\n\n

For that experience. Um, it\'s kind of [00:16:00] going back for a second. I know Nexus does. Okay. You graduate to us. Uh, store builder specifically, I think is for a different kind of, um, problem. And you might have said this, but I want to come back to it cause I, I think I might have missed sharing this part of it. So, store builder, if you, you know, want to start a store and here are, you know, 15 options.

\n\n\n\n

This is the option if you want to, um, start a store and grow it.

\n\n\n\n

Tiffany Bridge: Is that right? Yeah, I think so. I mean, I think there\'s a no better platform than WordPress and Woo for something that\'s gonna grow with your business and be flexible to your business. Like maybe you get farther down the road and you decide, you know what?

\n\n\n\n

I don\'t actually want to sell merchandise anymore. What I would rather do is do courses or events. I mean, all right, well just install another plugin. You can uninstall WooCommerce. , off you go. Um, and so, you know, having that option always available to people as well is really important. Like you can, [00:17:00] because as you know, it\'s so flexible and you can just swap in the pieces you need and take out the pieces you don\'t.

\n\n\n\n

Um, I think it\'s, it\'s really great to just get people, like, just, just get on the platform that\'s going to grow with you at the beginning instead of having. Migrate later, right? Like, nobody likes migrations, nobody likes, you know, having to convert their data and carry their, carry their orders from like their Shopify store and their commerce.

\n\n\n\n

Just start with WooCommerce. It\'s fine.

\n\n\n\n

Cory Miller: I know. Um, so we talked about in that experience, like really making that initial experience where you\'re like, I\'ve got something I want to sell. Um, you mentioned when we were talking before this too, like particularly you\'re on another platform, like an Etsy or some other platform.

\n\n\n\n

This is when, um, you\'re ready to go and there\'s this, there\'s this learning curve with WordPress WooCommerce that you\'re trying to sort out. Um, I think you said it when we were, um, prepping for this like idea to selling [00:18:00] is, is kind of that key, which I think is so awesome because I know from experience.

\n\n\n\n

People, you know, non-word, pressure related. Go, I\'m ready to do this. Lindsay and I, my wife have a, a partner, great founder who does physical products. And, and that was the question I was like, okay, well you have a couple of options. , they all have pros and cons, they have some things. Um, but having an experience like this, I think is so key because of that initial learning curve going live online.

\n\n\n\n

But there, I know there\'s other things too. Nexus happens to be in the family of LiquidWeb, which is Own, has a number of WordPress specific company outside of the Nexus brand of families that you all, um, leverage within the platform too.

\n\n\n\n

Tiffany Bridge: Yes, absolutely. Um, the biggest, uh, so you know, the liquid web family of brands is large and growing, right?

\n\n\n\n

And, and, and as our post status friends know, there are quite a lot of like WordPress plug-in businesses that are now part of the family of brands. And the one that we are leveraging most right now in store builder is [00:19:00] cadence. And cadence. For those who don\'t know, is this really great? I don\'t wanna call.

\n\n\n\n

I mean, it\'s a theme, but it\'s like so much more than a theme, right? Um, it, it is a theme. It is blocks, it is starter templates. It\'s this whole package and it\'s really geared around people who are like web designers, but just need a great, um, like way to build and customize a site that doesn\'t necessarily rely on like a third party page builder.

\n\n\n\n

Right? Something I appreciate about Cadence is the way it sort of embraces. Extends the WordPress Block editor rather than trying to replace it. Um, cadence is there, there\'s so much great stuff, right? Like right now, store Builder really leverages this Cadence starter template. So you pick one of the starter templates around, uh, around e-commerce, and we import a site for you, basically.

\n\n\n\n

Um, and then you just have to edit it and make it your own. Replace the images, replace the text. But, you know, the, the feedback that we\'re getting from our customers is that that\'s still a lot of work and it. Their feedback is that because it is, they are correct. [00:20:00] That is still a lot of work to do. And so something that we\'re kind of, the next problem we\'re trying to tackle in store builder is this idea of editing all the not store parts of your site, making sure that you have a homepage and an about page and you know, all of your policy pages and things like that.

\n\n\n\n

And making it as easy as possible for people. Because you know, cadence was kind of designed around people who are already web designers and that isn\'t who our audience is. So we\'ve been working very closely with the ca cadence team on, you know, what\'s a, how can we leverage cadence and the power and the, the, the experience that they have, but create like a really great experience for, um, people who aren\'t.

\n\n\n\n

Who aren\'t already savvy with web design, right? Who don\'t know, like, what is a gutter, what\'s a border radius like, you know, no one should have to know that. Um, so we\'re, that\'s the next problem that we\'re trying to solve and um, and it\'s been a real privilege to work with my colleagues over on that side of the house on that.

\n\n\n\n

Cory Miller: I, That\'s you just kind of like [00:21:00] highlighted one of, one of the benefits why we, our partner and, and the founder of that physical products company. Like why not just to use, let\'s say a Shopify site or something is like mm-hmm. , the stuff you said that the non-store stuff is so awesome and attractive.

\n\n\n\n

Mm-hmm. and helpful for store owners where you can blog and. NCO and different things like that. And I happen to have some inside knowledge as far as . Um, having been at Lake Web a couple years ago, sold, sold our themes to, uh, lake Web, that there\'s a suite of tools That\'s awesome. And to see, you know, post status by the way, also runs cadence and such a powerful framework, whatever we call it, you know, word critical.

\n\n\n\n

Tiffany Bridge: Yeah. It\'s a, it\'s a sweet a package. I don\'t know, it\'s like, it\'s a theme. It\'s a lot. It\'s a lot of stuff. Um, and it\'s, it\'s just great. And, um, I\'ve been really, it\'s been really nice to be able to, to work with, um, something that both kind of embraces kind of the WordPress way of doing things, but also really [00:22:00] enhances and expands it.

\n\n\n\n

Cory Miller: Okay. So help me complete this sentence. As for product lead for this, this particular. Um, there\'s probably all these things that your, your team knows in sudden and out cuz you built them and you built them based on these customer, this journey of these problems with obstacles people ran into. I wish people knew or did about what?

\n\n\n\n

As part of store builder. Is there things from like, you know, your team just goes, gosh, they\'re not taking advantage of the school teacher. They\'re not doing this one thing that would make their life easier, the business would grow better. What are, what are some of those things, part of the platform that\'s come to mind?

\n\n\n\n

Tiffany Bridge: Oh, that\'s a hard one. I mean, I think the thing that I find is that the thing that I always want customers to know is usually it\'s bec, usually they don\'t know it cuz I haven\'t adequately conveyed it to them. So it seems a little bit almost self-serving. Right. To be like, oh, I wish [00:23:00] they knew. Like, one thing that I always find myself wishing that people knew is that e-commerce is really complicated.

\n\n\n\n

Right. Um, cuz I think sometimes we get people who come to. To store builder and expect us to solve all of the complexity of the e-commerce when what we\'re really able to solve is like the complexity of the website part. Like I read our, um, One of the things I do as a product manager is I read all of our cancellation reasons.

\n\n\n\n

Um, so like anytime somebody has left the product and they wanna tell me why it\'s hard reading, sometimes , it\'s very bad for the ego, but it\'s very good for the product. And somebody once said, well, I, I can\'t believe how many things I have to log into to use this. Like, okay. Well if you\'re talking about like our Nexus portal, like I agree with you.

\n\n\n\n

I would love to reduce the need for people to have to log into a web hosting portal. Right? But if you\'re talking about payments shipping, like was there ever a future where you weren\'t gonna need a Stripe account? I know some people are [00:24:00] tackling that by like building their own payments, but then I feel like that\'s another form of lock-in that I don\'t love.

\n\n\n\n

Right. Um, so, you know, so a thing that I, I want people to know is that, um, the system ha the, this, we\'re trying to, we\'re trying to balance like that like. Opinionated versus like freedom thing, right? Like, can we be very opinionated? Like, look, you\'re just gonna use, this is the payment system you\'re gonna use.

\n\n\n\n

Just, just, you know, while also still giving people that freedom of w of, of WooCommerce, um, I think that\'s always like when I\'m reading stuff, that\'s always what I\'m wishing people knew. And so now it\'s just a question of like, well, how do I then, like how do I teach \'em that it\'s not their fault? They don\'t know that I know that they don\'t know that.

\n\n\n\n

I think about e-commerce all day. You don\'t, you, all you wanna do is just get online and like sell this thing you made,

\n\n\n\n

Cory Miller: sell your stuff. Absolutely. Well, and, and there\'s platforms out there like Shopify for instance, and it, it\'s super fast gets [00:25:00] something going, but the complexity exists of some of these things.

\n\n\n\n

Like, you gotta think through, are you selling to Europe? What do you, you know, that\'s just one that comes to mind for me. Exactly. Um, but I totally get it. Um, the space that you all are in, what the product you\'re trying to provide, um, that, that is kind of like a pro and con of the beauty of the. , you can with store builder, with WordPress, with WooCommerce, get a store up and going mm-hmm.

\n\n\n\n

Um, so you can do it. And that\'s a great freedom that we have and enjoy for sure. But that, uh, I know from having done had, obviously businesses that run e-commerce rely on e-commerce or website was our front door to our store, but it was down. We didn\'t make money. Um, and then trying to help navigate some of those complexities is, is a pretty tough job.

\n\n\n\n

Anything else that kind of comes out to. About what I wish people knew. Yeah.

\n\n\n\n

Tiffany Bridge: Oh gosh. So many things. All the [00:26:00] things. Um, , they need anything, I guess they wouldn\'t need store builder

\n\n\n\n

Cory Miller: anything about the product that we haven\'t. Mentioned that, that you want to share too? I

\n\n\n\n

Tiffany Bridge: mean, I think, like, I think we\'ve covered all the things that I\'m like most passionate about.

\n\n\n\n

Like I just, yeah. You know, well, we were, you remember that controversy several months ago about Etsy and like Etsy\'s increase in fees and people were sh closing down their Etsy stores. And, um, like I just, like, I want people to know that it doesn\'t have to be that. . Like, it doesn\'t have to be that way.

\n\n\n\n

Like you can own the plat, like you can own your platform. We\'re seeing this now with Twitter, right? The implosion of Twitter. People are like, what are we gonna do? Where are we gonna go? And I\'m like, you should have a blog is what you should do. Um, you know, I think I, it just, I want people to know that it doesn\'t have to be that way.

\n\n\n\n

We don\'t have. Like our presences on the web, which is an increasingly important way of way, way that we conduct business, the way we conduct our relationships, the way we meet new people. Like we don\'t have to, it doesn\'t have to be that way, right? You [00:27:00] can own your home on the web, whether that home is a store or just a blog.

\n\n\n\n

Or just a blog or, um, or anything else. Like it. Just like, it doesn\'t have to be this way. It can be. There are many of us who would love to help you with it. And like, I\'m not saying that just as a person who wants to sell store builders, I wanna sell store builders, but I want to sell, like the reason that I care about store builder is because what it allows people to do.

\n\n\n\n

Cory Miller: Mm-hmm. Absolutely. You backed into my question I was gonna ask you next was to, you\'ve been a workforce a long time and you know when we prop. Uh, examples, like, I don\'t want to just poo poo Shopify, but use Shopify software is a service. There\'s benefits to having a SaaS Absolutely. Solution for what you\'re doing, but there\'s also,

\n\n\n\n

Tiffany Bridge: there\'s a reason they\'re successful.

\n\n\n\n

Cory Miller: Absolutely. There\'s also downside, and you mentioned earlier it\'s like WooCommerce, WordPress, and even store builder and Nexus grows with you. Um, but I want you to share a little bit more about that. You know, Shopify, what I was telling our partner, I said, you know, [00:28:00] Shopify\'s the glamorous thing people look at.

\n\n\n\n

And I see, I see why. But I said, you\'re gonna trade some problems for a new set of problems. And one of those you\'ve mentioned a couple times is lock in. And the beauty of, I want you to share a little bit about the, what your thoughts are around WordPress, WooCommerce, and open.

\n\n\n\n

Tiffany Bridge: Yeah, I mean, I think, I mean, the number one, biggest one is that you can own it and you can go, you know, wherever you want, and you can decide the experience that you wanna have.

\n\n\n\n

Um, I think that\'s something that a lot of us are spending a lot of time thinking about right now as like various social media platforms or like the, the downsides of like, for example, kind of lock in, uh, in social media pro. Platforms is becoming apparent, right? So that\'s like one thing that I think is really important.

\n\n\n\n

Um, another thing that\'s important is that, you know, the thing about, like, there are lots of companies in WordPress and Yes, here we all are trying to sell you our solution, right? We\'re all trying to make money. We\'re all trying to, you know, everybody, we, we live in capitalism. We\'re all trying to make money here.

\n\n\n\n

[00:29:00] But at the same time, like there is no reason. That you have to have any of that, right? Like the only thing that, that you have to pay for to use WordPress is someplace to. Right. You can download it, you can use it, it\'s all free, and that you can decide what you need and then you know what\'s worth paying for versus what\'s worth not paying.

\n\n\n\n

Like you can, it\'s such a like a choose your own adventure kind of platform. And I feel like, you know, we\'ve had so much centralization and so much, um, You know, like it\'s just so much centralization, so, so much like merging and like this company buys this company that we kind of forget that like we don\'t have to be that way.

\n\n\n\n

And I think it\'s, it\'s really important. Uh, I think open source is really important to like individual autonomy in that way. Like we\'re starting to get a little of like philosophical here, but I think, you know, just knowing that. If nothing else, you can just go download WordPress and learn to use it. Like I started downloading WordPress and learning to use it because, um, [00:30:00] movable type was going to a pay a for pay model and it was more money than I could pay at that time to indulge my like personal blog habit.

\n\n\n\n

And everybody was talking about this new system, WordPress that was open source and free. And I was like, free is good cuz I am broke. And I downloaded it and I started teaching myself to use it and it completely changed my. And I know I\'m not the only one. Right. I have talked to other people who are like, great.

\n\n\n\n

WordPress was free for me to learn to use, so I learned to use it. Word camp was $20 for me to go, so I slept on somebody\'s couch and went to a Word camp. Something that I think is, is so important is, is that kind of low financial barrier to entry. I would love to see us have a lower like knowledge barrier to.

\n\n\n\n

and I think we\'re all working on that every day. Um, but um, that, that\'s just like, that barrier to entry I think is always really close to my heart because I really believe that, you know, these are things that can change people\'s lives if they just have what they need in order to take advantage of them.

\n\n\n\n

Um, and I think that the community really [00:31:00] does care about that. And that\'s something that\'s like, makes me very proud to be involved in WordPress.

\n\n\n\n

Cory Miller: Well, you, you just, there\'s a practical side to this too, and I love the philosophical because it has practical implications as well. It\'s like Absolutely. You get locked into a platform, like you\'re talking about, whether it\'s an Etsy or a Twitter or a Shopify.

\n\n\n\n

Mm-hmm. , you\'re at kind of the whims of. What they\'re doing. That\'s a little bit different in word control,

\n\n\n\n

Tiffany Bridge: like company gets bought by somebody who then does all kinds of questionable things with it, and then here you are, like, I\'ve been on Twitter for 15 years, right? Like I\'ve been on Twitter since, yeah, 2007.

\n\n\n\n

So I\'ve been on Twitter like 15 years and here I am. Like with my like 15 year old, like at Tiffany Twitter handle, because that\'s how long I\'ve been on it. I got my first name and now somebody\'s over here like running it into the ground, making all kinds of questionable decisions, messing up the experience I have.

\n\n\n\n

And then I\'m like, well, now what? Like half the people I know I met here, like now what do I do? And like here I am like. I got locked in. I said I wasn\'t gonna get [00:32:00] locked in, but here I am, locked in. Um, so yeah, I mean that has like very practical considerations. There\'s people that I\'m struggling to stay in touch with because I only knew them on Twitter and like, how do I find them now?

\n\n\n\n

Cory Miller: Well, and you know, just a real direct one-to-one is, um, Shopify and Etsy platform versus this. And you, you look at a lot of entrepreneurs, e-commerce merchants start something, it blows. It. It starts to really grow and that lock in down the stream really comes into play For sure. Like you start getting taxed on your success in a sense where you, like you said, to that own and locked in feature where you go now.

\n\n\n\n

Exactly. With WordPress, we built a tool to, I themes that stellar brand that you can move websites very easily with. Exactly. Including at Nexus Brands.

\n\n\n\n

Tiffany Bridge: Exactly. And you know, like you, you build something, you go viral, you\'re like, suddenly your Etsy store\'s [00:33:00] going crazy. Now you have like, you know, transaction fees at Etsy.

\n\n\n\n

So the bigger you are, like the more your fees grow at ets, you know, at Etsy. And um, so you have that problem, but also like maybe you never bought a domain name. So now everybody only knows where to find you on Etsy instead of getting a domain name. So now you\'ve gotta like figure out how to teach people to go somewhere else.

\n\n\n\n

Like if you wanna move, like it\'s, yeah, it\'s a real. . I see this a lot of times too with like content creators and like Instagram. They\'re like, oh my gosh. If, I mean, Instagram\'s how I reach my audience, how are people gonna find me? If inst, if Instagram goes down, y\'all, that is a problem. Like you need a website and, and it just like, it makes me nuts, like a thing that is, it just makes me like pound the table cuz I get so annoyed about it.

\n\n\n\n

Is so you don\'t have like, People, you can only have like one link on Instagram, right? It\'s in your bio link in bio. And so people will like pay money for a link in bio service and then like link to their website and a link in bio. And I\'m like, what if I told you that you could just put a page on your website with the list of all your [00:34:00] links and then put that link in your bio.

\n\n\n\n

Um, and then you wouldn\'t be locked into yet another service, right? You don\'t have to get locked into the, like, there\'s the lock into Instagram and then there\'s the lock into the, the thing that you did to like work around the limitations of Instagram. Just have websites. Y\'all just have websites.

\n\n\n\n

Cory Miller: It\'s well in this, this partner of our same thing, built a great, huge audience on Instagram.

\n\n\n\n

Mm-hmm. that you gotta have an gotta have a website, gotta have an email list that you\'re trying, you know, things have, things have evolved. There\'s other marketing opportunities. But I go for me, website, email list that you can contact them that you quote own. So if something shifts, but you know, Tiffany, I\'m interested too.

\n\n\n\n

You see all this, you know, looking, looking around Instagram for instance. Some of the people that have got huge audiences, and I click those links and I think, okay, well maybe they\'re what, you know, at some point, how do they monetize that? And I go and I wanna get your thoughts on this and this whole creator [00:35:00] economy and what, I think probably 10 years ago we thought it\'s like bloggers and , you know, we have a new name for it now, but the creator economy, where they used the platform to get some initial buzz, but then, Okay.

\n\n\n\n

What\'s the path to Monet monetization. I mean, we\'re all passionate about what we do, but at some point you also need to, you know, keep the lights on and pay the pay the bills kind of thing. Absolutely. But I\'m curious too, like seeing that you\'ve been at WordPress a long time, seen in the web, a long time, been a technologist, but like, you know, what\'s your thoughts on that creator economy?

\n\n\n\n

Just like you said, okay, hey, here\'s a good point. Build your audience here. Hey, maybe not just a link tree or whatever it\'s called, but like, here\'s your website and all that. But what kind of trends and, and themes are you seeing in, in the foreseeable future, uh, that you know, you have thoughts on and ideas for as the creator economy builds?

\n\n\n\n

Tiffany Bridge: I mean, I\'m seeing, I\'m seeing a lot of people kind of fall back to newsletters, which is very cool in like retro, right? Like this idea of [00:36:00] like email, like we\'ve all got email. We neglected our email boxes for a while, but now it\'s back email\'s back, baby. Um, I think that\'s really interesting. And, and you know, and we\'re still seeing like some consolidation there, right?

\n\n\n\n

Because then now it\'s like, oh, let\'s, let\'s have a CK and like, okay, but now you\'re like locked into ck, right? Yeah. Um, which, which is a little bit of a concern, but you can at least like export. Subscribers out from ck, like if nothing else, like you can take your list with you, which I think is really great.

\n\n\n\n

CK has put together like a really easy to use stack of things that you need to run a four page newsletter. And, um, and so they\'re, they\'re popular for a reason, even if I still think people should have websites mm-hmm. , um, you know, but, but we are seeing that and even within sub, I\'m starting to see people like branch out into.

\n\n\n\n

Having websites like ghosts, which is another open source project. I\'m seeing people do that instead. Um, I think it\'s, it\'s really interesting right now because we ha we\'re in this moment where like the, the platform, the [00:37:00] social media platforms are really starting to show the seams and, and it\'s starting to feel like maybe we\'re on the edge of something.

\n\n\n\n

And I was just talking about this with a friend of mine the other day, and cuz he was saying like, Man, like Google Reader died and it kind of killed R Ss, right? Like, and nobody\'s figured that problem out since then. I\'m like, well, no, because everybody just started aggregating through Twitter. Twitter\'s the new, your new Google reader, except now like Twitter is twittering.

\n\n\n\n

And, um, because then we all, you know, we, and, and that, and again, that\'s like that problem of consolidation. Like even Google Reader, which was aggregating sources, it was like the dominant r s s reader. And I don\'t know, I don\'t know how to solve that problem. decent, uh, of centralization. Right. But I think it\'s very interesting that we\'re seeing people kind of move to newsletters because then they at least know that they can contact you.

\n\n\n\n

Mm-hmm. , and, and you can, um, and you, and you can have more control of your audience that way. Well, and then I\'m watching people like try out, like mastered on and that\'s interesting. [00:38:00] I don\'t, I don\'t know how that\'s gonna go cuz I feel like Mastodon is still. It\'s too difficult from like an administrative perspective.

\n\n\n\n

Like it\'s too difficult to start an instance right. Still. Um, I was talking about this actually in post status Slack the other day. I feel like a big reason that I ever got as far as I did with WordPress is cuz they had that five minute install so early on. Yeah. Like even in 2004, it was easy enough to install that I could figure it out myself and that like, I tried to set up ma on like ju like just like on a Nexus test account and like, , we don\'t have a way to run that particular form of like, of SQL that it uses of S SQL L and so like, like I would immediately stop and like, well, I.

\n\n\n\n

Like this, this thing doesn\'t even, like, it has dependencies that aren\'t necessarily available everywhere. And um, and then you have to, like, there\'s all this stuff that you have to do to set it up. And I\'m like, and you all have to, and it all has to be done from the command line. Um, so I feel like, you [00:39:00] know, these kind of like federated platforms where you run under an instance are gonna have to put a lot of attention into installation and onboarding if they want to, if they really wanna take off.

\n\n\n\n

I think that\'s gonna be a big thing.

\n\n\n\n

Cory Miller: What I take from this too is really going back to if you\'re thinking about building a business, even if you\'re dancing for passion, all of a sudden you\'re back in. You go, oh my gosh, I\'m a business owner. The thought process here to me is make sure you understand. What you own and what you\'re renting or borrowing for a time.

\n\n\n\n

Yeah, and just like you said, like I think so much from the we, I think we so much, by the way, benefit from de decentralization, AK WordPress, . You can, yes, you can copy it, you can for it and do whatever you want with WordPress. And there\'s power in that. And that shift of power where another platform has the rules.

\n\n\n\n

and regulations and policies that they change like Instagram, changing from more focus on [00:40:00] video to compete what\'s, let\'s say a TikTok and you go mm-hmm. Well, and, and I\'m not looking at my analytics all the time, but I look at likes, right? And I go, well, my likes went down quite a bit. Well, because I don\'t do video, I don\'t want to do video.

\n\n\n\n

Right. And right. Then you go, there\'s a way to build, it seems like build some initial audience, but make sure you have these off-ramps into something, even like an email list, you said, much less complex to export your subscriber list and go to another platform than e-commerce, but be really choosy and picky about what you\'re doing because.

\n\n\n\n

When your business does continue to grow, you want to be able to grow with it in the right platform to do that.

\n\n\n\n

Tiffany Bridge: Absolutely. Absolutely. And also, you know, as like the thing about decentralization is that there are a lot of problems that we are accustomed to having platforms solved for us. That now we have to solve on our own a decentralized situation.

\n\n\n\n

And so those of us who\'ve been working in open source a long time and and who work in tech, kind of like we already understand that like moderation is a problem and you have to think [00:41:00] about it. But you\'ve got all these, like for example, new MA on instance, admins who\'ve never really thought about moderation is like a problem.

\n\n\n\n

They have to solve , , and, and, and you\'d better. Right? And so, and that\'s like a, I think that\'s gonna be a real adjustment for people to make as we kind of like, if we\'re, if we\'re really gonna see like the beginning of a decentralization here, like there\'s gonna be a lot of like lessons that have to get relearned.

\n\n\n\n

Cory Miller: Yes. And when you said that about the five minute install, raise my hand because I go, that\'s why I loved WordPress. I didn\'t have to, what\'s a command line? What\'s the, you know, how do I. Upload, install, extract, set up my databases. Like that kind of simple. I\'ve seen so many tools over the years that promise some decentralization.

\n\n\n\n

But it\'s great for the developers that know all those things. But for the everyday person, once that gets figured out, that five minute or click, click install, I, I think we\'re gonna see some shifts in power.

\n\n\n\n

Tiffany Bridge: Yeah, I think so too. I think, um, I think if they pay a lot of, at pay more attention to that, I think you\'ll start to see a lot more.

\n\n\n\n

Cory Miller: [00:42:00] Tiffany, thanks so much for being on, um, post draft today and sharing some of your background and obviously your vision values, and then, um, what you\'re doing over at Nexus with store Builder and the other products. Um, tell, tell people where they can find you.

\n\n\n\n

Tiffany Bridge: Well, um, my slightly less neglected these days.

\n\n\n\n

Personal blog is tiff.is so, https://tiff.is/, you can find me there as long as there\'s still a Twitter. You can find me on Twitter at Tiffany. And, uh, you can find me on Mastodon at, uh, Tiffany@theinternet.social social.

\n\n\n\n

Cory Miller: Awesome. Thanks so much, Tiffany.

\n\n\n\n

Tiffany Bridge: All right. Thank you.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 18:45:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Olivia Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:12;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:60:\"WordPress.org blog: The Month in WordPress – December 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"https://wordpress.org/news/2023/01/the-month-in-wordpress-december-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:12476:\"

Last month at State of the Word, WordPress Executive Director Josepha Haden Chomphosy shared some opening thoughts on “Why WordPress” and the Four Freedoms of open source. In this recent letter, she expands on her vision for the WordPress open source project as it prepares for the third phase of Gutenberg:

\n\n\n\n
\n

“We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.”

\nJosepha Haden Chomphosy
\n\n\n\n

December brought with it a time for reflection—a time to look back, celebrate, and start planning new projects. Read on to find out what 2023 holds for WordPress so far.

\n\n\n\n
\n\n\n\n

WordPress is turning 20!

\n\n\n\n

2023 marks the 20th anniversary of WordPress’ launch. The project has come a long way since the first release as it continues to advance its mission to democratize publishing. From its beginnings as a blogging platform to a world-leading open source CMS powering over 40% of websites.

\n\n\n\n

Join the WordPress community in celebrating this important milestone. As the anniversary date approaches, there will be events, commemorative swag, and more.

\n\n\n\n
\n

Stay tuned for updates.

\n
\n\n\n\n
\n\n\n\n

WordPress 6.2 is scheduled for March 28, 2023

\n\n\n\n

Work on WordPress 6.2, the first major release of 2023, is already underway. It is expected to launch on March 28, 2023, and will include up to Gutenberg 15.1 for a total of 10 Gutenberg releases.

\n\n\n\n

The proposed schedule includes four Beta releases to accommodate the first WordCamp Asia and avoid having major release milestones very close to this event.

\n\n\n\n
\n

Read more about the 6.2 schedule and release team.

\n
\n\n\n\n
\n\n\n\n

What’s new in Gutenberg

\n\n\n\n

Two new versions of Gutenberg have shipped in the last month:

\n\n\n\n
    \n
  • Gutenberg 14.8 was released on December 21, 2022. This version features a reorganized Site Editor interface with a Browse Mode that facilitates navigation through templates and template parts. In addition, it includes the ability to add custom CSS via the Style panel and a Style Book that provides an overview of all block styles in a centralized location.
  • \n\n\n\n
  • Gutenberg 14.9 became available for download on January 4, 2023. It introduces a new “Push changes to Global Styles” button in the Site Editor, which allows users to apply individual block style changes to all blocks of that type across their site. Other features include typography support for the Page List block, and the ability to import sidebar widgets into a template part when transitioning from a classic theme.
  • \n
\n\n\n\n
\n

Learn how Gutenberg’s latest releases are advancing the Site Editor experience to be more intuitive and scalable.

\n
\n\n\n\n
\n\n\n\n

Team updates: WordPress big picture goals, new Incident Response Team, and more

\n\n\n\n\n\n\n\n
\n

Check out the 2022 State of the Word Q&A post, which answers submitted questions that Matt could not address at the live event.

\n
\n\n\n\n
\n\n\n\n

Feedback & testing requests

\n\n\n\n\n\n\n\n
\n

Have thoughts for improving the Five for the Future contributor experience? This post calls for ideas on how this initiative can better support the project and the people behind it.

\n
\n\n\n\n
\n\n\n\n

WordPress events updates

\n\n\n\n\n\n\n\n
\n

Would you like to be a speaker at WordCamp Europe 2023? Submit your application by the first week of February.

\n
\n\n\n\n
\n\n\n\n
\n\n\n\n

Have a story we should include in the next issue of The Month in WordPress? Fill out this quick form to let us know.

\n\n\n\n

The following folks contributed to this edition of The Month in WordPress: @cbringmann, @laurlittle, @rmartinezduque.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"rmartinezduque\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:13;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:112:\"Do The Woo Community: Bringing WordPress Certification to the Community with Talisha Lewallen and Sophia DeRosia\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74322\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"https://dothewoo.io/wordpress-certification/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:427:\"

Talisha Lewallen & Sophia DeRosia from CertifyWP chat with us about the importance of WordPress certification.

\n

>> The post Bringing WordPress Certification to the Community with Talisha Lewallen and Sophia DeRosia appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 10:57:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:14;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: WooCommerce Blocks 9.4.0 Adds Support for Local Pickup\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141197\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"https://wptavern.com/woocommerce-blocks-9-4-0-adds-support-for-local-pickup\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1740:\"

WooCommerce Blocks version 9.4.0 was released with support for a new block-powered Local Pickup option under shipping settings. The feature plugin offers users early access to new blocks and improvements to existing blocks before they become available in WooCommerce core.

\n\n\n\n

Local Pickups introduces two new blocks: a shipping method toggle block that allows shoppers to select between regular shipping or pickup from a specified location, and a pickup location block that displays local pickup rates.

\n\n\n\nimage source: WooCommerce Blocks 9.4.0 release post\n\n\n\n

These blocks can both be enabled and configured via a new local pickup settings page. Store owners can even rename Local pickup to something else, and optionally add a price for this option.

\n\n\n\n\n\n\n\n

It’s important to note that the new Local pickup blocks can only be used with the Checkout block. WooCommerce Blocks also introduces a change with this new Local Pickup experience that will support location-based taxes based on the pickup address, improving tax reporting. Previously, WooCommerce based local pickup taxes on the store address.

\n\n\n\n

WooCommerce Blocks 9.4.0 includes a handful of other small enhancements and bug fixes. Check out the release post for a more detailed look at everything that’s new in the latest version of the plugin.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 02:59:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:15;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:21:\"Matt: Polls on Tumblr\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:22:\"https://ma.tt/?p=75803\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:38:\"https://ma.tt/2023/01/polls-on-tumblr/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:301:\"

We just launched polls on Tumblr, and it’s been pretty fun. Cool to bring together the Crowdsignal (née Polldaddy) technology into a new world.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 01:38:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Matt\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:16;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"WPTavern: WordPress Project Aims to Complete Customization Phase and Begin Exploring Collaboration in 2023\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141181\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:117:\"https://wptavern.com/wordpress-project-aims-to-complete-customization-phase-and-begin-exploring-collaboration-in-2023\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3124:\"

WordPress Executive Director Josepha Haden Chomphosy published a summary of the project’s “big picture” goals for 2023. The goals fall into three major categories: CMS, Community, and Ecosystem.

\n\n\n\n

WordPress development will focus on completing the remaining tasks for Phase 2 (Customization), and will move on to begin exploring Collaboration in Phase 3.

\n\n\n\n

“As we prepare for the third phase of the Gutenberg project, we are putting on our backend developer hats and working on the APIs that power our workflows,” Haden Chomphosy said in her recent Letter to WordPress.

\n\n\n\n

“Releases during Phase 3 will focus on the main elements of collaborative user workflows. If that doesn’t make sense, think of built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and programmatic editorial and pre-launch checklists.”

\n\n\n\n

The vision for the first two phases was “blocks everywhere” and Haden Chomposy said this will be updated for Phase 3 to be centered on the idea of “works with the way you work.”

\n\n\n\n

In addition to the Phase 3 APIs, Haden Chomphosy identified the following items as part of the CMS goals for 2023:

\n\n\n\n
    \n
  • Openverse search in Core
  • \n\n\n\n
  • Navigation block
  • \n\n\n\n
  • Media management
  • \n\n\n\n
  • Simplify the release process
  • \n\n\n\n
  • PHP 8.2 compatibility (Core and Gutenberg)
  • \n\n\n\n
  • Block theme development tools
  • \n
\n\n\n\n

Under the Community category, WordPress will be focusing on planning the Community Summit, which will be held at WordCamp US in 2023, contributor onboarding, improving Polyglot tools, establishing mentor programs, revamping WordPress.org designs, and keeping pace with learning content. The project is also aiming to develop a canonical plugin program, which should be helpful as some Performance team contributors recently expressed that they don’t fully understand what the process is for canonical plugins.

\n\n\n\n

The Ecosystem category will focus on the WordPress Playground, an experimental project that uses WebAssembly (WASM) to run WordPress in the browser without a PHP server with many useful applications for contributors.

\n\n\n\n

WordPress contributors also prevailed upon Matt Mullenweg to consider having the project devote some time to working through old tickets and fixing bugs. Mullenweg said he is amenable to tackling one long-standing ticket (the kind that are stuck because of missing decisions or multiple possible solutions) each month in 2023.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 22:57:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:17;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"Post Status: Big Picture Goals 2023 • WP 6.2 Planning • LearnWP Needs Analysis • Wrong Plugins\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146539\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"https://poststatus.com/big-picture-goals-2023-wp-6-2-planning-learnwp-needs-analysis-wrong-plugins/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:16840:\"

This Week at WordPress.org (January 16, 2023)

\n\n\n

Where is WordPress going in 2023? Read Josepha\'s Big Picture Goals for the year. WordPress certifications are in the planning phases, and the foundation will include LearnWP. The Training Team is conducting a Needs Analysis. Help gather the community\'s input. Plugins Team is seeking intentionally wrong plugins, and Core has the 6.2 Planning Roundup.

\n\n\n
\n\n\n\n\n\n\n\n

News

\n\n\n\n\n\n\n\n

\n\n\n\n
\n
\n

Central

\n\n\n\n\n\n\n\n

CLI

\n\n\n\n\n\n\n\n

Community

\n\n\n\n\n\n\n\n

Core

\n\n\n\n\n\n\n\n

WordPress 6.2

\n\n\n\n\n\n\n\n

Meetings

\n\n\n\n\n\n\n\n

Developer Blog

\n\n\n\n\n\n\n\n

Design

\n\n\n\n\n\n\n\n

Docs

\n\n\n\n\n\n\n\n

Hosting

\n\n\n\n\n\n\n\n

Marketing

\n\n\n\n\n\n\n\n

Meta

\n\n\n\n\n\n\n\n

Openverse

\n\n\n\n\n
\n\n\n\n
\n

Performance

\n\n\n\n\n\n\n\n

Polyglots

\n\n\n\n\n\n\n\n

Plugins

\n\n\n\n\n\n\n\n

Project

\n\n\n\n\n\n\n\n

Test

\n\n\n\n\n\n\n\n

Themes

\n\n\n\n\n\n\n\n

Training

\n\n\n\n\n\n\n\n

Online Workshops

\n\n\n\n\n\n\n\n

Tutorials

\n\n\n\n\n\n\n\n

WPTV

\n\n\n\n\n
\n
\n\n\n\n
\n\n\n\n\n\n\n\n\n\n\n\n

Thanks for reading our WP dot .org roundup! Each week we are highlighting the news and discussions coming from the good folks making WordPress possible. If you or your company create products or services that use WordPress, you need to be engaged with them and their work. Be sure to share this resource with your product and project managers.

Are you interested in giving back and contributing your time and skills to WordPress.org? \"🙏\" Start Here ›

Get our weekly WordPress community news digest — Post Status\' Week in Review — covering the WP/Woo news plus significant writing and podcasts. It\'s also available in our newsletter. \"💌\"

\n\n\n\n
\n\n\n\n
\"Post
\n

You — and your whole team can Join Post Status too!

\n\n\n\n

Build your network. Learn with others. Find your next job — or your next hire. Read the Post Status newsletter. \"✉\" Listen to podcasts. \"🎙\" Follow @Post_Status \"🐦\" and LinkedIn. \"💼\"

\n
\n\n\n\n
\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 20:57:41 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Courtney Robertson\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:18;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:80:\"WPTavern: #59 – Corey Maass on How To Use WordPress To Kickstart Your SaaS App\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"https://wptavern.com/?post_type=podcast&p=141113\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:94:\"https://wptavern.com/podcast/59-corey-maass-on-how-to-use-wordpress-to-kickstart-your-saas-app\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:46303:\"Transcript
\n

[00:00:00] Nathan Wrigley: Welcome to the Jukebox podcast from WP Tavern. My name is Nathan Wrigley.

\n\n\n\n

Jukebox is a podcast which is dedicated to all things WordPress. The people, the events, the plugins, the blocks, the themes, and in this case, how WordPress can be used to get your SaaS app off the ground.

\n\n\n\n

If you’d like to subscribe to the podcast, you can do that by searching for WP Tavern in your podcast player of choice, or by going to WPTavern.com forward slash feed forward slash podcast. And you can copy that URL to most podcast players.

\n\n\n\n

If you have a topic that you’d like us to feature on the podcast, I’m keen to hear from you and hopefully get you or your idea featured on the show. Head to WPTavern.com forward slash contact forward slash jukebox. And use the form there.

\n\n\n\n

So on the podcast today, we have Corey Maass.

\n\n\n\n

Corey is a full stack developer who works with agencies and businesses, large and small. He specializes in advanced WordPress functionality and building products for, and using, WordPress.

\n\n\n\n

Over the last decade or so SaaS, or software as a service, apps have become more and more popular. Not only are we using our computers more, but with the rise of smartphones, we’re connected to our services all the time. There does not appear to be any corner of life where online platforms don’t have some presence. From email to taxis, fitness to food planning and delivery. You can find it all in a SaaS app somewhere.

\n\n\n\n

Now that many people are comfortable using SaaS apps, there’s been a deluge of new players coming into the market, but it won’t surprise you to learn that most of them fail to make an impact and shut up shop.

\n\n\n\n

Corey is on the podcast today to talk about why he thinks that building an MVP, or minimum viable product, app on top of WordPress is a good way to start your product journey.

\n\n\n\n

We talk about how WordPress comes bundled with many of the features that apps require. User login, roles, permissions, and the REST API. This means that you don’t have to reinvent the wheel for the things that WordPress already does.

\n\n\n\n

On top of that, the plugin ecosystem which surrounds WordPress, might enable you to short circuit the need to build all the features that your service needs. It may be that there’s an existing plugin, which does most of what you require, and is ready to go right away.

\n\n\n\n

Corey talks about how using WordPress in this way might enable you to see if there’s really a market for your app. And if there’s not, you’ve used less resources finding that out. And if there is, then you might have some revenue to develop the app in other ways.

\n\n\n\n

If you’ve toyed with the idea of creating a SaaS app in the past, but never quite got there, this episode is for you.

\n\n\n\n

If you’re interested in finding out more, you can find all of the links in the show notes by heading to WPTavern.com forward slash podcast. Where you’ll find all the other episodes as well.

\n\n\n\n

And so without further delay, I bring you Corey Maass.

\n\n\n\n

I am joined on the podcast today by Corey Maass. Hello, Corey.

\n\n\n\n

[00:03:58] Corey Maass: Hey there.

\n\n\n\n

[00:03:58] Nathan Wrigley: Very nice to have you on. Corey, we’re going to talk today all about the capabilities of WordPress as a SaaS platform. But as we typically do on this podcast, it would be very nice if we could orientate the listeners, allow them to figure out what your credentials are, what your WordPress chops are, if you like. So would you spend a few moments just giving us a brief potted history of your relationship with tech and WordPress more specifically?

\n\n\n\n

[00:04:24] Corey Maass: Absolutely. Back in the late nineties in college, a roommate of mine introduced me to this internet thing and the first websites I saw were some of my favorite bands. And I was a aspiring musician at the time, and I said, well, I want to appear as famous as they are. How do I make one of these website things, and the rest is history.

\n\n\n\n

I taught myself basic web design, web development. That led to learning some programming, JavaScript and then ASP classic way back in the day. But around that time there was the new trend of SaaS apps. 37 Signals was popular talking about this. Forums like Joel Spolsky’s, Joel on Software. And I caught the bug because I’ve always had an entrepreneurial streak.

\n\n\n\n

So I said, oh, this internet thing, building software, but not selling a download, but selling access to a website. So, I started going down that path, building websites for clients, but also building SaaS apps to try to sell on the side. And then WordPress took off and for a number of years, WordPress was pretty much my day job. Doing development or website setup or what have you, and then building Sass apps. Not using WordPress for a number of years.

\n\n\n\n

And then suddenly the light bulb went off. One, the WordPress market was getting bigger and bigger, and I realized that there actually was money in it. So that led me to start building plugins, which I think is what had you and I talking last time. But also at some point it occurred to me that WordPress had matured enough and solved enough of the problems that I was encountering over and over building SaaS apps that I said, let me look at WordPress as a SaaS platform, and I’ve been doing it ever since. So now it’s been probably five years or something, and WordPress only continues to mature, and this conversation continues to evolve.

\n\n\n\n

[00:06:27] Nathan Wrigley: So you, in the last few years, you’ve joined together the idea of a SaaS platform, but with WordPress handling some of the basic things in the background, if you like. I say basic, I just mean some of the things that we are more familiar with in WordPress. So user management, obviously if you throw some other things like WooCommerce at it, you may be able to handle billing or subscription or whatever it might be, and getting people to the right page depending on whether they’re logged in or not. Is it basically the promise of that? You can cut out a whole body of work, which you would need to build, well potentially from scratch, each time you create your own new SaaS app?

\n\n\n\n

[00:07:04] Corey Maass: Yeah, I think that’s the way to think about it. So, when you’re solving problems for people online, these days it’s definitely more broad than it was five years ago and 10 or 15 years ago, of course. So if you’re building something that’s B2B, technically speaking. So if you’re trying to build an API or some sort of true service that other systems are going to talk to. WordPress is probably not the answer you want.

\n\n\n\n

The REST API is, has come a long way, but it’s not really what it’s meant for, right? But if you think of most B2C apps, business to consumer, most of these apps are websites that you’re signing into. Well, WordPress accommodates that. You’re clicking through from page to page. WordPress accommodates that. You’re taking billing, you’re handling subscriptions. WordPress with WooCommerce or Easy Digital Downloads, or Restricted Content Pro or any number.

\n\n\n\n

I’ve been paying more attention to the membership plugins lately, which are in some ways are specifically designed to handle exactly this problem. Users signing in and doing something, interacting. Interacting with the website. Interacting with each other, that kind of thing. One of the things that, an example that I pick up on a lot is, years ago when I was building apps regularly for clients, for friends, for myself. Over and over and over again, I had to implement some sort of user password reset. And it’s so mundane. Once you’ve solved it once, it’s boring to solve as a developer. But it’s crucial to every app.

\n\n\n\n

And I got to the point where I was like, I just don’t want to ever think about this stupid problem again. But I had to integrate the code, again every time over and over again. It’s like with WordPress, I never have to think about that. And there’s a plugin called Theme My Login, that’s one of my favorites that you drop in and users can register for your website and immediately get access to a slash dashboard, which you can change. But arguably that’s the first huge leap, you set up a basic website.

\n\n\n\n

You want users to be able to register and have exclusive access to a page that they don’t have if they haven’t signed in or haven’t paid or what have you. So, these kinds of plugins just solve all of these basic problems. The bottom of the pyramid, so to speak. So that you can get onto whatever problem, your unique problem, that your SaaS is going to solve. As opposed to spending days, weeks, months, tackling the not unique problems like user registration.

\n\n\n\n

[00:09:36] Nathan Wrigley: So what you are suggesting here, let’s just lay this out. The audience that you are suggesting this to, is people who want to get something shipped quickly. And really, if you are at the beginning of your SaaS app journey, you’re not quite sure yet whether the market even exists. You’re just trying to float a solution to something that you believe might be viable in the marketplace, but you’re not sure.

\n\n\n\n

So we’re creating a shortcut. We’re offsetting the billing, the user management and so on to WordPress, just as a, as a quick way of getting an MVP or a minimum viable product out there. Is that the idea? Just to sort of test the water? WordPress is a good bet for that, and then presumably at some point you would advise that if it turns out to be an out and out success, then maybe, at that point you might need to look at different tooling.

\n\n\n\n

[00:10:28] Corey Maass: Not necessarily. There was a time when I would’ve said that definitively, but WordPress has come a long way. Hosting has come a long way. Optimization has come a long way. So it’s definitely the scenario that I’m using WordPress the most. I’ve got a new idea, or I’m working with somebody and they’ve got a new idea and this is how I want to get it off the ground.

\n\n\n\n

But there are a number of companies, big companies, in the WordPress space that continue to work, use WordPress as the core of their SaaS app, and they’ve got plenty of customers. I think it really, when you get to that level of, if you see a, a good amount of success, then there’s going to be technical problems to overcome.

\n\n\n\n

And so it’s either ramping up hosting, server power or optimizing queries or rewriting certain aspects of your app. We can talk about that. I had to do that for one of mine, about a year ago. Or again, depending on the amount of user inactivity or user, user interactivity, how much and how often your users are using your app, you may find that it handles it just fine.

\n\n\n\n

[00:11:43] Nathan Wrigley: So right at the beginning you started talking about why you use WordPress. You mentioned a few plugins, which might assist you on this journey. So I think some of the ones that you mentioned were things like Easy Digital Downloads, WooCommerce, and so on. Whilst I don’t want to necessarily promote certain plugins, I’m just wondering if, given the experience that you’ve had, if you could give us some tips as to plugins that you have found to be helpful for particular problems that you’ve faced while you’ve been trying to build it. And then in a few moments we’ll get onto the subject of how you’ve had to amend WordPress to do things, let’s say more efficient.

\n\n\n\n

[00:12:20] Corey Maass: Sure. So these days, I actually use Beaver Builder for building pages out. Beaver Builder’s a page builder. Elementor is another good one. But I find that doubling down and knowing these tools well, helps greatly with being able to solve a variety of problems because they’re not a theme, so they’re not locked into a certain layout or that kind of thing.

\n\n\n\n

But most SaaS apps have a pattern called CRUD, create, retrieve, update, and delete. So if it’s Twitter, then you are creating tweets. You are retrieving tweets, meaning you’re viewing all of them. You can’t really update tweets, but you can update your profile, that kind of thing. And again, you can’t really delete tweets, but you could delete your account, and that kind of thing. Facebook, you can create posts, you can delete posts, your viewing posts, so your retrieving posts, that kind of thing.

\n\n\n\n

So, a lot, a lot, a lot of software comes down to that pattern, and so using something like, Advanced Custom Fields and there’s a great plugin called ACF Front End, I think it’s called, that essentially puts an ACF form on the front end. So that’s how users can create and update. You could also use Gravity Forms. Or there are a couple of other plugins, form plugins, that you can then put on the front end, for again, collecting data from users or letting users post data. Essentially insert data into the database. And then using something like Beaver Builder or Elementor that have post modules.

\n\n\n\n

So it’s like if I was recreating Twitter, I would create a form, and this obviously once I’m logged in, but I would create a form that said, what do you want to tweet? And that would insert it into the database as a post record. And then I would use Beaver Builder, me personally, but you could use Elementor or again, any number of page builders, with a posts module that says, okay, show all posts, meaning tweets, with the author of Corey. So then you’ve just created a way to create tweets and then for somebody else to go look at all of Corey’s tweets, that kind of thing.

\n\n\n\n

So thinking, breaking it down to these kinds of patterns and then looking at these different plugins on how to solve them. A lot of the time I’m able to find ways to quickly implement. And it, again, it doesn’t have to be quick, and this doesn’t have to be forever, but a lot of the time it can be where WordPress and these plugins can solve these problems so that my SaaS offers the, again, the unique problem or solves the unique problem that I’m, the whole reason I’m building it in the first place.

\n\n\n\n

To get back to your question about those other plugins in particular. If you only want users to sign in, I love the plugin called Theme My Login. Again, look at membership plugins. And then, if you want to charge, again, break down the problem. What are you actually, what do you want? Usually you want subscriptions, like that’s a SaaS pattern that most people are used to now. And what are users paying for? Usually they’re paying for access to a page or pages or content or some feature to interact with other users or something like that. And there are plenty of plugins that restrict content. Which is the way to think about that.

\n\n\n\n

And so there’s literally Restricted Content Pro as a plugin. Easy Digital Downloads, which is e-commerce, but they have an add-on for restricting content. WooCommerce is really more e-commerce, but can handle this kind of stuff. And then again, membership plugins that are, as people are setting up communities, as at least some people are trying to get away from social media and get back to more private communities without relying on Facebook groups or Twitter or what have you.

\n\n\n\n

Membership plug-ins have been mature for a while, but are, I’m seeing them become even more and more popular. And are designed exactly for this. So a user pays for access to features, pages, what have you. And that’s again, kind of the core of most SaaS apps.

\n\n\n\n

[00:16:24] Nathan Wrigley: I suppose that if you are thinking of building a SaaS app, you must have some kind of kernel of an idea of whatever it is that you are trying to solve. So, you’ve got this fabulous idea, and the most important thing at that point is to judge whether or not this idea A, can be built, and let’s assume that after sitting down and thinking it through and mapping it out, you’ve decided, yep, yeah, this has got legs. This can be built with the technology that’s currently available on the web.

\n\n\n\n

And then thinking, okay, is there an audience for this? Are there going to be enough people out there who are willing to open their wallet to make it worthwhile? And if you go down the SaaS route, you may very well be an incredibly adept developer, in which case this may be in your purview.

\n\n\n\n

But if you are not and you are just trying to figure out whether the market is there and you want to do that affordably, then WordPress seems like a fairly decent bet, just because of what you said. The fact that with 60,000 plus plugins in the WordPress repository and countless more that you can purchase, in many cases for a very small amount of money.

\n\n\n\n

It may be that you can get 90%, 80%, 70% of the features that you are trying to build, but without having to do much in the way of custom coding. It may be that you can’t get a hundred percent of the way there, and that would require some tweaking, which we’ll get into. But is that essentially it? You know, you might have to cut some corners or, on your roadmap, cut out some of the things that you really thought would be nice to have in and just go for the things which can be enabled quickly and affordably.

\n\n\n\n

[00:17:58] Corey Maass: Yeah, I think it just depends on what you’re trying to accomplish. I have a buddy who is non-technical, knows enough CSS to be dangerous, which he’s learned over times, specifically for this scenario. He wanted to create a mentor program, and so he needed scheduling for matching mentorees to mentors.

\n\n\n\n

So we found a plugin that did that, or did that well enough. And then put I think a membership plug in. I don’t remember how he handled subscriptions. But basically put WordPresses stylized user management in front of it. Limited access to features based on a user being logged in or a user paying. And then a little bit of CSS to make it look a little more integrated or little more branded or what have you.

\n\n\n\n

And that was kind of all he needed. It solved the problem. He was able to charge for it. He got some customers. And then at some point he did end up hiring a developer to add a few bells and whistles or whatever features he found that were missing. But yeah, it got him 70, 80% of the way. Arguably it got him a hundred percent of the way of solving the problem enough that at least users could start using it.

\n\n\n\n

[00:19:10] Nathan Wrigley: Yeah, I suppose that’s it, isn’t it? If he’s got a core body of users, and he’s determined that, in this case he can use a calendar plugin or whatever it may be, and it will get him the user base that he needs. Then he can start to use the revenue that’s generated from the, let’s call it the SaaS app, to invest in having something done bespoke.

\n\n\n\n

That’s really interesting. That’s kind of nice to know. I guess one concern, which I may have, and I’m sure you’ve come across this before. Is just the notion that if you did build this and you fully had the intention of it staying on WordPress for all time. Then you are of course very much dependent upon the plugins that you are using. The spaghetti of plugins being updated regularly.

\n\n\n\n

In many cases that would very much be the case. It’s updated frequently. It’s made secure, and any vulnerabilities and things like that are taken care of. But there is always that chance that the developer of a key part of your SaaS app may just decide to call it quits, and then you might be left hanging a little bit.

\n\n\n\n

[00:20:14] Corey Maass: And the scenario I’ve seen more often is a mature product. Meaning your own SaaS app evolves away from what the plugin that you purchased does. So I saw this with a very big company in the WordPress space, who long ago had built their platform on top of EDD, Easy Digital Downloads. But over time had hacked and slashed at it, so that they couldn’t update it anymore.

\n\n\n\n

And that’s just a decision they had to make at some point of whether they were going to keep going with EDD and just lean into the features that EDD had and forego the other features. Or most good, big WordPress plugins are well documented and have hooks so you can add function extra functionality, or figure out how to sort of hack around them, to a point.

\n\n\n\n

And then, yeah. They had to make the decision to just stop updating it, and there was discussion. Last I heard that they were going to maybe move to something custom altogether. But the idea being, one of my favorite phrases, we made the best decision we could with the information we had at the time, right?

\n\n\n\n

So starting out early. It solves all your problems. Go for it. And then down the road you can migrate away from it. You can code around it. You could build something custom, what have you. But yes, that is certainly a risk. I mean, it’s also a problem that a lot of apps have broadly speaking. So it’s, you know, if you’ve built an app that uses the Twitter or Facebook API, you’re putting yourself in their, their hands.

\n\n\n\n

Or if you are operating system dependent or even, something I’m seeing right now is, microchip dependent, right? If you build software for MacOS and it only works on Intel and, and they move to M1 or M2. So these are just risks that I think you assess over time.

\n\n\n\n

But what I like is, the point you keep emphasizing, that this is a, a way to solve the technical problem. What I think that a lot of SaaS founders, small and large, real and imaginary, don’t take into account and, I struggle with, and most of us struggle with, is that these days the technical lift of building an app often pales in comparison to the marketing.

\n\n\n\n

We hear about these wonderful, amazing stories, like Instagram selling for whatever it was, 8 billion after two months, and yada, yada, yada. Most SaaS apps fail. And so you, you want to build quickly with a low lift and then spend most of your time, like you said, trying to get it in front of customers, validating the idea, getting feedback from customers about what features they actually want, or now that you’ve built the features they want, does it actually solve the problem for them?

\n\n\n\n

All of that is arguably way more important than the actual platform you use. But that’s what brings me back to WordPress as a platform, is in fact often a great way to get something out the door. Even if it’s just a form to collect data and then a page builder or a theme of some kind to then show the data back to the user, if that’s what solves the problem.

\n\n\n\n

[00:23:36] Nathan Wrigley: It’s interesting because if there’s a body of people listening to this who are not building SaaS apps on WordPress, and they’re just building client websites, you’ve probably encountered that scenario where the client comes and they have incredibly grandiose expectations of what they want the website to do.

\n\n\n\n

And because you’ve been building websites for so long, you just know, you have an instinct which says, well, we could build all of that. But how about we just start here? Because I would imagine it’s quite unlikely that your staff are actually going to start using some kind of intranet solution that we build as WordPress. Or some messaging system that we build in the app. It’s much more likely that they’ll continue to use things like Facebook Messenger or WhatsApp or Slack or whatever it may be.

\n\n\n\n

And so over the years you’ve become accustomed to figuring out what is plausible, what is likely to work, and I think I feel it’s the same with SaaS apps. It’s very easy to come to the table. You’ve got your blank canvas and you throw everything at it, every idea, every permutation, every possible thing that the app could do, and then decide that’s what must be built.

\n\n\n\n

That’s it. Until that is all done, we’re not going to launch it. And I think history shows that you have to be much more agile than that. You have to be able to drill it down and say, okay, what’s the 10, 20, 30% of all of that, that we’ve decided upon, which is going to get us off the ground? And so that feels like where this goes. If you try to build everything, it’s probable that you’ll A run out of money, B run out of time, and nothing will be shipped.

\n\n\n\n

Whereas in your scenario, offset the uninteresting jobs that probably don’t need to be tackled because they’ve already been tackled by plugins or WordPress Core. And just concentrate on the things which are going to benefit your users. And frankly, you don’t know what is going to benefit your users.

\n\n\n\n

It’s always amazing to me when I open up a new SaaS app that I’ve never use before. And you think, oh, this will be perfect what I need. And you end up on support saying, does it do this? No, I wish it did that. And those companies that succeed tend to be, well in my experience, the ones who listen to their early adopters and quickly pivot their solution to satisfy them.

\n\n\n\n

[00:25:45] Corey Maass: Exactly. There’s obviously no harm in thinking through what your dream app does, all the features. You make a long, long list. But one of the things that drew me to WordPress plugins, and selling WordPress plugins early on, was a rather cynical observation that I made.

\n\n\n\n

I was building blogs for customers. I was building e-commerce websites for customers. And instead of writing another article, which is hard and work. Or instead of inserting more products, which is hard and feels like work. A lot of my clients would get in the WordPress plugin repo where all the plugins are free and go, oh, I could use a to-do list plugin and they’d install it.

\n\n\n\n

Or, it’s winter. I should install a plugin that adds snowflakes falling over my theme. And they would waste an unbelievable amount of time on what felt productive and felt free. And I was like, well, if people are people, we are all human, we are all valuable and we are all, don’t want to do the things that are hard.

\n\n\n\n

But I see all these people that are spending time just digging through the plugin repo, I’m going to start building and selling plug-ins, because the discoverability is amazing. And so I think you’ve touched on that for SaaS as well, which is, we generally shy away from the things that are hard.

\n\n\n\n

We also tend to skew towards our own genius. What we think is the best idea. Because we thought of it isn’t necessarily the features, or it isn’t ecessarily solving the problem that your actual paying customers have. The real strength, and the real challenge, comes more in that side of things. Marketing, sales, talking to customers, getting over your own ego, optimizing your own time, all that kind of stuff.

\n\n\n\n

[00:27:48] Nathan Wrigley: Yeah. It’s interesting the marketing piece you mentioned. Never ceases to amaze me how much of the overall budget needs not to be sunk into the development of the actual software, but in alerting people to its existence. A significant amount. And it’s not to be underestimated.

\n\n\n\n

And obviously if at the beginning you sink a hundred percent of your finances into the code, that’s great, but I guess you better be a really good word of mouth, somebody that can spread by word of mouth incredibly successfully. Because experience at least tells me that it’s very hard to gather an audience from a standing start.

\n\n\n\n

So we’re a WordPress podcast. We’re obviously very keen on WordPress, we think it’s amazing. But I’m guessing that there must be downsides to this. Let’s just talk about that for a moment. Any drawbacks to this system that you’ve encountered over time? Just some quick examples may be that, well, does it scale very well? Does WordPress tend to be doing a lot of things in the background that a leaner, more specifically custom-built solution may get you out the hole of? Just questions around that. Any drawbacks that you would alert people to if they do decide to go down this approach?

\n\n\n\n

[00:28:59] Corey Maass: A few years ago, I was tasked with building a food subscription website. So think Blue Apron or Freshly kind of website, if you’re familiar with those. And for better or worse was told that I had to use WooCommerce. And so I spun up a WordPress website, installed WooCommerce, got subscriptions going, customized the choose the meals that you want, and then check out. And that all was okay.

\n\n\n\n

But it turned out that, I think some of this has been changed, because this was a number of years ago but, WooCommerce was storing all of the data in a very WordPressy way, which was fine because it was a known pattern. But was not very optimal. And then for the business, because all of those meals were cooked every morning and then shipped out, all of the charges had to go through at the same time, at like two in the morning. And it turned out that WooCommerce subscriptions was built so that if you signed up for a subscription at 10:30 in the morning, it would renew at 10:30 in the morning. While we needed it to renew at two in the morning so that all of the orders went through, so then the chef knew how many dishes to make, and how many chicken dishes to make or whatever.

\n\n\n\n

And that’s the kind of risk that you run into, right? So if you are using a third party piece of software, WordPress, and then with plugins. And you are essentially building it to your, or bending it to your will, so that it’s doing things that it’s not necessarily meant to do. You’re going to run into issues.

\n\n\n\n

We found that our server didn’t have enough power to process all of these orders at the same time, because it’s essentially multiple threads need to be run at the same time. We wound up in that instance sticking with WooCommerce and WordPress for at least a little while longer.

\n\n\n\n

But switching off of a hosting company that really was most popular for blogs and delivering content and not necessarily running process, CPU power. And moving to a custom AWS set up. And we watched the CPU go from 80% all the time, to 3% all the time. So in that instance, we just needed to throw more metal at it.

\n\n\n\n

But again, we were definitely using a tool, at least slightly, in ways that it wasn’t meant to do. I also, during the pandemic, or at the beginning of the pandemic, my wife made the mistake of turning to me and saying, you know, my family plays this game called Mexican Train, in person all the time. Boy, I wish there was an online version. And she should just know better than to put that kind of idea in my head.

\n\n\n\n

So within a couple of months I had spun up the only interactive online version of Mexican Train, which was great for our family, but it’s a very popular game in retirement communities. And naturally during the pandemic a lot of people in retirement communities were isolating a lot more. The game became quite popular, because it spread word of mouth. And the first Christmas, I think I built it early in the year, and, and the first Christmas it peaked at like 2,600 concurrent games or something. Which, for me, I had never built anything that needed quite that much power.

\n\n\n\n

And it did eventually fall over. But initially I’d built it so that every time somebody played, all the other games, so four people are playing, basically all four games are sitting there pinging the server, looking for updates. That’s very inefficient because most of those pings don’t return anything, but the CPU still has to accommodate them. So I wound up switching to a pushing system. So I had to integrate with that. And originally I had built it so that the game itself, so when you’re signing into mexicantrain.online, that’s the website, the login screen you’re seeing is Theme My Login.

\n\n\n\n

All of the delivery of content, so like when you go to the My Games page and you see all of your games, that’s just Beaver Builder. And then the actual game I had to build, so it was quite a lift as far as development goes. But that was what that SaaS needed. But I built an app in a JavaScript framework called React that then talks to the server.

\n\n\n\n

Well, I built the initial version using the WordPress API. So my game talked to WordPress, functionality that was built into WordPress. And the API worked, until it didn’t. So, in that instance, again, too many people hitting the server too much. Aw, shucks, it was too successful.

\n\n\n\n

I had to revisit it after a year or two and build a custom API. Now I’m a developer. I have that luxury, right? But these are things that, I got enough of a version out the door. So, thinking about it from the perspective of a non-developer. I could have set up most of it except for the game itself.

\n\n\n\n

And the game is sponsored by donations. So I installed GiveWP, which is one of the bigger WordPress donation plugins. And I still used the free version. And so I got most of those sort of basic stuff using third party plugins out of the box. And then if I wasn’t a developer, I might have had to hire a developer.

\n\n\n\n

And so yes, I would’ve had to put some money into it. But they wouldn’t have had to build everything. And I also could conceivably hire different developers, or I could by using WordPress. So one of the things we haven’t talked about is because of the popularity of WordPress, you also have a lot more developers to choose from if you’re going to hire somebody.

\n\n\n\n

But anyway, if I wasn’t a developer, I would’ve had to hire somebody to build the game. And then down the road, presumably I would’ve proven that the platform was popular, hopefully in the form of donations, which would’ve been enough money to then hire somebody to rebuild the API, if I couldn’t have done it myself.

\n\n\n\n

You know? So there’s sort of this evolution of, as you’ve said. Try things, see if it’s popular, and then maybe hire somebody if you have to, you know, if you’re going to grow parts of the platform, parts of the app beyond WordPress.

\n\n\n\n

[00:35:40] Nathan Wrigley: It’s really interesting you mentioning about all of the very large number of WordPress developers. The developers I guess, go into different niches, don’t they? They might be experts in one field or another. Do you detect that there’s a lot of people doing this kind of thing? Building SaaS on top of WordPress. Or is it just you shouting into an empty room? What I’m basically saying is, is there a community, a subset of the WorldPress developer community who, like you, are interested in building SaaS apps on top of WordPress.

\n\n\n\n

[00:36:10] Corey Maass: There is a book called Building Web Apps with WordPress that came out from O’Reilly. So it’s popular enough that people are writing books about it. I’ve given talks on it at a few different WordCamps as far back as I think four or five years ago or more. And I’ve come across a number of people who are doing it, or are thinking about it or have done it. But it’s definitely not, and even Mullenweg has talked about it, but it’s not the most common use case.

\n\n\n\n

I think in part because people just don’t necessarily think about SaaS apps separately as much anymore. More and more websites do something. And so if they have functionality, maybe that people are paying for, and users are signing in to use the web app to do something.

\n\n\n\n

It’s a SaaS app. But that’s, again, I think more and more commonly just how people view websites. So it’s not necessarily something that people are thinking about or searching for. Except for, I think, as you’ve mentioned a few times, if you’re looking for no code now means something different. But if you’re looking for a non-developery way to spin something up quickly using third party software, then it still gets some attention. But to answer your question, no, I’ve never found a community. I’ve thought about starting one, but never have. Because I just haven’t gotten a sense that enough people are talking about it.

\n\n\n\n

Which is okay. Maybe at some point they will, or, you know, maybe some other better solution will come along and consistently solve the problems. But, right here, right now, I still find WordPress a great option.

\n\n\n\n

[00:37:57] Nathan Wrigley: It’s really interesting because curiously, there’s a great deal of overlap with something that’s going on in my world at the moment in that I have been working with a developer on a SaaS app. I won’t go into the details, but reached a point where a couple of years ago, the interest in it, from my point of view, I think probably, is best to describe it. It waned a little bit and so it went on the back burner and it’s never been revived.

\n\n\n\n

And as a couple of years have gone by, I’ve decided that, actually wouldn’t it be nice to revive this? And so with a couple of friends decided that, yeah, let’s give this another go. But actually, let’s just begin again, because I’ve noticed there’s significant things in what’s already been built that I would change.

\n\n\n\n

And guess what we’ve decided to do? We’ve decided to do the MVP inside of WordPress. Basically for pretty much all the reasons that you’ve suggested. We’re familiar with it. There are sometimes free, sometimes commercially available plugins, which will do a significant amount of the lifting. Will it be exactly what we would like from our roadmap? No. Will it be close enough to get us to measure whether there’s an audience for this? Yes, I think it will. And so, curious that this is actually playing itself out in my life at this moment.

\n\n\n\n

[00:39:19] Corey Maass: Nice, yeah. Depending on the problems you’re trying to solve, but I think that’s like most things, a bit of planning, sit down, design. I encourage everybody to do this. What is the all the bells and whistles version. We nerds are a big fan of what’s called the 80 20 rule.

\n\n\n\n

So what’s the 20% that needs to be solved now, today to prove the idea? And then see what plugins align with that. How they can get you there. Will it solve the problem? Do you need custom development? Are there features that just don’t have solutions or aren’t solved by any of the plugins you might want to use.

\n\n\n\n

And then go from there. See what you can do. The nice thing too about WordPress is you can start locally, which is free. Locally meaning on your computer, not locally in your town, although you can do that too. Most computers using software like Local WP, I’m a big fan of, and there’s a few others. Also InstaWP, which lets you spin up instances of WordPress online for free, for, you know, seven days or something, and then pay to keep them, or you can download them, I think, I don’t know.

\n\n\n\n

I definitely have been guilty of getting an idea and I needed to illustrate the idea rather than just write the idea down. So I spun up an instance of WordPress real quick. Installed a couple of plugins real quick, and then said, what do I need next? Or what would the next step be? Or, if I was a user, what would I expect to see next? All that cost me was a little bit of time. There’s kind of that advantage too, where it’s, you can use it for wire framing means something specific, but conceptually you can use it for wire framing ideas, which I think is crucial. Without it costing you anything.

\n\n\n\n

[00:41:04] Nathan Wrigley: Corey, if people listening to this, if they’re resonating with it and they’re thinking actually, do you know what, this is something that I’ve been doing for a while, or, I’m curious to get into the community that you said might need to exist. Where would be the best place to get in touch with you?

\n\n\n\n

[00:41:20] Corey Maass: Honestly, the place that I talk about this the most is Twitter. twitter.com/coreymaass, c o r e y m a a s s. Just start a conversation with me. I’d love to hear people who are interested in this. If this resonated with them, if they’ve tried it at all. Because again, I’ve run into people who have done it. I’ve heard about people doing it. A book exists. So there must be people talking about it somewhere.

\n\n\n\n

But I think it would be neat to have a community of people, or even just a network of people, helping each other out, solving some of these problems. Hey, does anybody have a good recommendation for a plugin that solves such and such a functional, or a problem that I have. Where should I start? Suggestions for hosting companies. I mean, there’s, there’s always information to be shared. And honestly, that’s one of my favorite things about the WordPress community is that it’s so open. So many people are talking to each other and willing to help each other. I definitely think there could be more conversation around using WordPress as a SaaS platform.

\n\n\n\n

[00:42:21] Nathan Wrigley: Corey Maass. Thank you for chatting to us on the podcast today.

\n\n\n\n

[00:42:25] Corey Maass: My pleasure.

\n
\n\n\n\n

On the podcast today we have Corey Maass.

\n\n\n\n

Corey is a full-stack web developer who works with agencies and businesses, large and small. He specialises in advanced WordPress functionality and building products for, and using, WordPress.

\n\n\n\n

Over the last decade or so, SaaS, or software as a service, apps have become more and more popular. Not only are we using our computers more, but with the rise of smartphones, we’re connected to our services all the time.

\n\n\n\n

There does not appear to be any corner of life where online platforms don’t have some presence. From email to taxis, fitness to food planning and delivery. You can find it all in a SaaS app somewhere.

\n\n\n\n

Now that many people are comfortable using SaaS apps, there’s been a deluge of new players coming into the market, but it won’t surprise you to learn that most of them fail to make an impact, and shut up shop.

\n\n\n\n

Corey is on the podcast today to talk about why he thinks that building a MVP, or minimum viable product, app on top of WordPress is a good way to start your product journey.

\n\n\n\n

We talk about how WordPress comes bundled with many of the features that apps require, user login, roles, permissions and the REST API. This means that you don’t have to reinvent the wheel for the things that WordPress already does.

\n\n\n\n

On top of that, the plugin ecosystem which surrounds WordPress might enable you to short circuit the need to build all the features that your service needs. It may be that there’s an existing plugin which does most of what you require, and is ready to go right away.

\n\n\n\n

Corey talks about how using WordPress in this way might enable you to see if there’s really a market for your app. If there’s not, you’ve used less resources finding that out. If there is, then you might have some revenue to develop the app in other ways.

\n\n\n\n

If you’ve toyed with the idea of creating a SaaS app in the past, but never quite got there, this episode is for you.

\n\n\n\n

Useful links.

\n\n\n\n

37 Signals

\n\n\n\n

Joel Spolsky’s, Joel on Software

\n\n\n\n

Easy Digital Downloads

\n\n\n\n

WooCommerce

\n\n\n\n

Advanced Custom Fields

\n\n\n\n

ACF Frontend

\n\n\n\n

Gravity Forms

\n\n\n\n

Beaver Builder

\n\n\n\n

Elementor

\n\n\n\n

Theme My Login

\n\n\n\n

Restrict Content Pro

\n\n\n\n

Corey’s Mexican Train website

\n\n\n\n

GiveWP

\n\n\n\n

Building Web Apps with WordPress book

\n\n\n\n

Local WP

\n\n\n\n

InstaWP

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 15:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Nathan Wrigley\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:19;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:46:\"Do The Woo Community: Looking at Code as Words\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74188\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:45:\"https://dothewoo.io/looking-at-code-as-words/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:425:\"

The hurdle is getting past looking at code and saying, \"Oh, this is code. I can\'t understand it.\" You\'re not looking at zeros and ones, you\'re looking at words you can understand.

\n

>> The post Looking at Code as Words appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 11:07:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:20;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:125:\"WPTavern: Jetpack Revamps Mobile App, WordPress.com Users Must Migrate to Keep Using Stats, Reader, and Notification Features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141116\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:133:\"https://wptavern.com/jetpack-revamps-mobile-app-wordpress-com-users-must-migrate-to-keep-using-stats-reader-and-notification-features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6594:\"

When Automattic launched a mobile app for Jetpack in June 2021, it was targeted mainly at users who were on a paid Jetpack plan, as it enables access to features like backups, restores, and security scanning. Most importantly, the app gave Automattic a more direct path for monetizing Jetpack, without adding more commercial interests into the official WordPress apps.

\n\n\n\n

This week Jetpack announced that it has revamped the app and is offering a more compelling reason for those using the free plan to migrate. As part of a longterm effort to refocus the official WordPress apps, features that require Automattic’s products (the Jetpack plugin or a WordPress.com account) in order to use them, will soon be removed. This includes the Stats, Reader, and Notifications features, which have been relocated to the Jetpack app.

\n\n\n\nWordPress.com announcement for the revamped Jetpack app\n\n\n\n

WordPress.com users and Jetpack users on the free plan who previously relied on these features will need to switch to the free Jetpack mobile app. All the features that are moving over from the core WordPress app will still be free in the Jetpack app.

\n\n\n\n

While most self-hosted Jetpack users may easily understand the need for the switch, this transition may be rougher for WordPress.com users who do not understand the history of the mobile apps and see it all as “WordPress.” They may not be aware that Automattic’s integrated products have been controversial features in the official WordPress apps for nearly a decade.

\n\n\n\n

The announcement on WordPress.com is confusing, as it presents Jetpack as just a new optional app and doesn’t convey the urgency of migrating if users still want access to stats, notifications, the reader, and any additional paid features.

\n\n\n\n

The post’s FAQ section describes the Jetpack app as “the premium mobile publishing experience for our super-connected world” and states that “the Jetpack app is free to download.” WordPress.com users who commented on the post found the words “premium” and “free to download” to be suspicious and confusing. They don’t understand the reason for two apps:

\n\n\n\n
\n

“Do we have to change over? I only want to blog, I’m not technical and I don’t understand why you have done this or how to use it?”

\n
\n\n\n\n
\n

“So is WordPress now called Jetpack?”

\n
\n\n\n\n
\n

“If it ain’t broke, don’t fix it. This move is not in your users’ best interests so why is it being done? This smacks of the recent pricing debacle.”

\n
\n\n\n\n
\n

“I’m really disappointed by this decision. Why are you forcing us to use two apps? Your explanation of the differences makes no sense, and sounds like you made a decision for some reason you won’t tell us and you’re just trying to justify it. This is not user-focused at all.”

\n
\n\n\n\n

Users are also concerned about data loss, as those who are migrating to the newly revamped app are advised to delete the WordPress app after installing the Jetpack app. The announcement states that “Managing your site across both apps is currently unsupported and may lead to issues like data conflicts.”

\n\n\n\n

One user asked if there are premium features in the Jetpack app that will carry additional cost, and if there is any advertising included within the app.

\n\n\n\n

“For clarity, the Jetpack app is free to use and doesn’t include in-app advertisements,” Automattic representative Siobhan Bamber said.

\n\n\n\n

“We’re still planning our 2023 roadmap, and it’s possible in-app purchases will be a part of our plans. The driving goal would be to offer features that bring most value to users, and we’re keen to hear any ideas or feedback. Any in-app purchases would be optional, with the currently free features remaining free to use.”

\n\n\n\n

In response to those asking about the differences between the two apps, Bamber said there will be a couple more posts on the WordPress.com news blog in the following weeks.

\n\n\n\n

Users will need to have the latest version of the WordPress app installed in order to automatically migrate their data and settings to the Jetpack app. This includes locally stored content, saved posts, and in-app preferences. The FAQ states that after users download the Jetpack app, they will be “auto-magically” logged in with all their content in place.

\n\n\n\n

“One good way to confirm whether your version of the WordPress app supports ‘auto migration’ is to tap one of the in-app ‘Jetpack powered’ banners,” Bamber advised users in the comments. “You’ll find these banners at the bottom of sections including Stats and Reader. If you tap the banner, you’ll only see the ‘Switch to the new Jetpack app’ prompt in versions that support migration.”

\n\n\n\n

The revamped Jetpack app has been presented to WordPress.com users as a more feature-rich way to publish to their websites, but it also lays the burden of choice on users to try to understand the difference between the two apps and select one for all the sites they manage. Many don’t want the inconvenience of switching to a new app. Based on the users’ responses, it might have been easier for them to understand that the official WordPress apps are removing all features require the Jetpack plugin or a WordPress.com account – instead of selling it as a new, shiny publishing experience.

\n\n\n\n

Migrating to the Jetpack app is the best option if you want to continue using the Stats, Reader, and Notifications features. In order to make it easy for users to choose the best path forward, future posts on WordPress.com should make it crystal clear what features users can expect in each app and when they will need to take action.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 04:57:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:21;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"HeroPress: Reflecting on My 3 Foundational Pillars\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=5037\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:152:\"https://heropress.com/essays/reflecting-on-my-3-foundational-pillars/#utm_source=rss&utm_medium=rss&utm_campaign=reflecting-on-my-3-foundational-pillars\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9650:\"\"Pull\n

I strongly believe that every experience we have up to our most current place in time shapes our identity. With that being said, as we go about living our lives it is not always obvious to see just how those compounding experiences shape us into who we are today. This is what makes all our journeys unique and worth reflecting on, because in our past often lies the tools and budding potential that influences the possibilities in our future.

\n\n\n\n

With that said, I’d like to share three pillars of my journey that have shaped me as a person and become the foundations of my current work.

\n\n\n\n

Technology

\n\n\n\n

I’ve found in one way or another that I have always lived technology-adjacent. When I was a kid my family had a Super Nintendo in the house which I always loved playing Super Mario World on– this device was essentially my first step into computers before we got our first home computer in the house when I was around 6 years old. By age 10, our computer was connected to AOL dial-up, which then allowed me to explore the early internet more deeply– MIRC, Livejournal, AOL Games, Neopets, MySpace… you name it. For the first time my world expanded beyond my immediate home of Rancho Cucamonga, CA, and El Paso, TX and into the interconnected world of the web.

\n\n\n\n
\n

Due to this opportunity of early access to computers, I became proficient in typing and navigating the internet at a very young age.

\n
\n\n\n\n

From what I’ve described so far, one would think that I was on track for a technology-related degree; however, there weren’t any people in my family or immediate network of friends who held a position in tech– so the idea that the computer could become a tool to propel my career didn’t really click until after graduating college.

\n\n\n\n

A slight detour– I’m a Social-Anthropologist by trade, having graduated from Lewis and Clark College with a Bachelor’s in East Asian Studies and a minor in Japanese. Following my passion for Japan and inter-cultural studies, I moved back to Tokyo after college and it was about one year later when I landed my first job as a Product Manager for a mobile gaming company called Cocone. This was my reintroduction to the idea that I could have a technology-driven career.

\n\n\n\n

In between working at Cocone and my return to the United States in 2016 I held a couple jobs that were not necessarily reliant on strong technical knowledge such as English Teacher, Executive Assistant, and even working at a karaoke bar. What my time as a Product Manager taught me, however, is that I do have a large thirst for working in the technology space so when I moved back to the States I applied to a Digital Agency called ASA Digital to get me back into that space.

\n\n\n\n

After a year at ASA Digital being a sort of Jack of all trades on projects that included mobile apps, web apps, websites, MR/ER/VR/AR, I knew that this was the right choice for me. Sometimes when you know you know, and when I moved on from ASA Digital to Automattic I was fully embracing my love for and need to have technology in my life.

\n\n\n\n

Diversity, Equity, Inclusive, and Belonging

\n\n\n\n

I haven’t always been aware of what the world now collectively calls DEIB, but since I was little I disliked the idea of injustice and lies. I have also faced adversity in the past due to who I am and what I look like, and it never sits right with me when others are in this kind of predicament as well. Due to this, DEIB practices deeply impacted my values and how I show up to work and with other people.

\n\n\n\n

It wasn’t until around 2019 that I became more involved in the world of DEIB in an official capacity at Automattic or at the incluu, LLC (a woman-owned and operated consultancy specializing in DEIB-thoughtful product strategy and advisement), and this is when I further developed this lens by participating in webinars on various DEIB topics, taking on leadership roles in the space, and keeping my eyes open to not only injustices that are happening but how they are being responded to.

\n\n\n\n
\n

The principles behind DEIB affect everyone and every aspect of our daily lives in some capacity, and embracing this space more fully not only allowed me to better understand the many systemic practices at play that keep us all from moving forward positively, but it also opened my mind to the real needs of people all over the world. 

\n
\n\n\n\n

Everyone deserves to live in a world or operate in a space with dignity and mutual respect.

\n\n\n\n

Community Building

\n\n\n\n

While I can understand the intent around the phrase “don’t mix friend groups”, I was never the type to follow this social role wholeheartedly. There are many times in our lives when we are put in situations where we interact with people we wouldn’t necessarily have engaged with such as school projects, clubs, sports, work, etc., and while it’s not all the time, sometimes a positive reaction can occur and we can meet someone new and interesting through these random groupings.

\n\n\n\n

I’m not quick to make friends, but when I do create a strong friendship it is because we share values and experiences which serve as the foundation for our relationship despite any other differences. Maybe it’s because of my (still ongoing) gaming days, but I tend to visualize people in the world as a character with a rich background story and something only they can bring to the table.

\n\n\n\n
\n

It has always brought me joy to bring people together and see how these chain reactions occur.

\n
\n\n\n\n

It could be that by some happenstance one of the friends is recruiting, they share a similar hobby, or come from a similar background. Facilitating safe spaces where folks can develop a sense of community has always been a passion of mine.

\n\n\n\n

I have had the pleasure of building community in the WordPress community through various outlets like BlackPress, with the Training Team, and even in Automattic’s Black employee resource group Cocoamattic.

\n\n\n\n

The Outcome

\n\n\n\n

Early last year I applied for the Community Education Manager with a basic idea based off of the job description of what I would be doing– fast forwarding to today I have found that the three pillars shared above gave me the tools I need to perform in this role successfully.

\n\n\n\n

As a Community Education Manager I work to break down perceived barriers for folks who want to contribute to the Make WordPress Training Team’s goals, and work as a close partner with the Training Team Representatives and members to empower them to excel in their leadership, goals, and strategy. I also help shepherd the Faculty Program, and therefore work to enable these folks to fully own and participate in their roles.

\n\n\n\n
\n

When working with our contributors, I focus on building relationships, encouraging engagement, and enabling contributions.

\n
\n\n\n\n

We have contributors from all over the world, so I also take care to be mindful of any language or cultural differences that may be at play and lean in with curiosity to better understand each community’s unique needs.

\n\n\n\n

When working with our Team Reps, I similarly focus on building relationships, and work with them (not for them) to create an environment where the goals of the team can be realized. 

\n\n\n\n

Lastly, I work with our Faculty Program Members by building relationships and connecting them with work related to their role, and with contributors who can benefit from their expertise and mentorship.

\n\n\n\n

Can you see how my pillars are directly impacting and influencing the work I currently do?

\n\n\n\n

Exploring Your Own Foundational Pillars

\n\n\n\n

There are probably many articles with thought-provoking exercises that can lead you in your own self-reflection, so I’ll leave you all with just a some questions from me that have worked to get me started:

\n\n\n\n
    \n
  • What have you been given positive feedback on lately?
  • \n\n\n\n
  • What actions/things bring you the most joy in life?
  • \n\n\n\n
  • What actions/things make you feel motivated?
  • \n\n\n\n
  • When was the last time you found yourself “in the zone”?
  • \n
\n\n\n\n

As you go through the questions for yourself don’t discredit or try to change your initial thoughts.

\n\n\n\n

Using these as a starting point, even if what comes up doesn’t immediately surface something that could be a pillar, you’ll surely learn or get to acknowledge something about yourself that shapes your character and how you present in the world.

\n\n\n\n

Take your time with it– the way we walk through life is a long-term journey which is constantly being updated by new experiences along the way.

\n

The post Reflecting on My 3 Foundational Pillars appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 17 Jan 2023 23:00:08 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Destiny Kanno\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:22;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:89:\"Do The Woo Community: Accepting Cryptocurrency in a WooCommerce Store with Lauren Dowling\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74258\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://dothewoo.io/cryptocurrency-woocommerce-store/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:521:\"

Lauren Dowling, lead product for Coinbase commerce joins returning guest Dave Lockie from Automattic as the conversation covers accepting cryptocurrency on WooCommerce shops, whether it be for your clients sites or your own.

\n

>> The post Accepting Cryptocurrency in a WooCommerce Store with Lauren Dowling appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 17 Jan 2023 11:06:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:23;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:79:\"WordPress.org blog: WP Briefing: Episode 47: Letter from the Executive Director\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/episode-47-letter-from-the-executive-director/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8660:\"

On episode forty-seven of the WordPress Briefing podcast, Executive Director Josepha Haden Chomphosy shares her vision and current thinking for the WordPress open source project in 2023. Rather read it? The full letter is also available.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

Show Notes

\n\n\n\n

make.WordPress.org/core
Join the 6.2 Release!
Submit Topics for the Community Summit!

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress Briefing, the podcast where you can catch quick explanations of the ideas behind the WordPress open source project, some insight into the community that supports it, and get a small list of big things coming up in the next two weeks. I’m your host, Josepha Haden Chomphosy. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:40] 

\n\n\n\n

Last month at State of the Word, I shared some opening thoughts about why WordPress. For me, this is an easy question, and the hardest part is always knowing which lens to answer through. Though I always focus on the philosophical parts of the answer, I know that I often speak as an advocate for many types of WordPressers.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:00] 

\n\n\n\n

So as we prepare ourselves for the start of a new year, I have a few additional thoughts that I’d like to share with you, my WordPress community, to take into the year with you. 

\n\n\n\n

Firstly, the Four Freedoms. If you have already listened to State of the Word, you have heard my take on the philosophical side of open source and the freedoms it provides.

\n\n\n\n

But if you didn’t, then the TL;DR on that is that open source provides protections and freedoms to creators on the web that I really think should just be a given. But there are a couple of other things about the Four Freedoms, and especially the way that WordPress does this kind of open source-y thing that I think are worth noting as well.

\n\n\n\n

One of those things is that WordPress entrepreneurs, those who are providing services or designing sites, building applications, they have proven that open source provides an ethical framework for conducting business. No one ever said that you aren’t allowed to build a business using free and open source software, and I am regularly heartened by the way that successful companies and freelancers make the effort to pay forward what they can.

\n\n\n\n

[Josepha Haden Chomphosy 00:02:02]

\n\n\n\n

Not always for the sole benefit of WordPress, of course, but often for the general benefit of folks who are also learning how to be entrepreneurs or how to kind of navigate our ecosystem. And the other thing that I love about the Four Freedoms and the way that WordPress does it is that leaders in the WordPress community, no matter where they are leading from, have shown that open source ideals can be applied to the way we work with one another and show up for one another.

\n\n\n\n

As a community, we tend to approach solution gathering as an us-versus-the-problem exercise, which not only makes our solutions better, it also makes our community stronger. 

\n\n\n\n

As I have witnessed all of these things work together over the years, one thing that is clear to me is this: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, but open source methodologies represent a process that can change the way we approach our work and our businesses.

\n\n\n\n

[Josepha Haden Chomphosy 00:03:01] 

\n\n\n\n

The second big thing that I want to make sure you all take into the year with you is that we are preparing for the third phase of the Gutenberg project. We are putting our backend developer hats on and working on the APIs that power our workflows. That workflows phase will be complex. A little bit because APIs are dark magic that binds us together, but also because we’re going to get deep into the core of WordPress with that phase.

\n\n\n\n

If you want to have impactful work for future users of WordPress, though, this is the phase to get invested in. This phase will focus on the main elements of collaborative user workflows. If that doesn’t really make sense to you, I totally get it. Think of it this way, this phase will work on built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and things like programmable editorial, pre-launch checklists.

\n\n\n\n

[Josepha Haden Chomphosy 00:04:00] 

\n\n\n\n

So phases one and two of the Gutenberg project had a very ‘blocks everywhere’ sort of vision. And phase three and, arguably, phase four will have more of a ‘works with the way you work’ vision.

\n\n\n\n

And my final thought for you all as we head into the year is this, there are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was State of the Word 2013, where Matt dreamed on stage of a true WYSIWYG editor for WordPress. Some say it was State of the Word 2016, where we were all encouraged to learn JavaScript deeply. For a lot of us though, it was at WordCamp Europe in 2018 when the Gutenberg feature plugin first made its way to the repo.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like it’s been a long time because it has been a long time. But I can also confirm that it takes many pushes to knock over a refrigerator. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:00] 

\n\n\n\n

For early adopters, both to the creation of Gutenberg as well as its use, hyperfocus on daily tasks makes it really hard to get a concept of scale.

\n\n\n\n

And so I encourage everyone this year to look out toward the horizon a bit more and up toward our guiding stars a bit more as well. Because we are now, as we ever were, securing opportunity for those who come after us because of the opportunity that was secured for us by those who came before us. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:33] 

\n\n\n\n

That brings us now to our small list of big things. It’s a very small list, but two pretty big things. The first thing on the list is that the WordPress 6.2 release is on its way. If you would like to get started contributing there, you can wander over to make.WordPress.org/core. You can volunteer to be part of the release squad. You can volunteer your time just as a regular contributor, someone who can test things — any of that. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

We’ll put a link in the show notes. And the second thing that I wanted to remind you of is that today is the deadline to submit topics for the Community Summit that’s coming up in August. That comes up in the middle of August, like the 22nd and 23rd or something like that. 

\n\n\n\n

We’ll put a link to that in the show notes as well. If you already have chatted with a team rep about some things that you really want to make sure get discussed at the community summit, I think that we can all assume that your team rep has put that in. But if not, it never hurts to give it a second vote by putting a new submission into the form.

\n\n\n\n

And that, my friends, is your small list of big things. Thank you for tuning in today for the WordPress Briefing. I’m your host, Josepha Haden Chomphosy, and I’ll see you again in a couple of weeks.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:24;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"WordPress.org blog: Letter from WordPress’ Executive Director, 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14180\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/letter-from-wordpress-executive-director-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5901:\"

Last month at State of the Word, I shared some opening thoughts about “Why WordPress.” For me, this is an easy question, and the hardest part is knowing which lens to answer through. The reasons that a solopreneur will choose WordPress are different than the reasons a corporation would. And while artists and activists may have a similar vision for the world, their motivations change their reasons, too. That’s why I always focus on the philosophical parts of the answer because I know that I am speaking as an advocate for many types of WordPressers. I have a few other reasons, too, which you may not be aware of as you use our software every day.

\n\n\n\n

Why WordPress?

\n\n\n\n

Most importantly, the Four Freedoms of Open Source. If you have already listened to State of the Word, you have heard my thoughts on the philosophical side of open source and the freedoms it provides. If you didn’t, then the tl;dr on that is that open source provides protections and freedoms to creators on the web that should be a given. There’s an extent to which the idea of owning your content and data online is a radical idea. So radical, even, that it is hard for folks to grasp what we mean when we say “free as in speech, not free as in beer.” Securing an open web for the future is, I believe, a net win for the world especially when contrasted to the walled gardens and proprietary systems that pit us all against one another with the purpose of gaining more data to sell.

\n\n\n\n

A second reason is that WordPress entrepreneurs (those providing services, designing sites, and building applications) have proven that open source offers an ethical framework for conducting business. No one ever said that you cannot build a business using free and open source software. And I am regularly heartened by the way successful companies and freelancers make an effort to pay forward what they can. Not always for the sole benefit of WordPress, but often for the general benefit of folks learning how to be an entrepreneur in our ecosystem. Because despite our competitive streaks, at the end of the day, we know that ultimately we are the temporary caretakers of an ecosystem that has unlocked wealth and opportunity for people we may never meet but whose lives are made infinitely better because of us.

\n\n\n\n

And the final reason is that leaders in the WordPress community (team reps, component maintainers, and community builders) have shown that open source ideals can be applied to how we work with one another. As a community, we tend to approach solution gathering as an “us vs. the problem” exercise, which not only makes our solutions better and our community stronger. And our leaders—working as they are in a cross-cultural, globally-distributed project that guides or supports tens of thousands of people a year—have unparalleled generosity of spirit. Whether they are welcoming newcomers or putting out calls for last-minute volunteers, seeing the way that they collaborate every day gives me hope for our future.

\n\n\n\n

As I have witnessed these three things work together over the years, one thing is clear to me: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, open source methodologies represent a process that can change the way we approach our work and our businesses. 

\n\n\n\n

WordPress in 2023

\n\n\n\n

As we prepare for the third phase of the Gutenberg project, we are putting on our backend developer hats and working on the APIs that power our workflows. Releases during Phase 3 will focus on the main elements of collaborative user workflows. If that doesn’t make sense, think of built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and programmatic editorial and pre-launch checklists.

\n\n\n\n

If Phases 1 and 2 had a “blocks everywhere” vision, think of Phase 3 with more of a “works with the way you work” vision. 

\n\n\n\n

In addition to this halfway milestone of starting work on Phase 3, WordPress also hits the milestone of turning 20 years old. I keep thinking back to various milestones we’ve had (which you can read about in the second version of the Milestones book) and realized that almost my entire experience of full-time contributions to WordPress has been in the Gutenberg era.

\n\n\n\n

I hear some of you already thinking incredulous thoughts so, come with me briefly.

\n\n\n\n

There are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was at State of the Word 2013 when Matt dreamed of “a true WYSIWYG” editor for WordPress. Some say it was at State of the Word 2016 where we were encouraged to “learn Javascript deeply.” For many of us, it was at WordCamp Europe in 2017 when the Gutenberg demo first made its way on stage.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like a long time because it has been a long time. I can also confirm that it takes many pushes to knock over a refrigerator. For early adopters (both to the creation of Gutenberg and its use), hyper-focus on daily tasks makes it hard to get a concept of scale.

\n\n\n\n

So I encourage you this year to look out toward the horizon and up toward our guiding stars. We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.

\n\n\n\n

Rather listen? The abbreviated spoken letter is also available.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:25;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"Do The Woo Community: Finding Team Members to Fit Your Companies Culture\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74204\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"https://dothewoo.io/finding-team-members-to-fit-your-companies-culture/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:442:\"

Marius Vetrici has built a process to bring in new employees that are drawn to fit his companies values, and to grow with them as a team member.

\n

>> The post Finding Team Members to Fit Your Companies Culture appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 10:09:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:26;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:95:\"Gutenberg Times: Box Shadow, Newsletter Theme, Testing Call 20 and more – Weekend Edition 241\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://gutenbergtimes.com/?p=23190\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"https://gutenbergtimes.com/box-shadow-newsletter-theme-testing-call-20-and-more-weekend-edition-241/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:15190:\"

Howdy,

\n\n\n\n\n\n

Last week’s Live Q & A on Layout features went really well, with numerous participants. The post and the show notes are still in the works. The recording is available on YouTube, should you want to revisit parts of it or missed it entirely.

\n\n\n\n

Now that feature freeze for the major WordPress release is only three weeks away, the contributors would appreciate it if you could heed the 20th call for testing from the FSE Outreach program. You can help find quirks, bugs and annoyances, so they can be fixed before February 7th and during the round of beta version of the release.

\n\n\n\n

Have a lovely weekend!

\n\n\n\n

Yours, 💕
Birgit

\n\n\n\n\n\n\n\n\n\n\n\n

Developing Gutenberg and WordPress

\n\n\n\n

Gutenberg 15.0 release candidate is available for testing. Sticky positioning, resizable Site editor, updated to the Page List block, modify block style variations from global styles, and a lot more refinements are coming to the Gutenberg plugin

\n\n\n\n
\n

🎙️ New episode: Gutenberg Changelog #78 -State of the Word, WordPress 6.2, Gutenberg 14.8 and 14.9 with Birgit Pauli-Haack and special guest Hector Prieto

\n
\n\n\n\n

Last April, a group of contributors started working on research on how to best implement an API for to make blocks more interactive. This week, JuanMa Garrido shared a progress report: Update on the work to make building interactive blocks easier.

\n\n\n\n

The resources linked in the post are mostly code internals, so they are definitely very technical at this point. With that said, understanding how the new API works, will not be necessary for developers to use this new standard. A standard proposal will be published the next few months. So for now, this is all bit technical and architectural. The work on the underlying framework is shared on this GitHub Repository

\n\n\n\n
\n\n\n\n

Plugins, Themes, and Tools for #nocode site builders and owners

\n\n\n\n

Munir Kamal, GutenbergHub, shows you in his latest post How to Find and Use Block Patterns in WordPress. You learn, how to find patterns in the post and site editor, how to navigate the WordPress Pattern Directory and how to install the patterns via the plugin Extendify Patterns and Templates

\n\n\n\n

If you want to create your own patterns, but don’t know how to code them, you can use the plugin Blockmeister – Block Pattern Builder.

\n\n\n\n
\n\n\n\n

Sarah Gooding reports on the Lettre Newsletter Theme Now Available on WordPress.org, It can be used with the newly release newsletter feature in Jetpack plugin or as a stand-along theme. “The theme puts the focus on the subscription form, which is the most important thing a newsletter landing page can do – make it easy for people to sign up. Beneath the form there is a link to read all the posts, followed by another subscription form. All of these elements in the home page design are blocks, making it easy for them to be removed or rearranged.” Gooding wrote.

\n\n\n\n
\n\n\n\n

Will Morris explained the three ways add a Table of Contents in WordPress in is post for the Torque Magazine. The three ways are:

\n\n\n\n
    \n
  • Install a plugin
  • \n\n\n\n
  • Use on the Custom Table of Contents blocks
  • \n\n\n\n
  • Create you Table manually in the Block Editor.
  • \n
\n\n\n\n

Soon you will be able to use the core Table of Content’s block once it comes out of the experimental stage. It’s already available via the Gutenberg plugin.

\n\n\n\n

Theme Development for Full Site Editing and Blocks

\n\n\n\n

In his post, Justin Tadlock, walks you through the layout classes in WordPress 6.1. With the latest release of WordPress, the software has now centralized its layout definitions, created semantic class names, and reduced code duplication on container blocks. “Originally, this post was intended to be a quick look at the changes to the system for theme authors. However, given the heftiness of the topic, it has evolved into a full overview of the layout framework.” Tadlock wrote.

\n\n\n\n\n\n\n\n

In his second post published on the Developer Blog, Using the box shadow feature for themes, Justin Tadlock took a look at the box shadow support, that what just released in Gutenberg 14.9. As it happens with similar features, the first iteration of box shadow support is only available via code. The interface for the site editor screens are still in the works.

\n\n\n\n\n

 “Keeping up with Gutenberg – Index 2022” 
A chronological list of the WordPress Make Blog posts from various teams involved in Gutenberg development: Design, Theme Review Team, Core Editor, Core JS, Core CSS, Test and Meta team from Jan. 2021 on. Updated by yours truly. The index 2020 is here

\n\n\n\n\n

Daisy Olsen held her inaugural live programming session on Twitch this week. The recording is now available on YouTube. In this stream, she talked about:

\n\n\n\n
    \n
  • using LocalWP for local WordPress development,
  • \n\n\n\n
  • the Create Block Theme Plugin, and
  • \n\n\n\n
  • took a look at the code from a couple of existing block themes.
  • \n
\n\n\n\n

You need a Twitch account and follow DaisyonWP to get notified when she goes live.

\n\n\n\n\n\n\n\n

In his latest post for CSS-Tricks: Styling Buttons in WordPress Block Themes, Fränk Klein, takes a detailed look markup of various buttons and how to style them via the theme.json properties.

\n\n\n\n

Building Blocks and Tools for the Block editor.

\n\n\n\n

Tom McFarlin continued his series A Backend Engineer Learns to Build Block Editor Blocks with Part 5 in which he covers adding color controls to a custom block for the use case, when you want to give the user the option to select the colors for the block themselves.

\n\n\n\n

McFarlin, recommend the previous articles first as they build on top of each other. So far, he published:

\n\n\n\n
    \n
  1. Required Tools, Plugin Structure, Dependencies, Block Metadata
  2. \n\n\n\n
  3. The Backend, The Frontend, Functionality, Styles, a Working Demo
  4. \n\n\n\n
  5. Block Attributes, Editable Content, Components, Editor Styles
  6. \n\n\n\n
  7. Saving Data, Styling the Frontend
  8. \n
\n\n\n\n
\n\n\n\n

Phil Sola create a Custom Color Picker for WordPress. Sola added some improvements to the existing color picker. It’s more an experiment rather than a full-fledged solution. His exploration might also be an inspiration for others to start experimenting with WordPress component library.

\n\n\n\n
\n\n\n\n\n

Need a plugin .zip from Gutenberg’s master branch?
Gutenberg Times provides daily build for testing and review.
Have you been using it? Hit reply and let me know.

\n\n\n\n

\"GitHub

\n\n\n\n\n

Upcoming WordPress events

\n\n\n\n

February 4 + 5, 2023
WordCamp Birmingham, AL

\n\n\n\n

February 17 – 19, 2023
WordCamp Asia 2023 

\n\n\n\n

Check the schedule of WordCamp Central of upcoming WordCamps near you.

\n\n\n\n

Learn WordPress Online Meetups

\n\n\n\n

January 17, 2023 – 3pm / 20:00 UTC
Patterns, reusable blocks and block locking

\n\n\n\n

January 19th, 2023 – 10:30 ET / 15:30 UTC
Live stream: Building an Advanced Query Loop block variation plugin w/ Ryan Welcher @ryanwelchercodes

\n\n\n\n

January 19, 2023 – 7 pm ET / 24:00 UTC
Let’s make custom templates in the Site Editor!

\n\n\n\n

January 20, 2023 – 3 am ET / 8:00 UTC
Let’s make custom templates in the Site Editor!

\n\n\n\n

January 20, 2023 – 10:30 am 15:30 UTC
Block Themes and WordPress: Live Stream w/ Daisy Olsen @daisyonwp

\n\n\n\n

January 23, 2023 – 10 pm ET / 1 am UTC
Patterns, reusable blocks and block locking (APAC time zone)

\n\n\n\n

January 26, 2023 – 10:30 am ET / 15:30 UTC
Live stream: Reviewing developer-focused features in Gutenberg 15.0 w/ Ryan Welcher @ryanwelchercodes

\n\n\n\n

January 31, 2023 – 3pm ET / 20:00 UTC
Creating a photography website with the block editor

\n\n\n\n
\n\n\n\n\n

Featured Image: Amit Patel: Mango Shake Orange Sweet found in WordPress.org/photos

\n\n\n\n
\n\n\n\n

Don’t want to miss the next Weekend Edition?

\n\n\n\n

We hate spam, too and won’t give your email address to anyone except Mailchimp to send out our Weekend Edition

Thanks for subscribing.
\n\n\n\n
\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 14 Jan 2023 22:30:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Birgit Pauli-Haack\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:27;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: WooCommerce 7.3 Introduces New Products Block in Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141097\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"https://wptavern.com/woocommerce-7-3-introduces-new-products-block-in-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2242:\"

WooCommerce 7.3 was released this week with the new Products block now in beta. In December 2022, the Products block went into testing in WooCommerce Blocks version 9.1.0. It’s based on the Query Loop block and is intended to replace all of WooCommerce’s current product-displaying blocks.

\n\n\n\n

This first beta version of the Products block allows users to list products based on specific criteria and their layout in the list or grid.

\n\n\n\n\n\n\n\n

Version 7.3 also introduces three “commerce-adjacent” patterns for building WooCommerce store pages. These are patterns that do not tap into WooCommerce store data but allow store owners to customize the images and the links. These patterns were also tested in WooCommerce Blocks 9.1.0. They include an alternating image and text block pattern, a product hero with two columns and two rows, and a “Just Arrived” full hero pattern.

\n\n\n\nimage source: WooCommerce 7.3 release post\n\n\n\n

This release also brings store owners a new multichannel marketing experience in beta. Under the Marketing menu in the admin, users can now view a list of recommended marketing extensions without leaving the dashboard. These can be installed directly from the Marketing page.

\n\n\n\n\n\n\n\n

Other notable features in WooCommerce 7.3 include Pinterest and Codisto extensions added to the onboarding wizard, a new warning banner when the tax settings have a conflict, and an improved UI for creating product attributes and uploading product images.

\n\n\n\n

Check out the release post to see the template changes and all the new actions and filters available for developers. The full 7.3 changelog is available on GitHub.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 14 Jan 2023 04:25:34 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:28;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: Lettre Newsletter Theme Now Available on WordPress.org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141076\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"https://wptavern.com/lettre-newsletter-theme-now-available-on-wordpress-org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2595:\"

Automattic has published its Lettre theme to WordPress.org. The company launched its newsletter product at the end of December 2022 using Lettre as the default theme. The self-hosted version of this block theme is for those who want to publish a newsletter using Jetpack.

\n\n\n\n\n\n\n\n

The theme puts the focus on the subscription form, which is the most important thing a newsletter landing page can do – make it easy for people to sign up. Beneath the form there is a link to read all the posts, followed by another subscription form. All of these elements in the home page design are blocks, making it easy for them to be removed or rearranged.

\n\n\n\n

Lettre comes with 15 block patterns for building different pages and designs, including about the author(s), a bold color signup, a two-column signup, various designs for the newsletter intro with light and dark background images, newsletter signup with media on the left, newsletter signup with logos for the background, a list of posts, an in-post article promo, three columns of text, and more.

\n\n\n\n\n\n\n\n

A live demo of the theme is available on WordPress.com. The menu items on the demo give a few examples of the different signup patterns in action.

\n\n\n\n

Lettre is designed to be used with Jetpack’s Subscription block, which uses WordPress.com’s infrastructure to manage emails and subscribers. If you like the design but are already using another newsletter service, the Jetpack Subscribe block can be replaced with any other block, including the shortcode block for newsletter services that haven’t yet made their subscription forms available via a block. Be advised, you may need to write some custom CSS to ensure that the subscribe form matches the original design.

\n\n\n\n

Lettre is one of the only themes in the WordPress Themes Directory that was made to be a newsletter landing page and certainly the only block theme dedicated to this purpose. Combined with Jetpack’s subscription feature, this is one of the most seamless ways to distribute a newsletter without all the extra steps of copying the content into a newsletter service’s editor. Lettre is available for free download from WordPress.org. I wouldn’t be surprised to see more themes like this pop up now that WordPress.com has launched its newsletter service.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 14 Jan 2023 02:50:40 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:29;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"Do The Woo Community: Taking a Deep Dive Into the Current State of Social Media with David Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74300\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"https://dothewoo.io/current-state-of-social-media/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:422:\"

David Bisset and I share our current experiences with Twitter, Mastodon, Linked, Tumblr, the Fediverse and open source.

\n

>> The post Taking a Deep Dive Into the Current State of Social Media with David Bisset appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 13 Jan 2023 10:58:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:30;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:77:\"WPTavern: New Video Explores Site Building Progress from WordPress 5.9 to 6.2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141039\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:88:\"https://wptavern.com/new-video-explores-site-building-progress-from-wordpress-5-9-to-6-2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3346:\"

WordPress 5.9 “Josephine” was released in January 2022, but that seems like ages ago when you compare the advances made in site building over the past year. Anne McCarthy, an Automattic-sponsored contributor who heads up the Full Site Editing Outreach Program, has published a short video that tours the important changes in WordPress over the past few major releases. The video also doubles as a preview of some of the features coming in 6.2.

\n\n\n\n
\n\n
\n\n\n\n

If you are using the Gutenberg plugin and have been tracking the relentless progress of the Site Editor, you will notice how limited the design options are in 5.9 and how much more consistent and expansive they are today. In 5.9 users users can only add a Front page template, and the site building interface is disjointed and less polished.

\n\n\n\n

McCarthy demonstrates how WordPress 6.2 will introduce smoother interactions with browse mode. It will also greatly expand the template options available for users to add and includes a new colorized list view.

\n\n\n\n

The Navigation block has had a long, rocky journey but seems to be reborn in 6.2. McCarthy showed how much more intuitive it has become with the new experience of editing navigation in the sidebar, and repositioning via drag and drop with live previews.

\n\n\n\n\n\n\n\n

The instant that Style Variations were introduced in WordPress 6.0, it seemed they were always with us. Looking back at 5.9 in the video, the Site Editor appears so bare without them. WordPress 6.2 will extend this even further with improved block style previews, a style book, and a new zoomed out view that makes it easy to see changes at a glance.

\n\n\n\n

Everything coming in 6.2 is converging towards better usability and more design options for site editors. The challenge here is to continue introducing new features without the interface becoming cluttered and chaotic. Many of these features are still being ironed out. For example, McCarthy mentioned that the Edit button is still a work in progress and may soon be relocated to be more prominent in the Site Editor.

\n\n\n\n

This video gives a quick visual summary of what is being done to wrap up the full-site editing phase of the Gutenberg project before contributors move on to Collaboration. It is worth a watch to see the site building progress that contributors have made in just one year.

\n\n\n\n

If you want to get involved in making sure all these features in 6.2 are ready for prime time, check out McCarthy’s latest FSE Testing Call: Find Your Style. It will plunge you into the new features of the Site Editor to perform a few tasks. It’s essentially a guided opportunity to explore the new interface while contributing back to WordPress, and you will earn a fancy testing contributor badge that will display on your WordPress.org profile.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 13 Jan 2023 03:56:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:31;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:96:\"Post Status: On OpenAI And WordPress With Jannis Thuemmig Of WP-Webooks— Post Status Draft 136\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146297\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"https://poststatus.com/on-openai-and-wordpress-with-jannis-thuemmig-of-wp-webooks-post-status-draft-136/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:43263:\"

Jannis Thuemmig, founder of WP Webhooks, joins Cory Miller to discuss Open AI and WordPress. Jannis is passionate about utilizing the power of technology to increase efficiency. WP Webhooks is exploring the ways Open AI can be used to revolutionize website processes and management. It seems we are only at the tip of the iceberg for what is possible when working with WordPress and Open AI.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

In this episode, Jannis Thuemmig, serial entrepreneur and founder of WP Webhooks, dives into the world of automation and Open AI with Cory Miller. Together they look at what is currently possible within the world of integration and automation within WordPress. Then they lean into what is unfolding as Open AI finds its way into the mainstream and discuss what this might mean for the WordPress community.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • Integrations & Automations to Save Time: Everyone is in need of some kind of automation. Our main goal is to save time by creating automations wherever there are pain points. Rather than doing things manually, WP Webhooks enables you to automate them within your dashboard.
  • \n\n\n\n
  • Avoiding Automation through Software: Using software as a service partner means hosting your data on their platforms. Using Webhooks for integration and automation allows you to keep things on your server and within your complete control.
  • \n\n\n\n
  • Possibilities with Open AI Integration: Webhooks is focused on using Open AI as an advantage to speed up processes by creating integrations between services and generating original content. They are working on finding cool use cases and understanding the actual power of what it makes possible.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n
\n

\"🙏\" Sponsor: WordPress VIP

\n\n\n\n

Founded in 2006, WordPress VIP is the agile content platform that empowers marketers to build content both faster and smarter so they can drive more growth. We empower content and development teams with the flexibility and ubiquity of WordPress—the agile CMS that powers more than 40% of the web—while ensuring the security and reliability organizations need to operate at scale

\n
\n\n\n\n
\n
\"WordPressWordPress VIP
\n
\n
\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

GMT20230105-161248_Recording

\n\n\n\n

GMT20230105-161248_Recording

\n\n\n\n

Cory Miller: [00:00:00] Hey everybody. Welcome back to Post Status Draft. This is another interview and discussion in our Product People series, and I\'ve got someone I\'ve met, let\'s see, last year or the year before Giannis and doing good work, but we were talking about AI and that led to OpenAI and something they\'re doing with WP Webhooks.

\n\n\n\n

So that\'s what the conversation is gonna be about today. But Giannis, welcome to the draft podcast. Would you tell us a little bit about yourself and your work and WordPress?

\n\n\n\n

Jannis Thuemmig: Sure, totally. Thanks for having me here. Uh, it\'s always an honor. Uh, my name is Giannis. I\'m from Germany originally, but started traveling a long time ago and since then, I basically work as a digital dumper from anywhere.

\n\n\n\n

And I would say with a, with a very, very deep focus on web. And specifically in automation. This is where W P Airport comes from. So we are basically focused on connecting different services and WebPress plugging to let them talk to each other and kind of just automate the [00:01:00] system and get rid of the human error and save everyone a little bit of time and money, which is really interesting nowadays.

\n\n\n\n

Cory Miller: Yeah, I, and I love it. Uh, one part I\'ll just sidebar real quick is I know when you say digital notepad, uh, the several times we\'ve had zooms, I\'m like, where are you in the world today, y\'all? It\'s like, . Um, so I, I love that. I love to see the nude, like landscapes you\'re in every time we talk. Um, okay. So WP Webhooks, um, I know you\'ve been, so automation is key.

\n\n\n\n

It\'s about efficiency, um, like really saving that time. In the processes you\'re doing, um, what, tell me what all does WP Webhooks do?

\n\n\n\n

Jannis Thuemmig: So basically it allows you to use a set of redefine integrations to let other services and WebPress plugin specifically talk to each other. So let\'s say there\'s, um, a woo commerce shop, for example, and you have a, a custom programmed plugin that has no integrations [00:02:00] whatsoever.

\n\n\n\n

You can use our plugin as a middleman to allow sending data in between, and that works with mostly any kind of WebPress plugins as well as external data like, uh, external services, something like Zapier or make or integrated. So the, the basic main goal is to just make them compatible, which they, in a lot of cases, aren\'t from the beginning. Or if they are, they\'re often very limited, which is something we realized as well. So we just want to kind of get that interoperability to WordPress, which is something that was just lacking over the last couple of years.

\n\n\n\n

Cory Miller: Yeah, I, I love that. I know Zapier has used quite a bit uh, obviously we\'ve used it in the past at, at Post Status because of all the external services, and you\'re trying to link these and do some things that certain pieces of software doesn\'t do out of the bat.

\n\n\n\n

So I, I love the premise of web hooks for sure. Uh, WP Web Hooks, what are you seeing or finding? Customers are gravitating to webhooks [00:03:00] for, like is there specific tasks that stand out that people are using these over and over and over and going, this is what I need. I don\'t want to pay for Zapier or some other alternative, I wanna do something here with my WordPress site.

\n\n\n\n

What are you seeing from your customers?

\n\n\n\n

Jannis Thuemmig: So I\'d say that\'s not a specific use case. There\'s, uh, quite a lot. So everyone, literally, everyone who\'s in the, in the, has a web presence or has an online shop or something related and does something with website. Everyone is in in need of doing some kind of automations.

\n\n\n\n

Let it be to automatically book orders into your accounting system or synchronize your properties from a property management website with your WebPress website. Or let\'s say you have a Teachable account and you sell online courses and you want to synchronize your students with a WebPress website to give them extra features.

\n\n\n\n

This is stuff that they are using it for. So basically wherever there\'s a pain point and there\'s some time that just can be avoided by automating it through software. This is something where we are, um, jumping in [00:04:00] and it\'s specifically interesting right now for people that are very critical about privacy because especially in Europe, a lot of people don\'t want to use software as a service partners like Zapier or Integra.

\n\n\n\n

Because they are hosting their data on other platforms, right? So they have no full control over it, which comes very handy with our plugin because you have your own server, so everything runs on your own server. You are in full control where your data is, what your data does. And this is a very, very critical point that is, uh, always, always well seen at the moment.

\n\n\n\n

Yeah.

\n\n\n\n

Cory Miller: Yeah, yeah, for sure. I, you went to a subject I didn\'t even think about, which is if you don\'t want your information out there on another service, having it in in WordPress, something you control. I think that\'s a key facet. Before we start talking about, uh, AI and specifically OpenAI, what are you most excited about with webhooks this year?

\n\n\n\n

Jannis Thuemmig: Ooh, for sure. Bringing that AI space model [00:05:00] web. Because we had so much fun over the last couple of months trying these things out, seeing in which direction it goes. And it\'s just incredibly fun to, to play around with it because the possibilities are really endless. And we are, we are fully about saving time.

\n\n\n\n

Right? And this is something we can even use to leverage more time out of our daily task, which is really, really good. Okay.

\n\n\n\n

Cory Miller: Well let\'s roll into that because I think that\'s one of the most, uh, uh, Interesting themes in our community is ai. I\'ve seen a couple tweets saying AI is gonna revolutionize, um, a lot of stuff with a website by the end of 2023.

\n\n\n\n

I can\'t remember who said that online. And I was like, well, I\'ve been paying attention enough. But talk to me about ai, OpenAI and what you, you see on the horizon for, um, for WordPress particularly, and opportunities and possibilities. . Yeah,

\n\n\n\n

Jannis Thuemmig: so ai uh, specifically in our case with OpenAI, uh, there\'s, uh, a little differentiation.

\n\n\n\n

So [00:06:00] right now it\'s very much hyped, the G P T three. So the, the kind of chat ai as you can, as you can, uh, think of, which is basically you just type in something and it, it gives you like a very human answer back, which is really, really incredible. And, uh, we specifically talk about the, the OpenAI api, which kind of allows you to.

\n\n\n\n

Communicate data on a programmatic level, which means you basically don\'t even need to type something yourself, but you can already use a service to let these things run through the web automatically without ever touching this kind of data. And this is, this is just something that that works very well with, with the automation part.

\n\n\n\n

Right. So we are, we are basically looking into bringing more possibilities that AI through non-static data, and, uh, what I mean by non static data, it\'s probably interesting to, to understand what an AI actually is. So it\'s a pre-trained network, right? It has the data that was feeded to it at some point. And with OpenAI, it\'s made from [00:07:00] mostly 2021.

\n\n\n\n

So it has no actual new data. If you ask it something like what happened yesterday, for example, it could probably not give you an answer to, you could give it the information if you have it yourself. But it can never give you like the, the news and accurate information. And using things like automation, you can basically bring a whole new word to it because you can kind of give the AI the possibility through response and, uh, requests to send data through automation, uh, validate it somewhere else and send it back to the AI and tell the ai, Hey, look, there\'s new information.

\n\n\n\n

We can use that, uh, or, or learn about that and, um, send me some more information or summarize me something. So it\'s, it\'s just a very interesting time in, in regards to giving the AI actual information that you can feed it, uh, that is currently not within its its own, um, possibilities.

\n\n\n\n

Cory Miller: So you said something there.

\n\n\n\n

Um, I, I haven\'t even gotten that in depth with OpenAI, but So in [00:08:00] 2021 they fed it a ton of data you\'re saying, and then trained it to be able to, to answer questions and things.

\n\n\n\n

Jannis Thuemmig: Yeah, exactly. So basically they had a, a huge dataset or couple of datasets for sure about the information that they fitted. And the AI can basically make.

\n\n\n\n

An answer that is, uh, in a human real reform, and that seems like it is made from a human, but the data that was fed is all from 2021, right? So it is a static data if we, if we want to hear it or not. So if you\'re gonna ask the ai, what is the latest model of iPhone, for example, it\'ll probably tell you something like it\'s the iPhone 13, because I don\'t think it has information about iPhone 14.

\n\n\n\n

That would be something cool to try, but I guess it must be, uh, outdated information. And with that gap of, of using that, that automation in, in connection with ai, you can kind of close that gap and you can actually feed it real time data and use that data to, to do certain things within the AI [00:09:00] and, uh,

\n\n\n\n

Cory Miller: I see, thats a new one.

\n\n\n\n

Yeah, it does. Um, totally to me, and I\'m asking as a newbie to all of this, um, because I\'ve used it and I\'m like, this is pretty dang fast. And I\'m like, how the heck are they doing that? That makes total sense. And then from the training side, um, the model itself is, I was like, gosh, if this had access to that, and you could just ask it questions like that.

\n\n\n\n

It\'s the, it\'s the a hundred times better Google. Yeah, because, yeah, it, like, I was, I, I mean I asked what are the strengths and weaknesses of WordPress, for instance, and it came back. Um, but knowing it\'s, it\'s a little bit lag on the data side is interesting to me. Um, but I saw the potential for this to truly.

\n\n\n\n

Revolutionized some things on the web. Um, so it\'s, it, it\'s been really intriguing and I mean, I asked it all kinds of questions that I was just actually curious about and seeing what, not just from the what, [00:10:00] how the model would work, but the answer. And I was like, this is like a perfectly uh, formatted.

\n\n\n\n

Informative, um, short essay that I would\'ve gotten in college, you know, so that\'s

\n\n\n\n

Jannis Thuemmig: intriguing. It\'s actually you can, you can write like on demand stories for your kids based on your own characters. Just type in a sentence, say you run a short story and it spits you out a short story that you can read them from going to bed.

\n\n\n\n

It\'s amazing. It\'s just like incredible.

\n\n\n\n

Cory Miller: I\'m gonna have to try that today. I, I continue. This subject fascinates me and I think it\'s something we need to be thinking about and looking at and talking about in WordPress and Post Status, because this new technology coming and then how WordPress is placed in this.

\n\n\n\n

And for years, I think this is a segway to talk about OpenAI and WordPress specifically. But you know, I\'ve either built sites for people or known a bunch of people that build sites for clients. And you turn on this awesome, it\'s like you turn this car over, you pull this car up to \'em, and [00:11:00] you\'re like, here\'s your car.

\n\n\n\n

But you gotta drive it with content, with things inside the site, and it\'s such a great vehicle for that. But oftentimes people get hung up on that part of. Oh, I don\'t know what to, I don\'t know how to drive my car. Right? Like these, you know, WordPress sites with the right architecture, the right things can really drive and make a dent.

\n\n\n\n

That\'s our kind of thing with WordPress is like it\'s magic like that. But you still have to like, Drive it, you have to put gas in it and drive it, uh, with content. So that\'s a compelling angle for me with OpenAI. And I\'ve heard about all these things. Before we segue specifically to the integration you\'ve done too and some possibilities there, what, where do you see all of this and WordPress going?

\n\n\n\n

Jannis Thuemmig: Like, that\'s a very interesting question. Yeah, yeah. Uh, I think, I think it will be in relay, I mean, it\'s, right now we are specifically in the content age, right? So I, I\'ve seen a lot of people. [00:12:00] Going into the space where they try to create on demand articles using an ai, which is probably a terrible idea just through plagiarism because it\'s very easily detected if you don\'t lose like a rewriter and you use your very own wordings in between.

\n\n\n\n

So this is something that we will see switching, definitely. But what I see as an advantage in the future with WebPress is that people will use to. We, we learn to use AI for the advantage in the sense of speeding up their process. So it\'s also kind of a, a way of automating things, uh, in the sense that they don\'t need to write their content anymore from scratch, or they don\'t need to write a, think about copywriting that much.

\n\n\n\n

They just ask the ai, it\'s bit something out. They put it in, maybe adjust it, tweak it in their own way so that it has their very own style. And they probably just make the, the way of, of riding blocks 10 times a hundred times faster than it\'s right now. So we\'ll definitely see like a, a boost in performance and [00:13:00] probably block block posts over the long term.

\n\n\n\n

Cory Miller: Okay. Well, so that leads into this specific integration you have and the tutorial I, I was looking through before we started. Um, so you saw OpenAI has an api and tell us, tell us about that in WP Web Web Hook.

\n\n\n\n

Jannis Thuemmig: So, yeah, we, we basically started, um, after trying a couple of times how OpenAI works to, um, to integrate it with our plugin.

\n\n\n\n

So we, we usually go for creating integrations for different services and plugins, and, uh, in that case it\'s, it\'s once separately for OpenAI, which makes it compatible with all of the other services and, uh, plugins. We are integrated. And the main goal was just to provide the integration, right? Because it\'s so new, barely anyone understands the actual power of it and what is possible.

\n\n\n\n

So we, we just kind of created it out of the blue with a thought of, Hey, it would be cool to just have it, you know, let\'s see what, what\'s going to happen. And right now we are basically just [00:14:00] working on finding cool use cases. And, uh, there are definitely a couple, uh, like I\'ve, I\'ve, uh, showed you earlier.

\n\n\n\n

We already have a blog post on our. That basically describes how you can auto generate method descriptions using OpenAI and Yost seo. So you basically just feed it in the title and it spits your order, perfectly made method description that you can just use or adjust as you want. And these kind of things, they just now come through trial and error basically.

\n\n\n\n

And, uh, it\'s, it\'s very interesting to see where it goes. And I can see that with these kind of automat. Um, we can also provide what I mentioned earlier, that that possibility of feeding the AI new information that is not available within the AI itself. So because we can make these kind of workflows, um, if that makes sense.

\n\n\n\n

And this is, uh, this is mostly what we are trying to do right now. We basically just working on, on use cases, see what\'s possible, trying out different things and it\'s a super, super exciting. [00:15:00]

\n\n\n\n

Cory Miller: Yeah, absolutely. Because I mean, you talk about this car, you some a a site builder turns the car over and they start to use it.

\n\n\n\n

But that meta, uh, description is one thing. Like I honestly confess, I never do, you know, but it\'s, it\'s helpful, it\'s vital. And so like that one little use case in the bigger picture of what I can do, I think starts to step us into this and it is really interesting. Um,

\n\n\n\n

Jannis Thuemmig: oh, totally. Yeah. This is, this is literally just the, the tip of the iceberg.

\n\n\n\n

If you want, you can basically let the AI create a, a full schema, uh, like a shima for your, for your website. So whenever there\'s a blog post, it can write the how-tos and everything in, in kind of adjacent format and, uh, spits your order perfectly well formatted SEO description and, and everything keyboards, the, the, the whole how to, and this is just, it\'s just such a time saver.

\n\n\n\n

It\'s incredible.

\n\n\n\n

Cory Miller: Well, okay. Do you have a tutorial on that [00:16:00] too? Because that\'s really interesting. Um, , or if you don\'t, we need one. Um, but so you\'re going into OpenAI or chat GPT or whatever, and then you\'re saying you\'re asking a question or something like that, and then it\'s gonna give you back those things.

\n\n\n\n

Jannis Thuemmig: Yeah, exactly. It\'s just you literally ask it just a humanly written question, something like, give me back adjacent with each of these information. And it spits you out adjacent with each of the information. And Jason, you can always use on a technical level, right? So we can just leverage that out and use it through our plugin to use it in different automations and do different things.

\n\n\n\n

Cory Miller: Oh, that\'s super cool. Well, what do you have anything you wanna share about what you\'re doing next with this WP WebHooks?

\n\n\n\n

Jannis Thuemmig: Um. As a, as a use case, it\'s a, I mean, we, we definitely, for, for now we really try to just work on the OpenAI things mm-hmm. and try to find some cool use cases there. Uh, we had a lot of, um, a lot of actually customers reaching out about the possibilities as well and how exactly it works because the models [00:17:00] and the configuration is a bit complicated if you, if you\'re not fully aware of it.

\n\n\n\n

But, uh, yeah, we, we just follow the standards and, uh, things should be fairly easy. But yeah, for us, it, it\'s mostly, mostly OpenAI and creating new integrations. That\'s something we\'re, we are hardly focused on at the moment.

\n\n\n\n

Cory Miller: I, I really think this is, like you said, the tip of the iceberg that, um, I\'m really intrigued by our WordPress community post at Post Status to go, okay, here\'s this cool technology.

\n\n\n\n

How do we translate this into practical? Um, uses for the end client, the end user in WordPress. Um, so that, that\'s, that\'s interesting. We\'ll be excited to hear what, what you find in explorer and launch launch next.

\n\n\n\n

Jannis Thuemmig: Yeah, you should just see the block post, uh, our, our, our block. There will be a couple of more tutorials coming.

\n\n\n\n

They\'re already in the making, so in the next days we should see someone there.

\n\n\n\n

Cory Miller: Okay. Perfect. All right. Um, okay. So. You, you [00:18:00] showed me something as like this. I think this is just showing the power of what it could do when we start to get these types of integrations into WordPress. Do you mind showing me the one you were telling me about?

\n\n\n\n

Jannis Thuemmig: Totally. Yeah. Not a problem. Of course. I\'m just gonna share my screen, probably this one. Yes, so I, I was basically just fooling around the other day on. With our integration, trying to find some new cool ways we can use to, to present the OpenAI integration. And, you know, like, like I mentioned earlier, you can kind of ask the AI to create adjacent format, um, with specific data.

\n\n\n\n

So Jason is basically just a structured way of presenting data within the web that is something that the, the browser or the, the server can. And in our case, we, we, we wanted to have like, like in here, uh, just a simple field that you can write something and based on whatever you write, it updates the block post of [00:19:00] your choice.

\n\n\n\n

So in our case, we just created a quick contact form seven form as we have an integration with contact as well. And we connected that with open. To create a so-called Jason and update a block post based on a specific information. So I can just demonstrate it here. You can see I have three block posts available and let\'s just take this one.

\n\n\n\n

I just need the ID because that\'s the way we wrote it. So we have ID 97, and what I would like to do is, let\'s say I want to update the, the title of this post, right? So I can, I can write something like, Update the post with the id, let\'s say post title

\n\n\n\n

with ID 97 and change it to, um, this is a new title based on OpenAI. So it\'s, it\'s basically what we read as a, as a human tech, [00:20:00] right. But if I submit that and I let our workflow. The AI basically interprets that and, uh, changes it based on our parameters within, uh, Jason. And when I refresh here and, um, the flow ran, it should display it.

\n\n\n\n

See if it doesn\'t, no, it doesn\'t. Uh, so the thing is, because it depends what you feed the ai. So the AI basically needs to understand what you do. And, uh, in some, in some cases, that\'s, that\'s the problem with ai. It fits you out text, right? So you try to, you need to, to format. And kind of use the text in a different way so we can see.

\n\n\n\n

Okay. Just didn\'t follow it. Just what I\'m gonna do is, so to, to just, so the very same example, I just tried to type the similar thing again. Let\'s try it again. So, um, update the post with the ID 87 and change the title to, [00:21:00] um, OpenAI. Something new. Let\'s see now

\n\n\n\n

Cory Miller: I always love live demos, . Yeah, I know. When you were showing me before I was like, wow, that\'s super cool.

\n\n\n\n

Jannis Thuemmig: Yeah, it really depends on the AI, if I, if I do it right or not. Um, but it seems like you see that it\'s not completed. So basically something stopped within the AI and uh, yeah, I would need to, I would need to see what.

\n\n\n\n

Cory Miller: Yeah, so you were showing, you were showing just now the webhooks, uh, pro dashboard. Do you mind taking us for a spin around the Webhooks Pro dashboard?

\n\n\n\n

Jannis Thuemmig: Uh, yeah, totally. So it\'s basically like, you know, standard WebPress plugin and stuff. On the site menu we have, uh, our W2 Web Hooks Pro, um, menu item, and basically it\'s, it\'s separated into two main things, which is the automations, the flows, and the web. So there\'s, there\'s kind of a difference in between, because originally we came from the web website, which means it\'s kind of like a [00:22:00] one-way street for information to present.

\n\n\n\n

Let\'s say you, you update a, a post on your WebPress website and based on that post you can send data into a, a certain direction, like directly and instantly to inform another service about that there\'s a new post. But then we realized that there\'s more of a need to actually automate kind of certain work.

\n\n\n\n

And then we created something called Flows, which basically allows you to connect the, or create a consecutive order of triggers and actions. So web book triggers and actions to do certain things in a, in a specific flow as we call it. So I just head into it, uh, into one, which is the human posture. This was the example I tried to show you, which, uh, was currently not working out because of something that I need to see. Um, but what we have basically, within the floor. You can see we have a trigger, right? The trigger fires on a contact form seven. Within the settings, we basically selected the form that we created earlier, which is [00:23:00] embedded in, in the site.

\n\n\n\n

And we don\'t want to send the email as we just want to send the data to OpenAI. And it was tested. We set up some conditionals, um, stuff that\'s not really important for now, but, uh, this is, this is basically what causes the actual workflow to fire, right? So, This specific trigger comes along with all the data that was sent within this form, and we then reuse the data in the other kind of actions here.

\n\n\n\n

And as you can see, the first action is something, uh, is our OpenAI integration, where we basically sent that information that we had earlier, as you can see here, to OpenAI as a, as a text. And this is, this is what we read. So it basically says, get the posterity and the PostIt from the JSON, uh, in the JSON format.

\n\n\n\n

This is the sentence, and the sentence basically is a dynamic string that comes from the input that we sent within this form. So it makes more sense if, if we go through it logically while, while building it. But, um, [00:24:00] when you click into a field like this, you will see it shows a dropdown, and inside of the dropdown you will see all of the information that was sent within the trigger, including the question like, change the title of the post idea, ???

\n\n\n\n

So this is basically what we selected here. And this is kind of more details about the OpenAI stuff. Sure. And yeah, when you, when you continue safe, you can test the action directly within here. That\'s something I can try, um, just as an example to see what comes back. So basically right now I\'m sending a real request with the data that we got earlier.

\n\n\n\n

And this is basically the response. So we can see, we got some text back from the AI, which looks a bit weird as it\'s text. But within our plugin we have something like a formatter, which allows us to format the data and change into something readable. So I\'m just gonna quickly do that to, to give you a better example of what we get back.

\n\n\n\n

So as you can see, this is what we get back from the AI or from the formatter, which came originally from [00:25:00] the OpenAI. And this specific information we want to then use in another action to actually update the post. And this is, this is literally everything it does. You can just think it of simple steps that, like we have a trigger.

\n\n\n\n

The trigger causes, uh, runs whenever the, the specific contact form was sent, then we sent the data to OpenAI. We format it in a certain way, and then we update the post based on whatever data we got back from the OpenAI. Excellent. So, yeah, exactly. This is, this is basically it for that.

\n\n\n\n

Cory Miller: What, what are some of the automations.

\n\n\n\n

Yeah, I, I saw the create the automation. So what are the, some of the things that webhooks can do from the automation side?

\n\n\n\n

Jannis Thuemmig: Uh, you mean some examples for example? Ah-huh. Yeah. Yeah, yeah. Like I say, you can, you can, for example, connect the different services together. Let\'s, for example, say you have newcomer, right?

\n\n\n\n

So you can go to the, to the integration [00:26:00] screen. You can install any kinds of integrations that you, you are working. So we have around hundred right now. And let\'s, for example, say you have commerce installed, right? So you can then install commerce once it\'s, once it\'s available on your website and within one of those automation workflows, you can then say, whenever commerce fires, then send the data using, uh, a WebBook, for example, to mm-hmm.

\n\n\n\n

your bookkeeping system. Or send, send an email using the WordPress integration. So in here I can show you. Click send email, and then you have the possibility to send an email directly from your WebPress to the customer whenever, whenever, uh, an order was created. So it basically, it basically just allows you to do certain things that you would manually do within your dashboard.

\n\n\n\n

Mm-hmm. ? Yes. In an automated way

\n\n\n\n

Cory Miller: There\'s a bunch of those things for the Post Status setup out the way. I\'m like, oh gosh. Yeah.

\n\n\n\n

Jannis Thuemmig: I can\'t imagine. Same here.

\n\n\n\n

Cory Miller: And, and what are, what are workflows to, uh, or I\'m sorry, it\'s [00:27:00] webhooks. Oh, I thought that\'s a workflow somewhere. I read that wrong. Okay. Yes. So what

\n\n\n\n

Jannis Thuemmig: I can show you, it\'s, it\'s basically separated in two parts.

\n\n\n\n

It\'s sent data and received data. What it basically means is these are kind of the triggers available, right? So whenever a user created, or when a user was deleted or when a form was submitted, you can send data to a specific url. Let\'s say, for example, I want to send a URL to my website, um, I cannot call it demo, and I, I add my api endpoint here and I edit, and then you can see it here. Which basically means when ev, every time a user gets created, you can send a direct webhook request to this url and you can test it, you can customize it with, with more features, more setups, um, based on your needs. Gotcha. And this is, this is what I mean earlier, like a, a direct connection.

\n\n\n\n

And the receive data is basically the exact opposite. So instead of sending data out on a specific event, you send data in and to do something specific. So you can, for example, activate a plugin. As you can see, you can call a PHP [00:28:00] function, you can create a comment, a post a user. So we have basically mostly all of the options of WebPress available through, uh, web as well.

\n\n\n\n

Excellent.

\n\n\n\n

Cory Miller: You don\'t have a Slack integration, do you by chance ?

\n\n\n\n

Jannis Thuemmig: Um, that\'s the, that\'s the thing. Depends what Slack has as an api. Um, if they truly have an API and if they have an api and it can be integrated with something like an API key or a hero token, it can also be used with our plugin. Um, and that\'s an interesting point.

\n\n\n\n

That\'s good that you mentioned that we have something like,

\n\n\n\n

Cory Miller: um, it\'s a, it\'s a. Post Status specific thing, but I think a lot of membership sites, which is a big trend too. People building membership sites, course sites, you know, a lot of people like us obviously use Slack. Being able to, um, one, this is a nuance and I\'m, uh, sorry for sharing, but this is like create a private group or something like that.

\n\n\n\n

I\'ve looked in some of the Slack API and. I\'m using us as a [00:29:00] test for a second to say it is a broader thing. I think a lot of membership sites, they\'re using Circle, for instance, maybe they want to use something else. So I, I stopped you though. Keep going.

\n\n\n\n

Jannis Thuemmig: Oh, no problem. No problem. Um, yeah, but what I, what I mentioned earlier is that, like you say, with, with, uh, slack, we can kind of integrate with any service that allows, like simple API calls or web and uh, we have an integration available that is called Web itself.

\n\n\n\n

So, When you install one like that, for example, and you go to, let\'s say an automation workflow, I can, I can come within here, add a new action, and you see I have a WebBook endpoint available, which basically allows me to send data or send a request to a specific site. So if you have Slack, you would, you would uh, add your Slack U URL here, for example, right?

\n\n\n\n

Slack API or something, and then you. Select the method you want to use to send data, and you can send the data and add it here along. [00:30:00] So if there\'s a, a service out there that just follows the standard rep hook or api, um, standards, you can integrate them as well with our plugin. So there\'s not directly, uh, integration necessary. Basically.

\n\n\n\n

Cory Miller: Excellent. Well, one thing that\'s intriguing about this is for as long as I\'ve been in WordPress, I, it, it has led the way in truly democratizing publishing, but over the years, you see Facebook, Twitter, what name, whatever default. Closed wall type garden come out. And um, I just did an interview with Mattias who does activity plug plugin for the Fed averse.

\n\n\n\n

And I was like, the, you, you think about what you\'re doing here with webhooks and then the Fed averse is kind of bringing that power back to the. To the user and saying, okay, fed averse can help. To me, I just see the potential to go, let\'s, let\'s decentralize some of the social [00:31:00] networks. So when a billionaire buys the next thing, or they change their policy at one of these closed set social networks, you\'re, you know, all these people are affected by it.

\n\n\n\n

And, and taking some of that control. So that\'s where I see FE averse. And then I go, what\'s the power here is. Ground zero for what you\'re doing is your WordPress site, and with things like tools like this, then you can start, I don\'t know, it\'s just helping bridge that gap of power. There\'s so much usability and features that these closed gardens have, but tools like webhooks and potential with the Fed averse is like bringing some of that power back, and I see WordPress truly being in the space to lead and innovate and bring that power back to the users.

\n\n\n\n

Jannis Thuemmig: Totally. Yeah, I fully agree with you. The, the, an interesting point about that is actually that using our plugin, for example, you can use it kind of as a standalone on WebPress. So if you say you want to make automations, you don\'t necessarily need to use WebPress, but [00:32:00] you can just set up a WebPress environment and install our plugin and you can.

\n\n\n\n

Automate external services through WebPress. Right? So you can use it kind of as a middleman for yourself without actually using WebPress.

\n\n\n\n

Cory Miller: And you still maintain control

\n\n\n\n

Jannis Thuemmig: in a lot of ways you have full control. Yes.

\n\n\n\n

Cory Miller: Even if it\'s not a public facing site where you have content on like using that, that\'s the power, that\'s the other side of WordPress.

\n\n\n\n

Do this has been become this huge power powerhouse of a, you know, a software. I talked to a lot of people on the enterprise and they mentioned. the connections. There\'s a, um, my friend Kareem at Crowd favorite talks about WordPress being the open source hub to connect services. So, like your example there.

\n\n\n\n

I, I resonate with it cause I just talked to Kareem a couple weeks ago. I love that example. Yeah. Yep. Well, Gianni, anything else you wanna share, um, that you all have going on or you\'re excited about or anything I forgot to.

\n\n\n\n

Jannis Thuemmig: Uh, yeah, I\'m excited to make this tutorial work, so I think the next blockbuster [00:33:00] will see is probably about this example.

\n\n\n\n

Okay. .

\n\n\n\n

Cory Miller: I love it. Just to have, I love it. That\'s the beauty of being a part of this community as I get to ask cool, smart people that can do these things and see, see how they go. But I, I\'ll be playing around with open api. OpenAI\'s, API\'s, mouth, um, just cuz I was playing with that and like, wow, this is powerful and I love this kinda stuff and I love there\'s people like you experimenting with it, testing it, and giving users, um, that opportunity to do that.

\n\n\n\n

Um, so thanks so much today for being on the Post Status draft podcast. Uh, this is under our product People series. I love our innovators in our community like you, and so thanks for joining me today.

\n\n\n\n

Jannis Thuemmig: My pleasure, really. So it\'s an honor. Thank you very much as well for inviting me.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 14:44:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Olivia Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:32;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:85:\"WordCamp Central: WordCamp Entebbe: First Wordcamp to happen in Africa in 2023 is on!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:39:\"https://central.wordcamp.org/?p=3158482\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:108:\"https://central.wordcamp.org/news/2023/01/wordcamp-entebbe-first-wordcamp-to-happen-in-africa-in-2023-is-on/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3126:\"

\n\n\n\n\"\"\n\n\n\n

WordCamp Entebbe 2023 is set to be a major community event for WordPress developers, website designers, online publishers, students, and teachers to come together and share knowledge and experiences, network with other WordPress users, and gain inspiration for their work. Taking place on Friday, March 10th and Saturday, March 11th at the Uganda Wildlife Education Centre (UWEC) in Entebbe City, this WordCamp will be the first to happen in Africa and is poised to be a memorable event for all attendees.

\n\n\n\n

The event will feature a range of activities, including beginner’s training, inspirational talks, showcases, best practices, and the latest trends in WordPress development. In addition, there will be a Women in Tech panel discussion, aimed at inspiring and empowering women-led businesses to thrive in the technology industry. A Teacher’s Workshop will explore the integration of WordPress in the local education curriculum, providing teachers with the tools and resources they need to introduce WordPress to their students for web design projects and assessments.

\n\n\n\n

Attendees will also have the opportunity to take a free tour of the Uganda Wildlife Conservation Education Center, where they can learn about the animals of Uganda and the ecosystems in which they live. The center, which was founded in the 1950s to accommodate confiscated and injured wildlife, has grown considerably in recent years and is considered a premier facility for showcasing wildlife on the African continent.

\n\n\n\n

Accommodation options are available for those traveling to Entebbe for the first time. Attendees can find a list of hotels and guest houses through booking.com https://bit.ly/entebbehotels or by contacting the WordCamp team at entebbe@wordcamp.org for more information and guidance. The full schedule of activities will be published soon, and we look forward to welcoming you to WordCamp Entebbe 2023!

\n\n\n\n

Get Involved

\n\n\n\n

There are several ways to get involved! Check out the details below:

\n\n\n\n\n\n\n\n

Join the discussion via #WordCampEbbs hashtag on Twitter

\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 11:58:51 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Kasirye Arthur\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:33;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"Do The Woo Community: Reflecting on the Past and Embracing the Future for WooCommerce with Paul Maiorana\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74261\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://dothewoo.io/2022-2023-woocommerce-paul-maiorana/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:442:\"

It\'s that time of year again when Paul Maiorana, CEO of WooCommerce, joins us for a review of the year and a looking into 2023.

\n

>> The post Reflecting on the Past and Embracing the Future for WooCommerce with Paul Maiorana appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 11:29:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:34;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:17:\"Matt: Thirty-Nine\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:22:\"https://ma.tt/?p=75200\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://ma.tt/2023/01/thirty-nine/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4687:\"

The last year of my thirties! WordPress turns twenty this year. Automattic is now ~2,000 people across 98 countries. There’s so much that has happened in the past decade yet it feels very much like we’re on the cusp of something even more exciting.

\n\n\n\n

This morning started well; I pulled the hammock out of the garage (it had been hiding from the rain) and read for a bit, trying to get my 5-10 minutes of sun in the first 30 minutes like Huberman suggests.

\n\n\n\n

Candidly, the last year was a really challenging one for me personally. There were some beautiful moments, and I consider myself the most lucky in my family, friends, and colleagues, yet among that same group there was a lot of loss, existential health challenges, and that weighed heavily on me. It’s also my last year to get on 40 under 40 lists! \"😂\"

\n\n\n\n

Usually when people ask me what I want for my birthday I don’t have a good answer, but this year I do! As Heather Knight wrote about in the SF Chronicle, the beloved Bay Lights are coming down in March. This has to happen — the vibrations and corrosive environment of the Bay Bridge is taking lights out strand by strand. Fortunately it’s now been a decade since the lights first went up, and there’s much better technology both for the lights and how they’re mounted and attached to the suspension cables. Finally, the lights were not visible from Treasure Island or the East Bay before, but this new version 3.0 will be, which is why the artist behind the lights, Leo Villereal, is calling it Bay Lights 360.

\n\n\n\n\n\n\n\n

Like the Foundation series, we can’t stop the coming period of darkness from happening, but if we raise $11M we can bring the lights back. If we raise it soon we can shorten the time they’re down to just a few months, so I’m working with the 501c3 non-profit Illuminate to help fundraise. The idea is to find ten people or organizations to put one million each, and raise the final million in a broader crowdfunding campaign, to re-light the Bay Bridge and give an incredible gift to the people from every walk of life that see the bridge, and hopefully have their spirits lifted by the art. I’ve heard 25 million people see the Bay Lights every year.

\n\n\n\n

It’s a lot to raise, but every little bit helps so please donate here, and if you are interested to do a larger gift please get in touch. I’m committing a million dollars to the fundraise, and myself, Illuminate director Ben Davis, and the artist Leo Villereal are happy to personally connect with anyone considering a larger donation.

\n\n\n\n

Because of some family health reasons I’m back in lockdown, so going to try and throw an online party tonight in the “Matterverse.” We’re going to party like it’s late 2020. \"🎉\"

\n\n\n\n

All birthday posts: 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 04:37:53 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Matt\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:35;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:82:\"WPTavern: Automattic Launches Blaze Ad Network for Jetpack and WordPress.com Sites\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140985\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:93:\"https://wptavern.com/automattic-launches-blaze-ad-network-for-jetpack-and-wordpress-com-sites\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5113:\"

Automattic is bringing Tumblr’s Blaze ad tool to WordPress sites with its launch today on WordPress.com and Jetpack. Blaze made its debut in April 2022, to the delight of Tumblr users who will gladly shell out cash to get people to look at their cat or promote a game they made. It’s an affordable way to attract new followers or just send out something funny into the universe, starting at $5/day.

\n\n\n\n
\n

Note To all small Twitch streamers. Tumblr Blaze is the best advertising tool out there. It has an embedding feature allowing you to embed streams. I went from 20 to 100+ live viewers rn! pic.twitter.com/aolZduCTEe

— Ian Miles Cheong\'s Prime Minister (@MToph91) January 3, 2023
\n
\n\n\n\n

WordPress.com users can now to go to wordpress.com/advertising, select a site, and promote content with Blaze. Jetpack users have access to the ad network inside the WordPress.com dashboard.

\n\n\n\n\n\n\n\n

After selecting a post, users are taken to the design wizard where they can add an image, title, a snippet, and a destination URL. The URL can be the post or page or it can direct visitors to the main website.

\n\n\n\n

When Blaze first launched on Tumblr there was no way to target the promoted content – it just displayed to random users. Now there are a few more options. When promoting content from WordPress.com or a Jetpack-enabled site, users can narrow the audience by device: mobile, desktop, or all devices, select from a few main geographic areas (continents) or serve it everywhere. There is also a dropdown with topics of interest, but they are fairly general, e.g. Arts & Entertainment, Automotive, Business, Education.

\n\n\n\n\n\n\n\n

After selecting the audience, users can set the budget for the campaign, starting at $5 with a max daily budget of $50. With a minimum of $5/day for a week users can expect an estimated 5,900 – 8,000 impressions. For $25/day, users can expect 29,700 – 40,200, and up to 59,500 – 80,500 for $50/day. Site owners can monitor the success of their ads in the Campaigns tab.

\n\n\n\n

Content sponsored by Blaze will be promoted across WordPress.com sites and Tumblr pages, an audience that accounts for an estimated 13.5 billion impressions per month.

\n\n\n\n

Blazing has become somewhat of an art in the short time it has been available on Tumblr. It will be interesting to see how ads originating from WordPress.com and Jetpack go over with the Tumblr audience.

\n\n\n\n
\n

There\'s a whole code of ethics around Blaze on Tumblr basically if you use it to do anything other than shitpost people hate you, you can also buy Tumblr merch and buy fake verification (which isn\'t seen as a serious thing, it\'s just a way to support tumblr)

— New Year New Simisear Fan \"🐀\" (@LakeUncalming) January 10, 2023
\n
\n\n\n\n\n\n\n\n

Creating advertising content that works across the disparate audiences between WordPress and Tumblr-powered pages may be a challenge for some site owners. Tumblr users can only target audiences by location for blazed posts. It’s possible that WordPress’ additional targeting options can help funnel the ads to sites where they will be most well-received, but the announcement says ads will be promoted across WordPress.com and Tumblr.

\n\n\n\n

Blaze campaigns require approval to be in compliance with Automattic’s Advertising Policy before being published. They are currently moderated in approximately 30 minutes but this may change in the future as more users try out Blaze.

\n\n\n\n

Automattic is treading new ground in creating its own ad network that any user across Tumblr and WordPress can tap into. It’s a strategic move to extend access to the world of WordPress, given that it’s such a large audience, and it will be interesting to see how the company improves the targeting options to meet the challenges of serving both audiences.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 22:52:36 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:36;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:107:\"Post Status: Improving 5ftF Contributor Journey • Building Interactive Blocks • Layout Classes • WP20\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146399\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"https://poststatus.com/improving-5ftf-contributor-journey-building-interactive-blocks-layout-classes-wp20/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:16911:\"

This Week at WordPress.org (January 9, 2023)

\n\n\n

Share your feedback about how to improve the Five for the Future contributor journey. Check out work underway on how to make interactive blocks easier to build, and take a walkthrough of layout classes in WordPress 6.1. It\'s time to start planning; how will you celebrate WordPress\' 20th birthday?

\n\n\n
\n\n\n\n\n\n\n\n

News

\n\n\n\n\n\n\n\n

\n\n\n\n
\n
\n

Community

\n\n\n\n\n\n\n\n

Core

\n\n\n\n\n\n\n\n

Meetings

\n\n\n\n\n\n\n\n

Developer Blog

\n\n\n\n\n\n\n\n

Docs

\n\n\n\n\n\n\n\n

Hosting

\n\n\n\n\n\n\n\n

Marketing

\n\n\n\n\n\n\n\n

Meta

\n\n\n\n\n\n\n\n

Openverse

\n\n\n\n\n\n\n\n

Performance

\n\n\n\n\n\n\n\n

Polyglots

\n\n\n\n\n\n\n\n

Plugins

\n\n\n\n\n
\n\n\n\n
\n

Project

\n\n\n\n\n\n\n\n

Support

\n\n\n\n\n\n\n\n

Test

\n\n\n\n\n\n\n\n

Themes

\n\n\n\n\n\n\n\n

Training

\n\n\n\n\n\n\n\n

Online Workshops

\n\n\n\n\n\n\n\n

Tutorials

\n\n\n\n\n\n\n\n

WPTV

\n\n\n\n\n
\n
\n\n\n\n
\n\n\n\n\n\n\n\n\n\n\n\n

Thanks for reading our WP dot .org roundup! Each week we are highlighting the news and discussions coming from the good folks making WordPress possible. If you or your company create products or services that use WordPress, you need to be engaged with them and their work. Be sure to share this resource with your product and project managers.

Are you interested in giving back and contributing your time and skills to WordPress.org? \"🙏\" Start Here ›

Get our weekly WordPress community news digest — Post Status\' Week in Review — covering the WP/Woo news plus significant writing and podcasts. It\'s also available in our newsletter. \"💌\"

\n\n\n\n
\n\n\n\n
\"Post
\n

You — and your whole team can Join Post Status too!

\n\n\n\n

Build your network. Learn with others. Find your next job — or your next hire. Read the Post Status newsletter. \"✉\" Listen to podcasts. \"🎙\" Follow @Post_Status \"🐦\" and LinkedIn. \"💼\"

\n
\n\n\n\n
\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 18:08:54 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Courtney Robertson\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:37;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"WPTavern: ClassicPress Community Votes to Re-Fork WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140878\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wptavern.com/classicpress-community-votes-to-re-fork-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3772:\"

In December 2022, the ClassicPress community voted on whether to re-fork WordPress or continue on with the project as-is. As WordPress continues to evolve, ClassicPress fell behind in pursuit of PHP 8+ compatibility. The fork is based on WordPress 4.9 and users are increasingly limited in what plugins will work with the five-year-old codebase.

\n\n\n\n

In a discussion limited to ClassicPress core contributors, Viktor Nagornyy, one of the project’s directors, announced the results of the vote: “The option to re-fork has 20 votes while continue-as-is has 18.” Nagornyy summarized previous discussions and suggested an approach that would be more realistic for the project’s limited contributors:

\n\n\n\n
\n

ClassicPress can’t be WordPress without Gutenberg, but it also can’t be its own CMS with a small core team at this time. There are simply not enough developers to make progress without backporting code from WP to move away from WP.

\n\n\n\n

An almost even split in the poll suggests the best option might be a hybrid one, find a compromise solution that will satisfy both sides.

\n\n\n\n

With a small core team, we have to find ways to be more efficient, to get more done with less. The only way to do that is to leverage all the work that’s done by WP contributors. As the core team grows, we can always explore the possibility of splitting away from WP but at this point in time, it’s simply not feasible.

\n
\n\n\n\n

Some participants in the previous discussion saw re-forking as postponing the inevitable, kicking the can down the road until the next re-fork, but it is the only option if users want to retain compatibility with the rest of the WordPress ecosystem.

\n\n\n\n

“If you read recent threads, you find out that the community expects plugin compatibility with WordPress… another reason for the re-fork option,” ClassicPress core committer Álvaro Franz said.

\n\n\n\n

Franz, who is also the author of the WP-CMS fork based on WordPress 6.0, previously said he would be unwilling to help with a continuation of the current version based on WordPress 4.9.

\n\n\n\n

“It [ClassicPress] doesn’t have to be a competition (and it never could compete with WordPress anyways), but it can be a leaner version, for people who are already disabling Gutenberg via plugins, for developers who want a different approach to the way they develop their projects (closer to ‘the classic’ experience, but yet… modern!),” Franz said.

\n\n\n\n

“Eventually, it won’t make sense to run a fresh copy of WordPress to then go and install a plugin that ‘disables’ half of it. What’s the point? Why not have a version that covers that specific use case?”

\n\n\n\n

As part of Nagornyy’s proposed hybrid approach, he suggested the project retain some changes that were introduced in ClassicPress in v1.x, such as the privacy-oriented changes (anonymizing data CP sends to APIs), the news widget, and ensure that all API endpoints use ClassicPress APIs as in v1.x.

\n\n\n\n

The discussion continues around how to proceed with the fork. ClassicPress contributors are leaning towards using Franz’s WP-CMS fork based on WordPress 6.0 but have not finalized the details yet.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 15:10:57 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:38;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: #58 – Lax Mariappan on How Headless WordPress Works\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"https://wptavern.com/?post_type=podcast&p=140972\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:77:\"https://wptavern.com/podcast/58-lax-mariappan-on-how-headless-wordpress-works\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:56075:\"Transcript
\n

[00:00:00] Nathan Wrigley: Welcome to the Jukebox podcast from WP Tavern. My name is Nathan Wrigley.

\n\n\n\n

Jukebox has a podcast which is dedicated to all things WordPress. The people, the events, the plugins, the blocks, the themes, and in this case, how Headless WordPress works.

\n\n\n\n

If you’d like to subscribe to the podcast, you can do that by searching for WP Tavern in your podcast player of choice, or by going to WPTavern.com forward slash feed forward slash podcast. And you can copy that URL into most podcast players. If you have a topic that you’d like us to feature on the podcast, well, I’m very keen to hear from you, and hopefully get you or your idea featured on the show. Head to WPTavern.com forward slash contact forward slash jukebox, and use the form there.

\n\n\n\n

So on the podcast today, we have Lax Mariappan. Lax is a web developer based in the Philippines. He’s an open source enthusiast and lover of all things WordPress. Lax has been tinkering with websites since high school. But it all changed when he discovered WordPress in 2010. Lax currently works as a backend engineer at WebDevStudios.

\n\n\n\n

We talked today about Headless WordPress, and it’s a complex topic. Headless is the concept of decoupling the WordPress admin from the front end of the site. WordPress will continue to work as expected, but the presentation layer will be done by a different technology. React Gatsby and Remix being some popular choices.

\n\n\n\n

This implementation of WordPress is complex, requires technical knowledge above and beyond that needed for a more typical WordPress install. But it has its benefits.

\n\n\n\n

Lax talks through all of this in great detail. How keeping on top of all the additional dependencies Headless WordPress requires can be time consuming. How it can create difficulties for content editors who don’t always get to see what their work will actually look like in real time. Why this approach to WordPress can take more time and resources during the build.

\n\n\n\n

Lex explains how these problems typically crop up, and how it’s possible to plan ahead and build in solutions for all the problems that you might encounter.

\n\n\n\n

If you’ve ever thought about going headless with WordPress, then the podcast today is for you.

\n\n\n\n

If you’re interested in finding out more, you can find all of the links in the show notes by heading to WPTavern.com forward slash podcast. Where you’ll find all the other episodes as well.

\n\n\n\n

And so without further delay, I bring you Lax Mariappan.

\n\n\n\n

I am joined on the podcast today by Lax Mariappan. Hello Lax.

\n\n\n\n

[00:03:30] Lax Mariappan: Hello, Nathan.

\n\n\n\n

[00:03:30] Nathan Wrigley: Very nice to have you with us on the show today. I have to commend you for your staying power, because Lax and I have tried to record this episode a couple of times and he’s been incredibly, incredibly thoughtful about getting his, all of his equipment and everything working. So thank you, first of all, I would like to express my gratitude for you staying the course.

\n\n\n\n

But before we get into the podcast, Lax, I wonder if you wouldn’t mind spending a moment just introduce yourself. Tell us who you are, where you are, who you work for, how long you’ve been using WordPress, all of those kind of things.

\n\n\n\n

[00:04:06] Lax Mariappan: Thank you. It’s good to be on WP Tavern, it’s one of my favorite publications, and also the favorite podcast. So I’m Lax, Lax Mariappan. I’m from India, and also I’m from Philippines. So I would say I live in both countries, and I use WordPress since my school days, like 2009. So I was looking for a platform to build a website for an event or something, and then I found out Blogger versus WordPress, and I liked WordPress more even that time.

\n\n\n\n

So since then, I’m using WordPress almost every day. And my first job I got started working as a PHP developer, I would say, and then fully focused on WordPress. And I wrote my first plugin in 2011. It’s a very simple one. It’s now kind of obsolete because Facebook changed it a lot. So I wrote a plugin for something to fetch Facebook feed. So, and then my journey goes on. Right now, I work as a backend engineer at WebDevStudios. So where I get a chance to learn and work more with headless CMS every day almost.

\n\n\n\n

[00:05:09] Nathan Wrigley: Your work at WebDevStudios, I don’t know a great deal about the company, but my impression of the company is that you work with, how should we describe it? Enterprise clients. You’re dealing with fairly large projects. I would imagine sizable budgets. Those kind of things, right?

\n\n\n\n

[00:05:27] Lax Mariappan: Yeah, yes. Enterprise level.

\n\n\n\n

[00:05:28] Nathan Wrigley: So when we decided we were going to have this conversation, Lax introduced the subject to me of headless WordPress. Now this is a word which I imagine some of you have heard before. Maybe some of you have never heard the word before. Perhaps there’s a subset of you which have experimented with it, but I’m expecting that the majority of WordPress users have not.

\n\n\n\n

So, first of all Lax, would you mind giving us a very, in depth I suppose is the right word. Give us an analysis of what headless WordPress is because I’m sure many people think they know what it is, but perhaps they don’t.

\n\n\n\n

[00:06:06] Lax Mariappan: So headless, or decoupled CMS, so first we all know content management system, right? So WordPress, we are using WordPress now as a content management system. It started out as a blogging platform. We used it mainly for blogging. And then WordPress introduced custom post types, taxonomies and all that sort of stuff.

\n\n\n\n

So we are now using WordPress to build simple to complex websites. Forums. Some people use it for their colleges, universities as a social media platform, and some of them use it for a job board and everything, right? So we have plugin for everything and we can customize it and we use it.

\n\n\n\n

So when it comes to the traditional CMS, we call that as monolithic. I hope I’m not using too much jargon here. Monolithic in the sense it has everything into it. So for example, if you go to a website, the header, footer, the sidebar, and the content that you see and the forms and everything that comes from the same CMS itself. So it is going to be, let’s say, in the case of WordPress, it’s built mostly with PHP and JavaScript.

\n\n\n\n

So everything is going to be PHP template with a bit of JavaScript and CSS to it. But when you say on the contrast, headless CMS, it means, so you can consider that as a, I would not say person. Maybe something like, you can imagine something that doesn’t have a head. So in the sense the body is the same, head is different.

\n\n\n\n

So you can imagine that as, you are going to use the same admin panel and you are going to have the same WordPress features. You can add the content, you can add menus, you can edit anything, you can add users, all that stuff. But when I view the website, so it’s not going to be your theme. So it’s not the typical way of how WordPress gets rendered.

\n\n\n\n

So instead we will be decoupling it. So that is WordPress admin will stay on another site. It can be on a subdomain or a sub folder, but the front end is going to be a different platform. So it’s going to be hosted in a, mostly a JavaScript based stuff. So you can use either React based frameworks like React itself or Gatsby, Next.js or Remix, or anything that you like.

\n\n\n\n

And also you can either go in another route as well. So you can make it like a fully static website, or you can render it on every time as a server side rendering as well. So every call will go to the server and renders.

\n\n\n\n

Okay, so now we can call that a small intro about headless. You may already know this one. It became a buzzword a couple of years ago, right? But now everyone wants to go as headless. I see that company goes headless, or my competitor goes headless. So I want to go that way. But, unpopular opinion. Maybe you might hear some other people say that too. Headless is not for everyone, or I would say not for every use case.

\n\n\n\n

It depends on how much content that you publish. What are your goals and what you want to achieve. So headless is good, it’s performant, it’s fast, secure, and it gives you more freedom and flexibility, especially in terms of performance it’s really good. But I would say it’s not the something like you should go headless. It’s not the answer.

\n\n\n\n

[00:09:10] Nathan Wrigley: So essentially you’re saying that there are scenarios where this is desirable, but there’s going to be other scenarios where WordPress, in the traditional sense of the word. The regular WordPress that you download, perhaps use a hosting company and it’s all driven by PHP. The normal way of doing WordPress. That might be the best solution for lots of people.

\n\n\n\n

Okay, so we’ve got our WordPress website, which we can interact with, and then the content that comes out of that website is pushed to something else. And probably we’ll get into what the options are there. But let’s take the use case of a company which comes to you and says, okay, we’ve heard this buzzword. We think that we want to go headless.

\n\n\n\n

What are the benefits of going headless? Let’s forget about all the problems that might be associated with it. Can we just iterate through the things that you will gain if you manage to pull off a headless WordPress website. Now, I know there’s going to be all sorts of different scenarios there, but maybe just pick out the low hanging fruit. Some of the things which you believe are really beneficial.

\n\n\n\n

[00:10:17] Lax Mariappan: Yeah. The first and foremost, or the popular one, is the performance. So WordPress uses PHP templates. We will do everything with PHP and Javascript and also a little bit of caching to render our traditional CMS like traditional pages. If you use a normal WordPress installation with a theme. So that’s how it’s get rendered.

\n\n\n\n

So there you can see it depends on the hosting company as well, and also how much plugins that you use and how you configured them. So that affects the performance of a site. But when it comes to headless everything is going to be bundled, and there will be how a normal JavaScript based application gets rendered.

\n\n\n\n

So it’s going to be a modern web application where you have control over, for example, if your page doesn’t use certain CSS classes, those CSS will not get loaded for that page. So I would say the assets that are loaded, it will be less. And the images will be more optimized. In either case, like in traditional too you can optimize images, but it’s like the performance is the first one, I would say.

\n\n\n\n

It’s going to be both developers will love it and also the site owners, and also, let’s say marketers, Everyone will like the performance aspect of it. And in terms of headless, I would say developers will like it, especially in terms of, so you can repurpose the content. So if you are having a CMS, WordPress as a headless CMS, you can use that same endpoint, get the data and display it in a different formats quickly.

\n\n\n\n

Other than a WordPress theme. So for example, if you’re using a WordPress theme, you have to create multiple templates. So this is a template for mobile, and this is something that, for example, if you want to use it for a landing page, you may have to do some small or extra changes. But when it comes to headless, you can just customize it in a way that you want to.

\n\n\n\n

For example, I want to have a landing page. I don’t want certain stuff to be there. So you can turn on, off certain components, that’s it. So it’s like you can render the blocks and render the content faster. So developers and designers will like it. And also, in terms of the security, that’s where I’m more interested in cybersecurity especially. When people say WordPress sites are not secure, that triggers me actually. Yeah, I do get angry.

\n\n\n\n

So it’s like, you don’t have to worry about that. So you don’t have to worry about changing your login page url. Adding captcha to your login form, all that stuff. Because that URL is going to be safe and secure. No one knows where you are hosted your CMS.

\n\n\n\n

[00:12:49] Nathan Wrigley: Can I just interrupt there? So could you explain that, because I imagine there’s a bunch of people scratching their head at this point. Because normally, let’s say you have a website, it’s example.com. You’re going to go to example.com/wp-admin, and there is your login page. But there’s something in between here. I’m not sure that we explained that quite. So just explain why the login is secure. Explain where it is and why it’s not normal WordPress.

\n\n\n\n

[00:13:19] Lax Mariappan: Yeah, so I mean, normal WebPress is also secure but people can guess it, right? Say example.com/wp-admin, so they know. They can see from the source code and the page source, they can see oh, this looks like a WordPress site. And then they can guess the admin url. So slash wp-admin, it’ll redirect them to the login page, right.

\n\n\n\n

But when it comes to headless, the example.com will be hosted somewhere, and the front end that you see will be different. So for example, let’s say CMS is your WordPress installation, all WP. So you can call that like wp.example.com. So that’s where your WordPress stays in. But when you go to the example.com, that’s your front end, so that’s just JavaScript and html. So it’s like, if somebody wants to hack your site or somebody wants to, just guess what will be the admin url. So they cannot.

\n\n\n\n

[00:14:10] Nathan Wrigley: It’s a difficult concept to understand if you haven’t encountered this before. But what you’ve got basically is a WordPress website, which is the container for the content, but it isn’t the website and we’re not used to that in traditional WordPress. You go to example.com/ wp-admin, get redirected, log in, do all the things, and click publish, and as soon as you click publish, it will be present on the website. That’s not the way that this is working because the WordPress website is completely decoupled from the thing which is presenting it to the world, right?

\n\n\n\n

[00:14:48] Lax Mariappan: Yeah. Yeah. Completely decoupled.

\n\n\n\n

[00:14:50] Nathan Wrigley: So given that, there’s no connection between, okay, here’s my website at example.com and where I might log in. And because of that there isn’t the capability to just guess the login page and then bruteforce an attack and so on. So in terms of security, it offers that benefit. The thing which people are most worried about, somebody getting your admin password going in and spoiling your site. That’s highly unlikely because they simply won’t know where to look.

\n\n\n\n

[00:15:23] Lax Mariappan: Yeah. And also, so for example certain normal pages like comments, so that’s where we get a lot of spam, right? So comments will go to comments.php. When you submit a form without any data, or maybe if it’s spam data, it just goes there, right? But when it comes to headless, we will be using some extra customization for the comments and everything.

\n\n\n\n

So it’s not going to be the data will store as comments in the database, and it’ll be, you can view them as comments in the admin panel. But when you are viewing it in the site, so you are reading a blog post, you have a comment form, so that form is HTML and JavaScript. So that’s not how a typical, a normal WordPress form, normal comments form.

\n\n\n\n

So that’s where you will get less spam as well. So you don’t have to worry about that too. Like people submitting spam data and also any other form. So that’s another thing. And you don’t have to worry about any other security related stuff, because it’s just static.

\n\n\n\n

So people cannot do anything or manipulate data. So it’s going to be just HTML stuff. Whatever they can do is just view the data. So I would say in the headless, so if you are viewing some pages or we are in a archive page and post archive, news archive, any archive page or any other page that does the data and fetches the data from the database, all that stuff.

\n\n\n\n

So all that stuff will be protected routes. So people cannot easily guess. Sometimes you might encounter database related attacks, right. So you may hear cross site scripting attack or any other stuff like, somebody trying to get data either they pull your data or they want to insert some other data to the database. That’s not the case.

\n\n\n\n

Everything is going to be static, like just html, and it’s only read only. So people are not going to input any data. And the input will be just maybe a comments form, contact us form, something like that. And that will be handled. It depends on what form provider you are using, or how you configure it, but still it’s more secure that way.

\n\n\n\n

[00:17:25] Nathan Wrigley: So just to reiterate the point one more time, just in case anybody hasn’t been paying attention. We have our WordPress website. It is used by the developers, by the content creators, by the editors. They do their normal work inside of WordPress, but the thing which is being viewed on the front end by the population at large is completely separate.

\n\n\n\n

You’re just sucking the data out of WordPress and putting it into whatever you like. The security’s fairly obvious, you’ve explained that really well. The performance, obviously, if all that you are showing is static html, essentially. That’s going to load really, really quickly. Nothing needs to be built at the time that the page is viewed and so on and so forth. It’s already been created.

\n\n\n\n

This all sounds amazing and of course that raises the question, why aren’t we all doing it? And you have given us, in the show notes you’ve given me, three different things which we perhaps should talk about, and some of them, you explained the problem and then we’ll get to the solution.

\n\n\n\n

So the first one that you talk about is dependency hell, you’ve described it as. And, I’m guessing that having a headless site is not straightforward. We’re very used in WordPress to, novices can install WordPress incredibly quickly. You basically upload a zip file and unpack it and connect it to a database, and these days, you know, you go to a hosting company and not even that. You just click a button and, wow, there’s your WordPress website 30 seconds later.

\n\n\n\n

I’m guessing that this is not the case for headless. There must be all sorts of complex layers of things going on in the background, and you say that in many cases it can become very difficult. Dependency hell. So describe the problem of all the dependencies.

\n\n\n\n

[00:19:13] Lax Mariappan: So when you have a WordPress installation, we will be installing plugins, right? You might be, if you are using WordPress for a while, you are already aware of the jQuery migrate plugin. All that stuff. So WordPress uses jQuery even now. So jQuery is a dependency that WordPress requires. WordPress depends on jQuery in admin panel, and also on the front end.

\n\n\n\n

So if you want to get rid of jQuery, it’s kind of, WordPress may not be the same, if you want to eliminate that. Because WordPress depends on it. So it’s something like, let’s say you cannot say that as a oxygen, but it’s something that we all need it. So we need that to survive. So WordPress needs jQuery to work normally.

\n\n\n\n

So similar case, when you are building a headless site, you will be requiring a lot of frameworks, libraries, and also packages. So for example, if I’m going to choose Next.js as my front end platform, front end framework. So Next.js is built with React. If I want to use Next.js, I may want to use some other Next.js related libraries.

\n\n\n\n

So it is something like if you are on Android, you may want to add extra apps on your phone. If you are an iPhone, you’ll be adding some more extra apps to extend, right? It’s the same case. Similar to plugins. Instead of that plugins, we will be adding packages. So that packages helps the developers to add extra features that we need.

\n\n\n\n

So the problem here comes in is, everything gets stacked in and one will be dependent on another. So, for example, if someone is installing a package like for SEO, and maybe that package will require something else. And let’s say if Nathan is maintaining SEO package and I installed it, and for example, for whatever reason, Nathan becomes a musician and he doesn’t, he is not interested in SEO anymore.

\n\n\n\n

So he may not be more active in maintaining that dependency, maintaining that plugin or that package. So what happens is I’ll be waiting for him to fix the bug or some errors. Or I will waiting for him to upgrade to the lightest version. But it’s not the case, right? So, my Next.js package will be waiting for Nathan, so it’s like I’m depending on him, but he’s not available. So in that case, I have to go and do that work as well. So that adds to our development timeline.

\n\n\n\n

And then, so this is just one package and one scenario. So this happens with multiple packages and stuff. And this is not just Node or NPM packages. It also happens to WordPress stuff as well. So, for example, let’s say we have a popular forms plugin, or we have a popular slider or any other plugin.

\n\n\n\n

So you will install that plugin and you want that plugin to work with headless. So how we are using headless, it’s the data is stored in the WordPress, and we want to get the data through either Rest API. It’s a method that we, you know, you go to a url, you ask the WordPress, hey, give me this data and it’s going to give. Or you’ll be using GraphQL. It’s the same. You go to an endpoint and you’re going to say, hey, I’m looking for this post. I want five posts from this date. So it’s going to give that data as well.

\n\n\n\n

So either you use Rest API or GraphQL. The problem is a plugin that you are using, your popular forms plugin, your popular slider, or any other plugin that you’re using. LMS plugin, E-commerce plugin or any plugin, like a payment gateway. So you have a plugin and you want to use it with headless. So that plugin should work with the Rest API or Graph QL. So if that doesn’t work, if that doesn’t give you the flexibility, and then you are still stuck there.

\n\n\n\n

Because you cannot go and create everything on your own, right? So we cannot reinvent all the wheels. We don’t have time to create everything from scratch. So that’s where it’s like that becomes a bottleneck. So you are like, hey, I found the plugin. I started working on it. It works up to this mark, but it’s not a hundred percent. So it’s like it does its job 80%. Now I have to go fill in that 20%. It adds to the budget, it adds to the development timeline. So that’s the dependency hell.

\n\n\n\n

[00:23:15] Nathan Wrigley: Yeah. So in the case of all of the technology, which is in the background if you like, which we haven’t really talked about too much, but like you said, the things which you are requiring from third party developers. There’s a dependency there, and it’s very similar to the dependency that you may have on plugins, you know, you want them to be updated and so on, but you are adding extra dependencies. And of course, the more dependencies you’ve got, the more costly, time consuming it is.

\n\n\n\n

I’m guessing that most of the things that you are depending on, in addition to WordPress and you described what a few of those were, you could, I suppose, do some due diligence and figure out which projects have been well maintained, updated frequently, and so on. And I guess in the open source world, much of the dependencies that you’re using will be open sourced, so you could fork them. But again, you are creating probably a large amount of work for yourself and your team.

\n\n\n\n

[00:24:13] Lax Mariappan: Yeah that’s true. Well said. So it’s like, since it is open source, it’s good. Like lot of reviewers. We have a lot of eyes on the code, and you can fork it. You have the freedom to do whatever you want. But still you are looking for a solution and that becomes a problem. You have to fix that as well. And that adds to the, another dependency, another dependency. It becomes a cycle that you cannot escape sometimes.

\n\n\n\n

[00:24:36] Nathan Wrigley: I guess this is a bit like a seesaw. You know, on the one hand you described all of the benefits, performance, security, and so on, of headless. And then on the other side is, is all of the things that we are now describing. You know, the dependencies and so on. You’ve got to weigh up at the beginning of the project whether one thing is worth all of the time and effort that may be required to do it.

\n\n\n\n

And I’m guessing in many cases, certainly at the enterprise level, the answer’s going to be yes, because the budget is there, we can put enough bodies to work to make all of this happen, and if we need to fork things, there’s enough people on the team that can do that and maintain the project, which has fallen into disuse. But for a little project the seasaw may tip heavily against something like headless just because of the things that you’ve described there.

\n\n\n\n

Okay. So that was our first thing, dependency hell. The second thing that you wanted to talk about was the fact that in the WordPress world, especially in the last five or six years or so, we are really used to what you see is what you get, WYSIWYG. You save something in WordPress, you publish something and you have almost a hundred percent certainty of what it’s going to look like. The backend looks like the front end, especially with things like page builders and so on. But you say that that’s not always the case with headless solutions. Why is that?

\n\n\n\n

[00:25:55] Lax Mariappan: We will be creating custom blocks. So, either there are a popular way of building now custom blocks is with ACF. So you all might be aware of and using it, even though you are not a programmer, you might be using it, right? So ACF is easy to install and create some custom fields. So you can use ACF to block, to build blocks for the site.

\n\n\n\n

So those blocks can be used or you can build your own custom blocks. You can use any block starters like, frameworks that are available now. Or you can just follow our, WordPress comes with packages that you can on build command, so you can just build your block in a matter of seconds.

\n\n\n\n

But still, all this stuff. So for example, if you are having custom blocks, I’m not talking about just normal blocks, like where you add a paragraph or image or something very simple. That is easy to build and that’s easy to see. That’s different. But I’m here talking about something complex.

\n\n\n\n

So for example, you can imagine that as an Elementor widget or, some other items that it comes with the page builders. So, let’s say a slider, maybe tabs, accordions, all that stuff, right? So that can be added through the blocks itself. But you cannot preview them, because when you add them in the admin panel and we add them in the content. Those content gets, you know, you can choose like, oh, this is the tab title, this is the content.

\n\n\n\n

And you can keep adding the content, but you don’t know how it’s going to render in the front end. But let’s say if you are using some, there are a lot of free blocks and also even premium blocks available. So if you are using a block to build them, and then using the normal WordPress installation. Or you can use WordPress with the full site editing, the modern themes, or the hybrid themes, like old plus full site editing themes.

\n\n\n\n

Still they both work well. Like you can preview, oh, okay, this is the tab I added this content. I can’t view this one. But when it comes to ACF blocks or other certain custom built blocks, you cannot preview them.

\n\n\n\n

So when a editor or a user adds content, they may get lost. So I have a slider. I want to add three, four images to it. I may get lost. Oh, what’s the third image? What I have added there, and how it looks? Is the images correct? Is the text rendered properly or should I reduce any title or text or anything, right? So all this stuff becomes a little tricky. And also sometimes it becomes a pain for the content writers, content editors, and also the site owners.

\n\n\n\n

[00:28:24] Nathan Wrigley: So in the normal, traditional WordPress, let’s say we’re creating a page, we add a page, and we use whatever tool it is that we want to use for that. We add in some blocks. We are perhaps using Elementor, whatever it may be. And we click publish and then we are able to immediately view that because WordPress is working in the traditional sense of the word. The page gets pushed through the templating engine and it’s rendered with its template and we can see it right away.

\n\n\n\n

But because that’s not happening here. And the mechanism for rendering that page is entirely different. You can’t necessarily view it immediately. Have I kind of encapsulated that? What you are doing in the backend, because it’s decoupled with the presentation layer on the front end, you can’t necessarily always see it?

\n\n\n\n

[00:29:16] Lax Mariappan: Yeah, so that’s the challenge. So the solution here is to customize the way you built. So for example, we can give them a preview button so they can preview what are the slides, and how they look. And they can see that immediately in the editor itself. Like when they are adding content in the block editor, they can see it.

\n\n\n\n

And also the other way is to have a button, a preview button. So that will preview before the content gets published. So, you can change the workflow. So if somebody hits, instead of publish, you can have like a preview button or keep it as a draft. So that way it’s like nothing goes to the front end without your approval or preview, right? So you have to preview it and see, oh, make sure everything looks correct, and then you can say, hey, I want to publish it. Yes, confirm, publish it, and then it goes to the frontend.

\n\n\n\n

[00:30:04] Nathan Wrigley: That’s fascinating. That’s really ingenious. So, because we can’t necessarily see it on the frontend, you and your team have built a custom preview system. So on a block by block basis, you can see what that block will look like when it’s rendered. So in the example of your slider, presumably where we’ve got three or four fields. We’ve uploaded maybe some text, we’ve uploaded an image, and it’s just a bunch of fields. Normally we’d click publish and we’d go to the page and preview the page and we’d see it right away. But in your scenario, you are going to hit a button inside the block to show what that block and that block alone will look like. Have I understood that?

\n\n\n\n

[00:30:48] Lax Mariappan: Yeah, that’s what we did. Because the users, they are used to the traditional WordPress. And especially that was with classic editor, I mean the old editor. So if you insert an image, they can see it’s an image. And if you insert something, you can see. And we are all used to the page builder era, right? So if you add a accordion, you can see how the accordion is going to look.

\n\n\n\n

But when it comes to headless, all this stuff is going to differ. So, the tabs, accordions, sliders, and also anything else, any other custom stuff that we built, we added a preview button, and when you click on the preview, you can see that right away.

\n\n\n\n

Then you can make sure like, oh, the colors are correct, the image is correct, and everything renders properly. Because sometimes if you are not looking at the content and adding content, you might miss some data, right? So you might have missed a small setting that says full width, or you know, boxed. So then you feel like, oh, why this looks so awful. Oh, I’ve missed this full width button. So that’s how the preview button works.

\n\n\n\n

[00:31:49] Nathan Wrigley: So if I’m looking at the block and it’s a, let’s stick with the slider just for the sake of it, and I’ve uploaded my images and whatever fields were required and I click the preview. Does it literally happen inside that block? Or is this some kind of modal which pops up and shows things? Or is it, is it literally taking over the block itself?

\n\n\n\n

[00:32:09] Lax Mariappan: Ah, it’ll be within the block. Like it will replace, so for example, if you have a block and you are adding some content to it, and when you click on the preview, it’ll replace where you are adding the content, right? It’ll replace the form. Form of the block where you are saying like, hey, this is the title, this is the subheading, this is the description. Instead of that, it’ll just render the titles, heading and description.

\n\n\n\n

[00:32:32] Nathan Wrigley: Right, and then you toggle that off again once you’re, once you’re happy. So, ah, that’s really interesting. So the workflow there is really very different. And I’m presuming that after a period of time, the people who are editing, creating this content, that just becomes part of the process? They just understand that, okay, rather than viewing the whole page or whatever it may be, post whatever, I’m just viewing this little bit, and I’ve done it several times now and I’m confident that if it looks right inside the block preview, then I can click publish, wait for everything to happen, and hopefully that page will go live. And, it’s just a different workflow that you have to get used to. But once you’ve done it several times, it’s, familiar and normal.

\n\n\n\n

[00:33:14] Lax Mariappan: Yeah, it becomes part of the workflow. And also, like we discussed earlier, your site will be like, CMS.example.com. And the front end will be on example.com. Sorry, every time you have to go to example.com/about, example.com slash contact us. Instead of that we will have a preview button. So, you can preview each block and you, if you, or feel like, hey, I want to see how the whole page looks like, you can click that preview, and that will take you, or that will show you immediately, oh, this is how the front end, like example.com/the page will look like.

\n\n\n\n

[00:33:45] Nathan Wrigley: Yeah, that’s a good point. We’re so used to the preview button being connected to the URL in question, because it’s being rendered by WordPress. You click the preview page button or whatever it may be, and it takes you to the correct place. In this case, there’s no connection between what the URL will be and where you currently are, so yeah, that’s fascinating.

\n\n\n\n

Just as a bit of an aside. We haven’t got into this, but I think it would be a good topic to discuss for a couple of minutes. If WordPress is separated from the presentation layer, this sort of headless notion. How often does the website get regenerated, if you know what I mean? So for example, if we click publish in our headless WordPress website, what is typical there? Are you going to generate the page immediately and store it as static html? Or do some clients have different expectations there? You know, for example, if you are a, a site which needs to publish things regularly, perhaps you need that capability.

\n\n\n\n

I click publish. I want that page to be live within a matter of moments. Or it may be that you’ve got a website where it doesn’t really matter if the pages are not built, I don’t know, three hours, six hours a day, whatever it may be. Do different clients have different expectations there?

\n\n\n\n

[00:34:56] Lax Mariappan: Yeah, that depends on how the publication frequency is. If you want to publish immediately, we can do. If you are okay with publishing the changes after two, three hours, still we can do. So it’s about how you want to set, how you want to build the things.

\n\n\n\n

So here, few things to consider. You can go with static, fully static website. That’s just static and only when a page gets updated. So for example, you have a hundred page. All of them are static and those pages will not be regenerated. So if you change just the about page and only that 99 pages will remain the same. Only that about page will get regenerated again. You can go that route.

\n\n\n\n

And also you can go with, every time in the page gets rendered, you can go server side rendering. So every time that’s new, so you can go that route as well. So that depends on how you want to render the data and everything has pros and cons. The normal way is like how Next.Js does now. Because it is like, keep everything static and if you want to render something, you can still regenerate the specific page.

\n\n\n\n

So this way it’s like you don’t have to build everything all the time. So you can build what has changed in the WordPress. You can see that in the headless frontend. And also you don’t have to wait for it. So, for example, if I go make some change and click update and you can see that immediately.

\n\n\n\n

[00:36:21] Nathan Wrigley: Really interesting, because there is no exact way of doing this is there? You can just build it in whichever way you think is most beneficial, or whatever the client needs. You know, if, if it’s a newspaper website where, really I need to click publish, and within a few moments I need that page to be live because the content that we’re creating is tremendously important to be fresh and new and so on. But it may be that, yeah, you don’t have that expectation and you’re quite happy to have it work in a different way and publish on a, a much less frequent basis. I can’t really imagine a scenario where anybody would say no, I’d rather it was published less frequently, but maybe there are scenarios where that’s beneficial. I don’t know.

\n\n\n\n

Okay, and the last point that you wanted to talk about was, the whole conversation has proven to be really interesting, but it’s pretty clear that there’s a lot more work involved in this kind of website. And so your first point was about the fact that the dependencies, lots of dependencies. Your second point that was that you don’t always get to see what you see is what you get in operation. And the third one is basically the amount of time it takes, the amount of resources it takes. You’ve described this as headless asks for more. Tell us about that.

\n\n\n\n

[00:37:34] Lax Mariappan: Yeah, so when it comes to creating a normal WordPress, like a standard WordPress theme. So what you do is like, you start with your prototyping tool. Like it can be Figma, Adobe XD or anything. So you have your design ready, right? You are creating mock-ups, discuss with the client, and then create a mock-up and then find the variations, all that stuff. And you are settling in, hey, this is my design. And now I’m going to create the theme.

\n\n\n\n

So, I want to create this many templates. I want to create this many menus, all that stuff. When it comes to traditional stuff, it’s like, you don’t have to consider too many things. So it’s kind of straightforward process and like designers and developers can, the engineers can work hand in hand. And it’s, you can follow Agile like, build stuff, reiterate and just deliver it.

\n\n\n\n

So that’s how that works. But when it comes to headless, so you have to consider a lot of things. I would say the first thing is the knowledge or, you know, expertise. With WebDev Studios, we are, I would say kind of one of pioneers and also experts in WordPress plus headless stuff. So we have launched, it’s a open source like we have Next.js starter template. So if you want to try out Next.js a headless frontend for your WebPress site, you can just take a look at WDS Next.js starter. It’s free and it’s in GitHub, so you can just start using it.

\n\n\n\n

So, expertise comes one, like whether you should be, have sound knowledge in that. So you can go and fix stuff. You know what you are doing and you know what to expect and all that stuff. But this requires something like, for example, I am a backend engineer. I have limited React knowledge. I’m now catching up with React, Next.js, all that stuff. But I would, I would not say I’m an expert at it. I build stuff, I still use Next.js every day, but it’s like, I won’t say I’m an expert at it.

\n\n\n\n

So expertise is one. So your team should have sound knowledge in the framework or anything that you do. Or even if you don’t have sound knowledge, let’s say if you are doing something like, something very new, like Remix got released only one or two years ago, right?

\n\n\n\n

So if you want to go use Remix, You should be an expert in React and you should play around with React. So that’s the time. So my point is like time, it asks for expertise and it asks for time. So when it comes to just normal WordPress theme, probably you might finish the theme, let’s say, in a few weeks, or at least a few days even sometimes. With page builders finish it in few days or few weeks, right?

\n\n\n\n

But maybe if you are building it from scratch and you are doing a lot of customization, it may take a while. But when it comes to headless, may take even longer. So more expertise, more time, and all this adds up to more budget.

\n\n\n\n

This may sound like, oh, well should I do all this stuff? It’s kind of worth it. So you don’t have to, for example, if you have your, the front end components ready you may be having your storybook, like where you want to see how the button should look like, how the elements, how the panels are. Let’s say how each component will look like and how they render, all that stuff, right? So when you have all these parts ready, you can go from, for example, today I’m using Next.js, sooner I can move to something else, like I can use Remix. Or I can use something else that’s going to be hot in the market in future.

\n\n\n\n

But when it comes to the typical WordPress, you are going to change everything from scratch. So if you want to add a new theme, so maybe if you want to change the look and feel, that’s different. So everything has pros and cons, but the short answer is the headless CMS ask for more.

\n\n\n\n

[00:41:13] Nathan Wrigley: Yeah. It does sound like not only do you need more time to develop all of this for the reasons you’ve just described. It’s more complicated, so it takes more time. There’s more moving parts, shall we say. And it may also be that you need to spend some of that time not just building the thing, but learning how all of this hangs together, because there’s an awful lot going on in the backend here. And if you don’t have expertise in that, presumably things could go pretty wrong.

\n\n\n\n

With that just before we end. You’ve obviously decided at WebDevStudios that this is an approach. I don’t know if you build the majority of your sites in this way or subset or a proportion of them, not sure. But, typically what is the amount of time longer it would take to get a website out? Let’s say, for example, that if you were just going to use WordPress as is a normal WordPress website, and you built an exact same website, but did it headless. And let’s imagine a site with, I don’t know, several different custom post types.

\n\n\n\n

It’s got hundreds of pages. I’m just kind of making up something off the top of my head. But typically, you know, does it take twice as long, three times as long, 50% longer? What, what are we looking at?

\n\n\n\n

[00:42:28] Lax Mariappan: I’m going to answer just like other engineers do. It depends. But it’s like, I would say it takes a long, maybe you can say, maybe you can say double, but it should not take more than double or something. So that’s where I would say start with more of research. So you should not change frameworks or libraries in between. Like once you started as React, go with React. And if your team is, they are very comfortable and they’re knowledgeable in React, use that. If you are going to use Vue.js or Astro or any other framework. When you start with something and you can go with it.

\n\n\n\n

So, it is a matter of discovering what the client needs and where the goals meet. How we can achieve it. And once we are very clear on that, you can start developing. And during the development phase itself, we can see what are the possible, you know, the bottlenecks or what causes the issue, what could be a problem, and we can figure out other different approaches and solutions.

\n\n\n\n

So, for example, you don’t have to let’s say, PayPal is not the only payment provider right now, right? The payment gateway. So we are using so many different stuff and they do the payment integration quickly. But before those days, let’s say 10, 15 years ago that case was different, so now we have more options.

\n\n\n\n

So similarly, you don’t have to create a form and you don’t have to wait for someone to, the third party or some other open source in a package or something to be ready. So either you can build something on your own if you have time and budget, or you can fork something and then you can adjust to it.

\n\n\n\n

Or the other way is, I would say you can go with some existing third party or SaaS or any other solution, which is just already there and you can see how you can use it with WordPress. So these are the stuff that can reduce your development time.

\n\n\n\n

So when you say if you are, I don’t know exact hours or something, let’s say a thousand hours. So if you say a thousand hours for a normal WebPress installation, so headless may take a little longer, 1,500 or 2000 or anything. But it depends on what the client wants and what framework you choose and your expertise, like, I mean, the whole team’s expertise. And also how well we plan, organize, and go.

\n\n\n\n

So sometimes it’s like just the client takes so long to respond, or sometimes it’s just like, even the client is clueless or what’s happening. So that adds up to some stuff. And I would like to also highlight, when you hear all this stuff, somebody listening is, they will be scratching their head like, so headless is yay or nay.

\n\n\n\n

So, recently, I cannot say the client name and stuff, but I would say, how we figured this out and how it is kind of helpful. So we had to publish more than 20 websites. That’s for a single client. And all of them are different, and all of them are headless, but that’s for a single parent company.

\n\n\n\n

So what happened is, we had the architecture ready, right? So we, we know what happens when you publish. We have everything ready. I mean, the back end and the front end ready. So things become more easier that way. The development time is actually just for one site and then other sites, it’s just like, it was fast.

\n\n\n\n

But we had enough configuration and enough options we given to the client. So every site is not going to look exactly the same. They have their own customizations. But still it’s like amount of development time is the same or is actually less when you compare to traditional. But it depends. It depends on what’s the use case? How, what you are trying to build and everything.

\n\n\n\n

[00:45:52] Nathan Wrigley: Yeah, it really does sound, there were so many good perspectives at the beginning where, you mentioned performance and so on where this is definitely going to be worth it. I guess if the client is willing and the budget is available and the expertise is there, then this sounds like an incredible option. Steep learning curve probably, but a lot of benefits on the backside of that.

\n\n\n\n

Lax, just before we round it up, if somebody has been thinking about playing with headless and they’ve listened to this and they think, okay, I’d like to take that a bit further. Couple of things, firstly, where can they get in touch with you? But also have you got any guidance about resources that they may find useful?

\n\n\n\n

So that could be a website or a book or whatever it may be. So let’s start off with resources and then we’ll turn to you to finish it off. So what resources do you recommend to learn about headless in general?

\n\n\n\n

[00:46:49] Lax Mariappan: In general it’s like you can start with WP Engine has their own blog. They have stuff about headless WordPress and they also have some of packages and stuff they maintain. They have Atlas. It’s a platform they are planning to go full fledged on headless stuff. And also you can read about GraphQL, WP GraphQL. Their team is more active and they share a ton of stuff on how to customize and maintain stuff with headless.

\n\n\n\n

And also you can, like a shameless plug. So I’d also highlight about our WebDevStudios blog. So you can see a lot of headless related articles, tips, and tricks. If you want to play around like, you know, you don’t have to spend something to test it out. So you can go with a lot of free starter templates.

\n\n\n\n

So we have, WDS has like WebDevStudios has a starter template. We have Next.js starter. So that’s a headless thing. All you need is your WordPress, and then you can install that on a locally in your laptop or machine, and then you can just test it out, how it looks, compare the performance and everything.

\n\n\n\n

And also, like other developers and writers have their own stuff. Like Colby Fayock is a popular WordPress developer. He has his own Next.js starter. So you can just simply Google WordPress headless starter, and you can find a lot of starter templates. If you are a developer, go this route or if you are a, you know, site owner or you are just hobbyist, you want to just try or understand a little bit more?

\n\n\n\n

You can still do that reading the resources, right? You can actually check our blog as well. WebDevStudios blog. We have, I would say a couple of headless related stuff. That’s one of the popular article last year. Why headless WordPress is trending. So you can see why it is trending, what to expect. You can read more details in that blog.

\n\n\n\n

[00:48:40] Nathan Wrigley: Thank you very much. And then finally, just to finish this off. Where could people get in touch with you? Are you available on social media? Maybe an email address? Whatever you’re comfortable with sharing.

\n\n\n\n

[00:48:50] Lax Mariappan: Sure. You can find me on, you know, Lax Mariappan. I’m on all the social media like Twitter, Instagram, Facebook, and everywhere you can find me. So you can reach out to me as an email as well, laxman.0903@gmail.com. Anywhere like GitHub everywhere is the same. Luckily I got my name on all the social media, so you can find it.

\n\n\n\n

[00:49:10] Nathan Wrigley: Lax Mariappan, thank you so much for chatting to me today. I really appreciate.

\n\n\n\n

[00:49:16] Lax Mariappan: Thanks Nathan. It’s been great. So I’ve been listening to WP Tavern Podcast for a while. Especially, I like to catch up with what’s going on. The new stuff with WordPress. So it’s good to be on the show,

\n\n\n\n

[00:49:28] Nathan Wrigley: Well, you are most welcome. It’s been a really interesting and informative episode. Cheers.

\n\n\n\n

[00:49:34] Lax Mariappan: Cheers. Thank you.

\n
\n\n\n\n

On the podcast today, we have Lax Mariappan.

\n\n\n\n

Lax is a web developer based in the Philippines. He’s an Open Source enthusiast, and lover of all things WordPress. Lax has been tinkering with websites since high school, but it all changed when he discovered WordPress in 2010. Lax currently works as a Backend Engineer at WebDevStudios.

\n\n\n\n

We talk today about Headless WordPress, and it’s a complex topic. Headless is the concept of decoupling the WordPress admin from the frontend of the site. WordPress will continue to work as expected, but the presentation layer will be done by a different technology. React, Gatsby and Remix being some popular choices.

\n\n\n\n

This implementation of WordPress is complex, requiring technical knowledge above and beyond that needed for a more typical WordPress install, but it has its benefits.

\n\n\n\n

Lax talks through all of this in great detail. How keeping on top of all the additional dependencies Headless WordPress requires can be time consuming. How it can create difficulties for content editors who don’t always get to see what their work will actually look like in real time. Why this approach to WordPress can take more time and resources during the build.

\n\n\n\n

Lax explains how these problems typically crop up, and how it’s possible to plan ahead and build in solutions for all the problems that you might encounter.

\n\n\n\n

If you’ve ever thought about going Headless with WordPress, then the podcast today is for you.

\n\n\n\n

Useful links.

\n\n\n\n

React Library

\n\n\n\n

Gatsby

\n\n\n\n

Remix

\n\n\n\n

WebDevStudio Next.js WordPress Starter

\n\n\n\n

GraphQL

\n\n\n\n

WPGraphQL

\n\n\n\n

WebDevStudio Blog

\n\n\n\n

Colby Fayock’s website

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 15:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Nathan Wrigley\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:39;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"Do The Woo Community: 95% of Websites Have an Issue with Color Contrast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74180\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://dothewoo.io/color-contrast/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:379:\"

Even just by getting your color contrast right, which is very easy, anyone can do it. You just use a contrast checker.

\n

>> The post 95% of Websites Have an Issue with Color Contrast appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 10:43:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:40;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: WordPress Performance Team Working Towards Unbundling Performance Lab Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140668\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"https://wptavern.com/wordpress-performance-team-working-towards-unbundling-performance-lab-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4476:\"

WordPress’ Performance Team met this week with the express purpose of responding to Matt Mullenweg’s recent request to stop adding functionality to the Performance Lab plugin which could otherwise work as a standalone plugin.

\n\n\n\n

At the end of December 2022, the Performance Team published instructions for how to test the new SQLite implementation, which was bundled into the Performance Lab plugin as a module. Mullenweg commented on the post, indicating he saw the SQLite functionality as better suited to becoming a standalone community plugin:

\n\n\n\n
\n

Can we please make this its own community plugin, hopefully to become a canonical one, and stop putting additional things like this into Performance Lab — it feels like we’re stuffing things into PL unnecessarily.

\n\n\n\n

In mid-October I have requested that we stop this unnecessary bundling before with @tweetythierry around WebP, which was put into Performance Lab, so it is disappointing that another large function like SQLite was bundled into Performance Lab plugin.

\n
\n\n\n\n

In an effort to galvanize a base of testers for upcoming performance features, the Performance Team has leaned towards bundling new performance-related functionality into the plugin. Although they are already developed as self-contained modules so they can be easily extracted as individual plugins, the concern is that their visibility would be greatly reduced. The Performance Lab plugin has more than 30,000 active installs. Any standalone plugin would take time to build up to a user base, whereas functionality added to Performance Lab has an instant audience.

\n\n\n\n

“Agreed that there are definitely valid use cases for stand alone plugins, remaining mindful of some of the advantages of a single hub plugin such as development/maintenance, adoption, promotion, developer onboarding/contribution etc. which the Performance Lab facilitates well today as a central performance focus community hub plugin,” Performance Team contributor Thierry Muller said in response to the unbundling request.

\n\n\n\n

Muller outlined three different options contributors discussed in this week’s Performance Team meeting:

\n\n\n\n
\n
    \n
  • Option 1: Keep PL as is, but additionally deploy modules as individual plugins
  • \n\n\n\n
  • Option 2: Make PL a “wrapper” focused on central infrastructure and recommendation of individual plugins
  • \n\n\n\n
  • Option 3: Deprecate PL completely in favor of individual plugins
  • \n
\n
\n\n\n\n

Option 3 seems to be the least attractive to those who participated in this week’s discussion, as it introduces more hurdles for discoverability. Performance Team contributor Felix Arntz noted that one benefit of option 1 is the plugin would continue to work as-is for the 30K people who currently have it installed and that option 2 “would require a complex migration that users likely would not understand.”

\n\n\n\n

WordPress developer Jonny Harris suggested that having each functionality in its own plugin helps with testing but also asked what defines a module.

\n\n\n\n

“Would the current Site Health checks all be together, for example?” Harris asked. “SQLite and WebP are clearly their own modules, but what about smaller things?”

\n\n\n\n

Arntz suggested contributors continue the discussion regarding the scope of how the current modules could be distributed as plugins. He suggested every module could become its own plugin where some modules become standalone plugins and others would be grouped together into a few “topic specific” plugins.

\n\n\n\n

Contributors are discussing the different approaches in more detail on a GitHub issue and will be voting on the best approach. The vote will be open until Friday, January 20, 2023.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 03:34:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:41;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:49:\"HeroPress: Why small can be just the right choice\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=5014\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:150:\"https://heropress.com/essays/why-small-can-be-just-the-right-choice/#utm_source=rss&utm_medium=rss&utm_campaign=why-small-can-be-just-the-right-choice\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7334:\"\"Pull\nHere is Ellen reading her own story aloud.\n\n\n\n

I feel honoured to write an essay for HeroPress. While thinking about what I should write about, I wanted to make sure it will be helpful to others.

\n\n\n\n

Of course, everyone’s goals are different. My partner Manuel and I started to create WordPress products, because we saw the opportunity to build a small business and keep it a business we both felt comfortable to work in over the years. And that’s what we did. We love to travel and searched for a way to live the nomad lifestyle long before the term was even a thing. We travelled and worked on our blog and themes. And don’t get me wrong, it was not easy in the beginning. We had to build an audience first, so we wrote blog posts about everything we learned while keeping financially afloat with small client projects. We put endless hours of work into our blog, before even dreaming of one day earning income just with our themes. But we loved every minute of it.

\n\n\n\n
\n

We worked from Thailand, Sri Lanka and New Zealand, and we felt creative and free.

\n
\n\n\n\n

We went to WordCamps and creative conferences along the way and met so many new people with similar values and goals.

\n\n\n\n

Having these experiences formed our way of thinking about the way we wanted to work moving forward. The benefits of being a small team of two seemed so obvious to us. We could make decisions fast and react to new trends without asking anyone for permission. As long as we built something others liked, we would always be ok. So that’s what we focused on. We built one theme after the other and loved the creative freedom this work gave us. The positive feedback and listening to the stories our customers shared on how our themes helped them reach their goals kept us going.

\n\n\n\n

Living abroad

\n\n\n\n

As we could work remotely from any location. We didn’t need an office or a local team. Keeping our business so flexible allowed us to move from Germany to New Zealand in 2015. After about two years working towards it, we were able to apply for a business visa and eventually for permanent residency four years later. Living away from family is never easy, but the opportunity to live in another country surely teaches us so many valuable lessons we would never want to miss. It’s a true gift, all made possible by our small WordPress business.

\n\n\n\n

Reacting to changes

\n\n\n\n

Fast-forward to 2018 and the WordCamp Tokyo, where we first got the chance to dig deeper into the Gutenberg project during contributor day. We knew changes were coming, and we needed to react with our business. Even before, we felt that building one theme after the other felt a bit tiresome and not like the most effective way for WordPress users to build their site design. We were never convinced by the page builder solutions, as it just seemed too bloated and untrue to WordPress core to bring a wow effect to us. We love to keep things flexible and minimal, and adding an entire framework on top of WordPress never felt like a great idea to us.

\n\n\n\n
\n

So here comes this Gutenberg thing, a promise to a more flexible, component based way of creating designs for WordPress.

\n
\n\n\n\n

We felt like this is meant to be for us. So once home from the WordCamp we started to build blocks and explore how this new WordPress would work. We did not realize back then how big these changes would become and how much it would impact our work and our business.

\n\n\n\n

But it felt good to build something new and to try to find a better solution to offer for our theme customers. We struggled to gain footage for quite some time, as there were just so many new technical things to figure out and so much was unclear. But we still never doubted that we are on the right track, as with every new release the opportunities seem to get better and more stable.

\n\n\n\n

And just now we are just about to relaunch our business websites with a brand-new block theme that is solely built with our blocks, WooCommerce blocks and WordPress core blocks. It finally feels like all the work comes together and themes and the Gutenberg project are ready to be merged into one and released for production.

\n\n\n\n

Opportunity to pivot

\n\n\n\n

During all these changes, we had the time to think about the future of our WordPress business and what we want our road ahead to look like. Many others around us have sold their independent businesses or took a job at one of the big WordPress businesses. I feel like it’s also a natural path of WordPress and all of us growing up.

\n\n\n\n
\n

For us, we feel like we are just getting started again, finally having found a way to have fun creating for WordPress again.

\n
\n\n\n\n

Building one of our last classic themes, we felt like we had lost the fun in designing for WordPress. We felt like themes were stuck, being either too inflexible and or way too bloated to be any good. It felt like we were trying to build, squeeze out a solution into a product that technically was never meant to be this way.

\n\n\n\n

Block themes, the site editor, patterns, and blocks come as a chance for us to do it better. It’s a big shift and a difficult project to pull off, for sure. WordPress is used by so many people in so many ways. But block themes make WordPress lighter, and they don’t stand in the way of other add-ons as much as classic themes felt they were. It’s amazing how we can take all the components apart and mix and match them together. There are still missing pieces, but we are getting there.

\n\n\n\n

For us, we are taking this shift that we are sort of making together with WordPress, as an opportunity to make things better. We always felt like we wanted to offer more support and help to our customers. But we never found the time. So with our upcoming relaunch, we are taking the chance to change that. We will offer new services and are exploring more ways to offer our customers what they actually need. It feels like a breath of fresh air to us, and we haven’t had so much fun with WordPress in a long time.

\n\n\n\n
\n

It’s funny, who would have thought that a piece of software can impact your life in such a big way.

\n
\n\n\n\n

WordPress has impacted where we live, who our friends are and which destinations we like to visit. We feel more open-minded because of WordPress, we believe in the power of open source projects and we believe that a group of people from all over the world can build something meaningful together.

\n

The post Why small can be just the right choice appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 01:15:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Ellen Bauer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:42;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WordPress.org blog: WordPress is Turning 20: Let’s Celebrate!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14155\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"https://wordpress.org/news/2023/01/wordpress-is-turning-20-lets-celebrate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1474:\"

2023 marks the 20th year of WordPress. Where would we all be without WordPress? Just think of that! While many technologies, software stacks, and fashion trends have come and gone throughout the past two decades, WordPress has thrived. This is due to the fantastic work and contributions of the WordPress community, comprised of thousands of contributors; and millions of users who have embraced the four freedoms of WordPress and the mission to democratize publishing.

\n\n\n\n

Let’s celebrate!

\n\n\n\n

Throughout the beginning of 2023, leading up to the official anniversary date of WordPress’s launch (May 27, 2003), a number of different events will celebrate this important milestone, reflect on the journey, and look toward the future.

\n\n\n\n

Please join in!

\n\n\n\n

Over the next few months, be sure to check WordPress’s official social media accounts along with the official anniversary website for updates on how you can be involved in this exciting celebration by contributing content, collecting cool anniversary swag, and much more. 

\n\n\n\n

Use the hashtag #WP20 on social media so the community can follow along.

\n\n\n\n

If you have something planned to celebrate that you would like to be considered for inclusion on the official website, please use this form to share the details.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 21:38:49 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Dan Soschin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:43;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:88:\"Do The Woo Community: WooCommerce, Payments and Crypto with Keala Gaines and Dave Lockie\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74249\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:52:\"https://dothewoo.io/woocommerce-payments-and-crypto/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:416:\"

Keala Gaines from WooCommerce and Dave Lockie from Automattic chat about the relationship between WooCommerce and Crypto.

\n

>> The post WooCommerce, Payments and Crypto with Keala Gaines and Dave Lockie appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 10:11:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:44;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:85:\"WPTavern: Gutenberg Times to Host Webinar on How to Use New WordPress Layout Features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140874\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:96:\"https://wptavern.com/gutenberg-times-to-host-webinar-on-how-to-use-new-wordpress-layout-features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4790:\"

Gutenberg Times will be hosting a live Q&A webinar titled “Layout, Layout, Layout” on January 11, 2023, at 05:00 PM in Eastern Time (US and Canada) via Zoom. This event is open to WordPress users of all experience levels who are interested to learn more about how to use WordPress’ layout features when building sites with blocks.

\n\n\n\n

Host Birgit Pauli-Haack will be joined by WordPress veterans Isabel Brison, Andrew Serong, and Justin Tadlock. Brison will be demonstrating different layout scenarios during the presentation, and attendees will be able to participate with questions.

\n\n\n\n

Any user who has attempted to layout a design in WordPress has likely tried out container blocks that offer layout settings. These blocks include Columns, the Cover block, and the generic Group block.

\n\n\n\n

The event will cover how to manipulate layouts by defining the width of post content, arranging blocks horizontally or vertically, right or left aligned, and inside container blocks.

\n\n\n\n

“In terms of block styling, Layout is a complex feature because it affects child blocks in ways that go beyond CSS inheritance,” Pauli-Haack said.

\n\n\n\n

WordPress 6.1 introduced more layout controls and flexibility in the block editor, but Pauli-Haack said the dev note on updated layout support was written more for developers.

\n\n\n\n

“Feedback from users through the FSE program and other connections revealed that handling the layout settings for container blocks is not particularly intuitive and takes some trial and error to find the right combination,” she said. “The Live Q & A will bring a better understanding to users and #nocode site builders.”

\n\n\n\n

When Pauli-Haack started the Live Q & A’s in 2018, she routinely brought in guests who were building the block editor, with the intention of having users meet them and discuss features like full-site editing, block themes, case studies, and discuss challenges.

\n\n\n\n

“Since then, quite a few initiatives of the official WordPress project have come to life,” she said. “There is the highly successful Full Site Editing outreach program, spearheaded by Anne McCarthy, who now holds regular Hallway Hangouts with community members and contributors.”

\n\n\n\n

People are also learning the ins and outs of site editing through the efforts of the training team, which began creating courses and lesson plans and hosting workshops on Meetup.com in 2021. These are also recorded and uploaded to WordPress.tv and YouTube. WordPress.org also launched a blog for developers in November 2022. With all these new learning opportunities, Pauli-Haack is changing the focus for her live events.

\n\n\n\n

“For the Gutenberg Times Live Q & As, I am now looking at topics and discussions about more complex concepts, more case studies, and technology on the cutting edge,” she said. Most recently, the show featured the developers and digital strategies of the Pew Research Center, a high profile site that was built with a block-first approach.

\n\n\n\n

“We are also in planning phase to hold a Live Q & A with the developers of GiveWP who are using Gutenberg as a framework to build the next generation of their popular donations plugin with the components and scripts that Gutenberg uses, but outside the post or site editor,” Pauli-Haack said.

\n\n\n\n

She also has another Live Q & A planned with the WordPress VIP design team that works on design systems for companies that need a streamlined way to stay within their design standards. Pauli-Haack intends to talk with them about a plugin they created that lets designers automatically create a website’s theme.json file with all the styling pulled directly from Figma designs.

\n\n\n\n

The upcoming Layouts webinar is free but attendees need to register to get the zoom link. An archive of all the past Live Q & A events is available on the Gutenberg Times website. The best way to stay informed about future events is to subscribe to Gutenberg Times’ Weekend Edition, as subscribers get an early invitation for the next Live Q & A’s.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 03:37:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:45;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:23:\"Matt: State of the Word\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:22:\"https://ma.tt/?p=75018\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:42:\"https://ma.tt/2023/01/state-of-the-word-2/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:365:\"

A few weeks ago, but what feels like a lifetime ago, I was in New York City with a few dozen extra special people from around the WordPress world. Alongside Josepha and the community we presented this review of how WordPress did in 2022, and vision for what’s coming:

\n\n\n\n
\n\n
\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 09 Jan 2023 23:25:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Matt\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:46;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:85:\"Post Status: Support Inclusion in Tech with Winstina Hughes — Post Status Draft 136\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146189\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"https://poststatus.com/support-inclusion-in-tech-with-winstina-hughes-post-status-draft-136/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:52731:\"

In this episode, Winstina Hughes joins Cory Miller to talk about the  Support Inclusion in Tech project created to champion diversity, equity, and inclusion in the WordPress community by providing assistance to WordCamp speakers for travel and hotels.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

Winstina Hughes  is a long-term community member and organizer within WordPress. She joins Cory Miller to discuss Support Inclusion in Tech, an effort to increase representation of minority and underrepresented speakers at WordCamp by providing needed financial support. This offers everyone in the WordPress community the chance to share their expertise and contribute resources so everyone has the opportunity to engage.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • Creativity is our common bond. WordPress is the playground where we all came to tinker and build for fun or business. It is the software magic where we discover what new things we can do each day, how to make ideas become reality, and how we might leverage what we learn to create a better world.
  • \n\n\n\n
  • Ripple effect of inclusion: When you provide the ability for a large group of people to participate, travel, and network, the impact extends beyond the WordPress community to create the bigger changes we want to see in the world. This is our community magic.
  • \n\n\n\n
  • Fifth Freedom in WordPress: We are all familiar with The Four Freedoms of WordPress. This is the 5th – full participation. Removing the financial barrier will bring us closer to the reality of being a truly inclusive community.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n

\"🙏\" Episode Partner: Gravity Forms

\n\n\n\n

Gravity Forms is a powerful form builder for WordPress and the #1 choice for businesses and web professionals across the globe. Its vast array of features, intuitive drag-and-drop form editor, and extensive ecosystem of add-ons, ensure customers can design beautiful, intelligent, and accessible forms for any project requirement.

\n
\n\n\n\n\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

Welcome to back to Post Status draft. I\'m with a good friend of mine when Winstina hughes. I met with Winstina a couple years ago in the post status community. We\'ve got to meet in person, talk numerous times, and, um, I\'m excited about what we\'re gonna be talking about here. Um, she\'s got a new, a project called support inclusion in tech.com and we\'re gonna dive into that today.

\n\n\n\n

But, uh, hi, Winstina. Hi. And pumped to finally have you on,

\n\n\n\n

Winstina Hughes: I\'m excited to be with you.

\n\n\n\n

Cory Miller: Could you tell us what you do in WordPress?

\n\n\n\n

Winstina Hughes: Okay. Um, What do I do in WordPress? every time I speak with, you know, every time I have one of these, um, you know, opportunities to speak with someone in the community, I end up like re repeating the question.

\n\n\n\n

Um, cuz it really helps me. I am a community member, um, and I\'m also, you know, a, an organizer, um, a meetup organizer and a board camp organizer. I started, um, going to [00:01:00] meetups in New York City and I transitioned into, Speaking, um, at Word Camp, New York City, and then I was invited to become a meetup organizer.

\n\n\n\n

And so, um, my, you know, my participation in the community was, um, you know, like in the early, um, you know, 2010s. And then around 2015, 2016, um, I started, you know, speaking at, at New York City, and then I became an organizer. I meet up organizer. In 2018, I led my first word camp and my only word, camp , hundred twenties, um, a budget of 120,000, a team of 18.

\n\n\n\n

Uh, it was an amazing experience. They were wonderful people and it was. Really tiring .

\n\n\n\n

Cory Miller: Yeah. You know, over the years, Winstina, I\'ve had so many dear friends that have been Word Camp organizers and really I go, oh my God, I love you so much because of what you\'re doing for the community. But I also go, I hope you [00:02:00] still like word this afterwards because it\'s a such a labor of love that I think, um, so often we don\'t really give the credit and thanks to the people, That do this voluntarily.

\n\n\n\n

Yeah, like you\'re talking about all the stuff you\'re done. So anyway, I wanna say thank you because I\'ve said it so many times to dear friends over the years going, thank you for what you\'re doing. I\'ve always shied away from it because it\'s so much work and I see all the passion and energy that you and other organizers have and I\'m really thankful cuz I think that is so critical to the entire community to have these, and now we\'re talking in 2022.

\n\n\n\n

But we hear WordCamps are back. You and I got to see each other in San Diego at Word Camp US. Yes, yes, yes. So

\n\n\n\n

Winstina Hughes: anyway, so Word, word camp Us. I was a co-organizer for Word Camp US this year. Um, and so yeah, you\'re right. Like we had a chance to teach other again there, and that was like, yay. That was awesome.

\n\n\n\n

Cory Miller: It was, yeah.[00:03:00]

\n\n\n\n

Yeah. A absolutely. Well, okay, so what drew you to, okay, how did you start with WordPress? Were you using WordPress for, uh, your own website, somebody else\'s website? How\'d you get started with the actual software?

\n\n\n\n

Winstina Hughes: So I started with WordPress in 2006, 2007. Um, I had a college course that was . Yeah, I, I had a college course.

\n\n\n\n

Um, and our professor required us to add, um, you know, the work that we\'d done, uh, into a wordpress.com blog. Um, it was a geographic information systems class. And, uh, we were looking at public health data at the census block level. Um, and so we were actually, you know, looking to see. You know, where, um, there were instances of like, um, I don\'t wanna say disease, but you know, like different illnesses.

\n\n\n\n

And so what what\'s really interesting is that you can, that schools get access to that data and you can actually like, You can [00:04:00] essentially imagine, and I don\'t wanna go too far deep into it, but imagine you have like, you know, Google Maps, right? And like when you have Google Maps open, you can do street view.

\n\n\n\n

So Google Maps lets you like go from that whole, um, like that map into like street view where you jump in as a person. So, uh, this data essentially took you away from just the geographic element, um, and the typography and like really. The census, you know, track level, like essentially, um, you know, looking at neighborhoods and, you know, the instances of disease in those neighborhoods.

\n\n\n\n

And so he, you know, he gave that to us as our final assignment. Um, you know, we did some like, uh, some heat mapping to show where there were greater concentrations of a particular type of illness, , right. Um, or, um, you know, disease or, you know, Uh, I\'m not exactly sure like what, what we [00:05:00] were calling it, but that\'s what our assignment was.

\n\n\n\n

And, uh, he asked us to, you know, take like a picture of the map and to post it in wordpress.com and that\'s how it all started with that , with that assignment. Um, so we were you.

\n\n\n\n

Cory Miller: We were using WordPress at the same time. That\'s the same year I started with WordPress when you started. I did not know you went that, that far back with WordPress.

\n\n\n\n

So I love that. Uh, yeah, I do. Thank you. And then you said like in 2010 you started actually, uh, getting involved with community events. And this is relevant to us talking about support, inclusion and tech. So what drew you to start participating in volunteering and contributing to WordPress?

\n\n\n\n

Winstina Hughes: So I went to New York City Meetups, um, and, uh, WordPress, New York City, uh, is the one that\'s in closest proximity to where I lived.

\n\n\n\n

I could just take the train in. Um, and it was, it was great. Like I, I really felt, um, that the community there was, was [00:06:00] open, like the organizers were open and, and they were welcoming. Um, Dana, rendy, uh, those were organizers at the time, Steve Bruner, who was an organizer. Was he is the organizer, , he started it and he\'s, he\'s kept it, you know, um, really like strong, like, since its inception.

\n\n\n\n

Um, and so like just going to these events and meeting these, these like wonderful generous people, these kind people, um, you know, meeting Kevin Cre, Christiana there as well. Um, and you know, just that environ. Was what led me to continue attending events. Um, and they really encouraged me to submit a talk to speak at New York City, um, ward Camp, New York City.

\n\n\n\n

And I submitted a talk to speak there and, you know, since that time I\'ve been more engaged in. Event organizing component, [00:07:00] um, or part of the community. So it moved beyond just, you know, um, Like learning, you know, to use Word Pro, you know, building sites and breaking them, uh, the best, right? Yeah. Like, that\'s the best way.

\n\n\n\n

That\'s the only way you can really learn. I mean, I, I started, you know, with different hosting plans, I\'ve had like four or five, like I have multiple domains. Like I think when you\'re in our space, you got a chance to really create. And, um, and that\'s what I was able to do and what I\'m able to continue doing, and.

\n\n\n\n

Now moved from just creating and building with WordPress to assisting with supporting, you know, our community through events like meetups and, uh, word camp organizing and supporting inclusion in tech is, is an extension of, um, of this work, this contribution that I\'ve been doing. It, it, it pieces together so many different elements that I\'ve come to, like I\'ve come to see and I\'ve come to understand [00:08:00] and. It\'s, it\'s a solution that I propose to, um, some current challenges that, um, I\'ve heard being expressed. Yeah,

\n\n\n\n

Cory Miller: I, uh, I wanna scroll back for a second and say that when you\'re talking about create, I sometimes it, for as long as you and I have been in WordPress sometimes forget that magic of being able to create something on the web or in the, in the world.

\n\n\n\n

See this cool tool called WordPress, so I appreciate that. I think that\'s what we rally around in the WordPress community and particularly to post status is helping build tools and projects and things on top of this magical thing we call WordPress. So that was a, when you said create, I was like, it\'s just little tingle of magic came up of that\'s, that\'s why we, I think that\'s our common bond in

\n\n\n\n

Winstina Hughes: WordPress.

\n\n\n\n

I agree. I agree. And I think that, uh, when we create as community members, um, and not necessarily [00:09:00] just as. Business owners or, or, um, you know, those who are providing like services. That\'s a component of creating. But, you know, in the middle of doing all that, I think, you know, I mean, I like to sit down and just literally play and see, you know, what could I do with it today?

\n\n\n\n

and, um, I entered a com competition, um, held by Sustainable New Jersey, um, right around the time I completed graduate school. And there were municipalities that were seeking, um, solutions for challenges that they had. And there was the city of East Orange and they wanted like a marketplace, um, and a place for their planning department to, you know, add their documents and also something for their green team.

\n\n\n\n

And when I saw this, I was like, I could use WordPress and e-commerce. So I created like a WooCommerce marketplace for them to sell, you know, for residents that would sell their products and services. And I demoed it. Um, and then I also had a website and also a Buddy press site. Um, and the buddy press site would be for their green team members.

\n\n\n\n

And I think that [00:10:00] like, when, when we create with WordPress, like we\'re able to like see like, you know, These asks and really apply like our knowledge of what we know the c m s can do and then provide a solution. And the city was actually really happy with the solution. Um, and I made it to the finals of the competition.

\n\n\n\n

Um, but there was another, uh, but there were other teams that that won it. Um, but it was, it was really exciting to show what WordPress, you know, software and what WooCommerce can. Uh, that\'s the

\n\n\n\n

Cory Miller: dream. Um, that, that\'s so awesome. Thank you for sharing that backstory. As much as we\'ve talked and stuff, I haven\'t got the chance to ask those questions and, um, it\'s a good reminder for me about, you know, I think if you go long enough in the community, you start to, well, I, I\'ll say I start to.

\n\n\n\n

Forget some of these nuances, [00:11:00] like being able to go, here\'s a project idea, this could be done in WordPress, you know? And that the tools are mostly freely available. Yes. And you can start and build something online.

\n\n\n\n

Winstina Hughes: Exactly. Yeah. You just, you know, download . Yeah. Yeah. Well, yeah.

\n\n\n\n

Cory Miller: So that leads me to support inclusion and tech. And you mentioned you saw a problem or problems and challenges in our community that you wanted to help make some a solution to toward it that became support, inclusion and tech. But can you talk about that a little bit? Cause I know my, my understanding and you continue to help me expand my understanding of all this is it\'s not just one particular country with DEI, it\'s a global thing. But could you talk a little bit about the problems and challenges that you saw in the space.

\n\n\n\n

Okay.

\n\n\n\n

Winstina Hughes: Absolutely. Um, absolutely. So I, you know, really wanna, like, I wanna hold true to like, um, to how [00:12:00] I, um, shared it on my website. Um, but really the backstory is that. There was a conversation that erupted on Twitter, um, about the need for more diversity on Word Camp, um, organizing teams. And this started, uh, due to, um, you know, uh, some, some thoughts that were expressed about Word Camp Europe, uh, where WordCamp Europe\'s organizing team, um, not being.

\n\n\n\n

Very reflective, um, of, you know, more ethnicities or a wider range of them. Um, it was a really difficult conversation that was happening. And my take on it really is that it\'s not where camp you\'re specific, right? Like, I mean, let\'s, you know, let\'s really step back and think about the fact that, you know, there\'s so many ethnicities around the world that have a ch [00:13:00] like it\'s really.

\n\n\n\n

When you\'re in the minority as a group, Really up to the group that\'s in the majority to weave you into those experiences and those opportunities. Um, and when that doesn\'t happen, then you have groups that don\'t have an opportunity to be, to participate and to be involved and, you know, support inclusion and tech.

\n\n\n\n

I mean, considering this was a conversation about word camps and our participation in them. Support, inclusion and tech really seeks to assist us in solving the challenge of, um, not having as much, you know, ethnic or, um, or just diverse representation within the Word camp experience. It doesn\'t seek to, you know, um, it doesn\'t, it doesn\'t seek, you know, to like solve, um, Like these, you know, the world that we [00:14:00] live in.

\n\n\n\n

And it doesn\'t seek to solve like, um, you know, diversity and inclusion outside of the WordPress space. Um, but I believe that in, in providing these, um, these opportunities within our community, since we\'re so large, that the ripple effects can extend well beyond the WordPress community. I believe that when you, Absolut.

\n\n\n\n

When you provide such a large group of people, the ability to, um, to participate in work camps, um, the ability to travel to them, the ability to network to them with, when you attend, um, the ability to like, you know, seek, um, you know, out more relationships, friendships, professional relationships. Then there\'s this ripple that extends outside of our community and I think.

\n\n\n\n

That level of empowerment can extend outside of WordPress and those ripples can assist us in diversity inclusion beyond, [00:15:00] um, you know, our, our involvement in WordPress. But you know, this, this particular solution is intended to solve the challenge that I saw, you know, um, being expressed, you know, within our community.

\n\n\n\n

And so the thought is really, Since, you know, since there\'s a take on it. And there\'s, it\'s a, I mean, it\'s an, it\'s an honest one, right? We don\'t see enough people of color. We don\'t see, um, enough, you know, people of, um, other minority groups, um, you know, uh, from other parts of the world. Um, You know, we are seeing an equal, more equal balance of, um, men and women.

\n\n\n\n

Uh, you know, but when it extends beyond that into like, you know, more representation in terms of like, you know, a wide range of religions, which ties to ethnicity often. Um, and when you\'re looking at representation in terms of those of us who, um, have like neuro [00:16:00] diversion, you know, um, you know, like, uh, characteristics and those of us who, um, you know, who we choose to love, , you know, the what society, um, you know, asks of us , right?

\n\n\n\n

Like, and um, and when we choose to hold true to that or when we\'re dealing with the physical limitations, um, that, you know, that we were born with or when we\'re in minority. Groups, you know, that have a harder time, you know, uh, receiving opportunities, um, to participate and to increase, you know, their reach and even, um, you know, the professional opportunities that are available to them.

\n\n\n\n

You know, like this. What can we do to, um, to really like solve. To solve that. Mm-hmm. And I thought, what could I do within our community Yep. To, you know, to integrate, you know, like all of those of us who are, um, Either, you know, disadvantaged [00:17:00] or not as represented into WordPress programming and support, inclusion and tech, um, seeks to, you know, take away that financial barrier, which I believe is really what, you know, can limit our participation.

\n\n\n\n

We want to participate, we want to speak, but if we can\'t afford to speak , right? I mean, if we can\'t afford to travel to the conference and if we can\'t afford a place to stay at the conference, um, then. Like, why would we even think to apply to speak at the conference? Right? Like,

\n\n\n\n

Cory Miller: yeah. That\'s, that\'s really beautiful, Winston, because, um, there\'s a couple of takeaways I, I got from this.

\n\n\n\n

Number one is, I, I\'ve always believed, um, at least in my world, that WordPress has been. The, an inclusive place, ever growing inclusive community. That\'s like a mirror to my world, the way I want my physical world here in Oklahoma to be. And I have [00:18:00] so much learned from our community leadership, uh, over the years, um, that there\'s a cons.

\n\n\n\n

Consistent push and drive from the entire community and the leadership to be truly diverse, truly inclusive in all those words. And I, I learn a lot from this. Um, so the mirror I, and I do think WordPress is, our community is so powerful cuz we\'re distributed all over the world. So if we make change in our community, in our WordPress, That should be, that should be reflected.

\n\n\n\n

And I think that\'s another, we talked about the software magic. This is the community magic. Exactly. Uh, the other thing is, I, I love and I respect because I try to take too much on that, you said, Hey, here\'s something I\'m passionate about, being an organizer, being at these community events, how special and valuable they are to you and other people instead.

\n\n\n\n

I\'m gonna make this dent first. Yeah. Like, I\'m gonna, I\'m gonna take on this aspect first. You. Beautifully and clearly [00:19:00] shared. This is the thing I\'m trying to take on in this bigger, bigger, um, change that you wanna see. We wanna see in the world. Thank you. Okay, so we\'ve got this now we\'ve got a website. Um, you\'ve got a website up to kind of share this.

\n\n\n\n

Now. Take me through, if you would, I am, pretend for a second you\'re talking to someone that is in an underrepresented, uh, in, in tech. Of, um, situation. Mm-hmm. , how\'s the process to, to get on the, Hey, I want to go to these WordCamps. I want to speak, but I do need some assistance. What does that process look like for, for support inclusion and tech?

\n\n\n\n

Winstina Hughes: So support, inclusion and tech. Um, also weaves into other initiatives in order to, to assist our speakers. Um, and so when you\'re accepted, support inclusion tech, it become, moves into the position to, to assist you once you\'re accepted into [00:20:00] Word Camp. Um, as soon as you get that, you know, acceptance, you know, go to https://supportinclusionintech.com/.

\n\n\n\n

Um, and you\'re simply just, you know, gonna put in the word camp that you were accepted in. And then there are two components, um, in addition that they\'re suggested , right? Like you\'re encouraged to do this. Um, you know, uh, we\'re in the community of consent and so, um, you have, you know, um, You\'re gonna give, you know, the consent to be included in these other initiatives, um, you\'re not gonna be forced into it.

\n\n\n\n

Uh, there\'s underrepresented in tech and there\'s also the WordPress diversity, speaker channel. Um, both of those, uh, are ways of. Further supporting diversity and inclusion and representation within the WordPress space and creating, you know, um, you know, successful opportunities for us to, um, to, you know, to put together great speaker applications and then to also, um, you know, move beyond just submitting [00:21:00] them. Um, but to being accepted.

\n\n\n\n

The, the ask is that, you know, once you\'ve been accepted to camp and you\'re starting the process of, you know, receiving funding through supporting inclusion and tech, that you also participate in those other two initiatives as well. Um, because you know, in the process of doing that, it\'s further supporting the work that we\'re doing in the WordPress community. Exactly as you said, Corey, that, you know, the word WordPress leadership already has been putting in, um, you know, the work to, you know, to assist us in resolving the challenges that face society as a whole. And so there are initiatives that currently exist and those two in particular, I think.

\n\n\n\n

You know, are ways that we can continue to support underrepresented minority groups in the WordPress community. Um, and so in the process of, you know, uh, applying for the funding, uh, you\'re encouraged to, you know, to list yourself on underrepresented tech to join the, um, the, the diversity speaker channel [00:22:00] on make WordPress.

\n\n\n\n

Um, and then once you\'ve just put on, put that information in and you\'ve identified the type of support that you\'re seeking, um, you just like, and it starts from there. Like I start, um, you know, pairing you with, you know, with a partner that you know can, can step in and provide, you know, the funding for you.

\n\n\n\n

And so, you know, they\'re gonna cover your travel and they\'re gonna cover your hotel. Um, and that way in order for you to participate, you\'re not going to be paying anything really that you know, out of pocket. For that participation, um, in that WordCamp. And that\'s really the goal. Um, the goal is to remove the financial barrier to your participation.

\n\n\n\n

Cory Miller: Yeah, that\'s fantastic. By the way, I wanted to sidebar for a second and say underrepresented in tech, uh, by Allie and Michelle Frechette. If, if you\'re listening to this and, uh, you also as a be becoming a member of underrepresented in tech, get a free [00:23:00] uh, professional membership at post status?

\n\n\n\n

Winstina Hughes: Yes. Yes. I started, I and let\'s not also forget too, that like there are other opportunities as well as Post Status has been, um, you know, looking into as ways of increasing, you know, diversity and representation within the Post Status community. Um, so underrepresented tech and that membership, and I know that there\'s some other ways that you\'re working on it too, Corey.

\n\n\n\n

Um, you know, I think, I think when we can pull all our efforts together. We have a stronger community. Um, and you are, you know, you\'re, you\'re offering that and then supporting inclusion and tech, you know, encouraging, you know, speakers to, to register and to participate in those two other programs. Strengthen all our efforts. Yes. Um, and, and that\'s, you know, that\'s the process of it. And so once you\'ve submitted, you know, your. once you submitted that form, you know, just letting me know, like the speaker registration that you\'re seeking, the [00:24:00] support. Um, you\'re also gonna complete the blind directory listing and that blind directory really.

\n\n\n\n

That Blind directory listing has the word camp that you\'ll be speaking at. Um, and it has the type of support that you\'re seeking, whether it\'s just beach travel or hotel, or both, and that\'s it. Um, no one in the community, um, you know, needs to know who you are. They don\'t need to know what your need is. Um, they don\'t need to know where you come from.

\n\n\n\n

And they don\'t need to know what makes you underrepresented and what makes you a diverse speaker. Uh, it\'s simply a way for, um, for companies that are considering sponsoring to see that the need does exist. And it\'s also a way for our community to see that the need does exist, um, and that we do have members that are seeking the support.

\n\n\n\n

Um, that, that blind directory listing is, is just a way, you know, for our community to see that, um, that our need is there. Um, yeah, and it\'s also a way of, um, uh, [00:25:00] keeping everyone up to date on the work that\'s happening.

\n\n\n\n

Cory Miller: So I know we\'ll have two asks. The first ask is if, um, you need assistance, want assistance to go to a WordCamp to be sure to go to supportinclusionintech.com?

\n\n\n\n

Winstina Hughes: Yes. Once you\'ve been accepted, go to supportinclusionintech.com. Complete the form for speaker registration, and you\'ll, um, you\'ll be paired currently, um, with four companies, uh, that, um, that have partnered to work on this.

\n\n\n\n

Cory Miller: That comes to our second ask. Yeah, that\'s right. Okay. So tell me how, um, now this is very relevant for post status because we\'re a bunch of professional and business members in our community.

\n\n\n\n

So the second ask is, we need someone, one, participants, people that need and want assistance go and speak at Word Camps. And the second part of this is the sponsors and partners. Can you tell me a little bit more, more about that and [00:26:00] how that.

\n\n\n\n

Winstina Hughes: Okay, so starting off, partners are sponsors, , um, partners are the first, um, you know, companies that expressed an interest in supporting this project.

\n\n\n\n

Um, you know, this initiative. Uh, and so like that is my way of thanking you, um, by, you know, by acknowledging. that you came into this, um, wholehearted and opened armed. And so thank you to the four companies, um, that have done this, uh, that have stepped forward to say that they will support. Um, you know, it was really exciting once the call went out, um, from Word Camp US that they were seeking, uh, support for underrepresented.

\n\n\n\n

Speakers. It was really exciting because Master wp, um, stepped in at that time, you know, to say that they thought that this was a great project. And, you know, they\'re the fourth company they joined, um, GoDaddy, Post Status, and Yoast, um, you know, the original three that said that, you know, that they would love to support this initiative.

\n\n\n\n

And so, um, now we have, [00:27:00] we have four of them and there are also several companies as well that are providing in-kind donations. Um, and, you know, they\'re doing so, makes it possible for support, inclusion and tech, um, you know, to, to function, right? Because like, you have the website and then there are all these different like plugins that make it functional and make it possible, you know, for, um, for, for it to run and function the way that we need to.

\n\n\n\n

Um, so if your company that wants to. Sponsor speakers, you know, you just have to go to the site. Um, there is a section there for you to register your support, um, your register, your desire to support. It\'ll ask you, um, you know, to provide, you know, like a contact. Um, it\'ll ask you the type of, uh, How you want to provide this support.

\n\n\n\n

Um, would you prefer to reimburse speakers for their expenses or are you, um, ready and, you know, willing and able to pay for their, um, their travel and their hotel in advance of their trip? [00:28:00] Um, so, you know, once you\'ve identified your contact, you know, your contact is the type of support that you want to provide, you know, then, you know, we\'ll have an opportunity, I\'ll have an opportunity, you know, to really. Sit down with you and for us to have a conversation about like, you know what would be your process, you know, what would make it easy or for you to be a part of this initiative? Um, this isn\'t a cookie cutter means of support for, for companies, because you\'re all different.

\n\n\n\n

Um, how GoDaddy, you know, is providing support is different from how Post Status is providing support is different from how Yoss is providing support. And it\'s different from, you know, how, um, Master WP is and, uh, When I started this, and I, you know, I, and I wrote on my blog, like, really this proposal on https://winstinahughes.com/.

\n\n\n\n

I went into it, um, you know, with the understanding, personal understanding is that it\'s gonna [00:29:00] take a couple years to understand the needs of our community and the ways, you know, companies and our ecosystem can support these needs. And in the last six months, Exactly what I, you know, anticipated, um, is what I\'ve been able to, you know, to, to see.

\n\n\n\n

And, you know, currently, um, there have been three, you know, requests, um, for, you know, to participate, you know, um, for funding, for support, for camps and, um, two unique, you know, individuals have, have made those requests. Um, and you know, so right now it\'s a question of. You know, like assisting them, you know, with the process of how, you know, our four partners, you know, can support them in that way.

\n\n\n\n

Um, and I think that answers part of your question. Um, the second part of the question is like, so how is this financial component gonna work? Right? [00:30:00] Like, are companies giving me money? No, you\'re not , like, I\'m not receiving, you know, um, any of the money. Is the financial support that you\'re providing. Um, instead it\'s looking at your company\'s processes, um, you know, your, your financial processes, your accounting processes for you to, you know, step back and think like, how could we as a company provide this level of support?

\n\n\n\n

Um, you know, it could be that you already have an existing program. Yoast already has a diversity fund. Um, and so Yoast partnering with me is a way of, um, you know, kind of bringing the need that exists to them as well. Um, and so therefore they\'re able to like further serve the community, um, you know, through those who are expressing an interest through support inclusion and tech.

\n\n\n\n

Um, the way Post Status, you know, is seeking the support speakers too, is different from Yoast. Um, and, you know, uh, [00:31:00] Yoast has a budget, um, and.

\n\n\n\n

Has their own system and their own ways of support. Um, and so they also have a budget and then Master wp, they also have a budget. And so once that budget has been met, then you know the partners essentially gray out for that year. Um, and they become active the next year. Um, and so. , that is a way of making this sustainable.

\n\n\n\n

You know, you, you pledge how much you can support, um, speakers financially, and once that has been met, then your, I mean, your capacity for the year is, is, is met. And then next year, once you\'ve reallocated your budget, or not re reallocated, but once you\'ve defined, you know, your budget, um, for the year, then you would go, you know, back into the process of supporting [00:32:00] speaker.

\n\n\n\n

Cory Miller: And I wanted to say from personal experience here, that there\'s many way, there\'s, there\'s creative ways to support these speakers, uh, to go, you know, uh, you, you talked about hotel and flights. Yeah. And, um, I, I wanna, I wanted to say that one standard to say this is not an unapproachable. Opportunity to support d uh, diversity inclusion in tech.

\n\n\n\n

Um, this is very manageable for most members at Post Status, by the way. So, you know, flight costs, uh, depending on where it is in the world. Um, I think the first question you asked me was, what\'s your budget? Yeah. And that\'s a great way. So as you come in and click sponsor, just be thinking of these things with when st for how you can.

\n\n\n\n

Help support this amazing project. Um, and that there\'s creative ways to do that. And I, I think Winstina, most members, business members at Post Status can make a meaningful contribution in this way [00:33:00] through this, your project here. And I love the fact also, I know we talked about this too, you wanted to be real careful.

\n\n\n\n

You wanna say you want the support to go to the person as best as possible. A lot of nonprofits have overhead. You have graciously generated your time and your talent to this project, and I, I, I love the way you\'ve done it too, even though I go, gosh, Winston, I love that you have this passion. Um, but thank you so much for this.

\n\n\n\n

But I know you give of your own time. For this particular project, but as you talk to Ena, if you\'re listening to this now, there\'s creative options and ENA is so good at helping you, helping understand where you\'re at, and then pair it with people that need assistance.

\n\n\n\n

Winstina Hughes: Thank you. Yes, and that is, that is the goal.

\n\n\n\n

In terms of my contribution to the WordPress community, burnout is so real and because of the fact that I work full-time outside of the WordPress space, the WordPress ecosystem, um, I\'m really [00:34:00] cognizant of the fact that I need to perform well. And at a high level , right? Uhhuh , um, at work, you know, and in my personal life.

\n\n\n\n

And WordPress fits into, um, you know, into that. And so I\'ve been able to contribute in different capacities since I was in college and. Graduate school, first attending in college, um, or post-college in graduate school, moving into speaking and organizing, um, and now working, you know, professionally maintaining, you know, organizing as a meetup organizer and a WordCamp organizer, and understanding that this can really lead to burnout.

\n\n\n\n

You know, um, my ultimate decision is, you know, that for the next two years, I\'m not gonna be a WordCamp speaker, and I\'m also not gonna be a. Organizer, you know, this, these are the ways that I can, you know, I can continue to contribute. I can contribute through support, inclusion and tech. Um, you know, but really pair, pair down all the other ways that I could burn out.

\n\n\n\n

[00:35:00] And so by maintaining, Being a New York City meetup organizer and hosting at least a minimum of six meet meetups a year, and, um, really pivoting and concentrating my energy towards support, inclusion and tech. I can sustainably contribute to the community. And so this is a perfect opportunity to really share with you, um, that, you know, I want to meet with every speaker.

\n\n\n\n

You know, that expresses the interest for support. So as you submit your, you know, your speaker registration and you join the directory listing, I will, um, you know, I\'ll ask to meet with you, for us to have a conversation, for me to understand your needs and to share. what it is I understand and I\'ve learned over time, and also how our partners seek to support.

\n\n\n\n

So we\'ll have that conversation. It\'s gonna be on the weekend. I hope you graciously incorporate that into your schedule. Um, because, you know, I, I work during the week, [00:36:00] um, and so, you know, we\'ll meet once. Uh, hopefully within a week or two of your registering as soon as possible. Especially it\'s, it\'s, it\'s ideal, uh, not ideal.

\n\n\n\n

It\'s encouraged to register as soon as possible, um, because the closer you get to your ward camp, you\'re gonna. Most likely, um, be reimbursed if you apply much sooner, like a, like two or three months in advance. You know, there are companies that will be able to, you know, cover your, your, your costs, um, of participation in advance of your trip.

\n\n\n\n

If you are reaching out like three to two. You know, to the time of, of your support that the time that you need, then you\'re looking at being reimbursed for your expenses. And so like, you know, that\'s, that\'s something to, to keep in mind when it comes to registering, you know, for this is that companies will be able to assist you with removing this.

\n\n\n\n

It just might be [00:37:00] later. When your need is expressed closer to the time that you\'re speaking that it\'s more, it\'ll be a reimbursement instead. Um, and so that\'s something to keep in mind, the timing in which you submit your interest, and also the fact that, um, you know, that we\'ll be meeting on a weekend. Um, there\'s this speaker that just registered and he wanted to meet with me.

\n\n\n\n

Um, On Christmas, he\'s in another part of the world. I mean, you know, like, so yeah. Um, and so I just, you know, I just like, I think when, and I had a con, you know, I just like responded and let him know that it\'s, it\'s Christmas for me. I\'m, you know, I\'m a Christian and I\'m so celebrating my holiday today. Um, you know, and, you know, like, uh, let\'s, let\'s meet next week.

\n\n\n\n

Um, so, you know, uh, we\'ll have like, you know, we\'ll have these conversations and we\'ll, we\'ll see. And you know how. Um, you know, how you and I can, can have that conversation and [00:38:00] meet and how your need can be met. And I\'ll also meet with, you know, companies that wanna sponsor as well. And I wanna tell you, I want you to tell me what\'s realistic for you.

\n\n\n\n

Um, I want just, just to, just to give you a sense of how some of the companies are. In fact, um, you have, uh, of the four partner. You have one partner who seeks to provide support, um, you know, within the us. Um, as of our last conversation, you know, the desire is to support minority speakers, um, specifically people of color, um, specifically, you know, black Americans, um, to improve or those of black descent to improve, um, their numbers.

\n\n\n\n

WordCamps in the US. Um, our last conversation was, you know, this is the direction that they wanna go. This is the greatest impact that they think that they can achieve. Um, and [00:39:00] I\'m, I\'m so glad that I get to listen to what everyone. Hopes to do, you know? Um, because it gives me a sense too that our community is really thinking through, like, this is how we\'re gonna solve it, right?

\n\n\n\n

Like, this is how we\'re gonna make the dent that we wanna see. So this company already knows this is how we\'re gonna make the dent that we wanna see. And there, there. Process too, is that they\'re just gonna give you a blanket amount of money and they\'re not gonna micromanage how it is you spend it. Um, they just simply ask, you know, that you, not simply, the requirement is that you put it towards your WordCamp experience and that\'s where they are with it.

\n\n\n\n

Um, there\'s, you know, another company host of course, has an established diversity fund and they have processes already in place for the support. And so you\'re simply gonna go through the existing process that, um, Yoast has established and they have a generous fund. Um, and their support, um, is something [00:40:00] that they\'ve been offering the support for a long time, and they\'re very, um, they\'re really respected , you know, for that effort.

\n\n\n\n

And, um, I\'ve had an opportunity to like, you know, to speak with someone who has been a part of their support in the past or received it and they speak so highly of, of Yoast um, and that\'s, you know, Yoast has already thought it through and they\'ve already walked through. You know, Corey and I, you and I have spoken about, you know, the budget, you know, that you\'re, that post status is set aside and, and you\'ve already shared.

\n\n\n\n

You know, what is the need? Like, we\'re not micromanaging, right? Like, let us know what type of support that you need, and we\'re just gonna provide that to you. Um, and so like you\'re, you are already thinking about like, how can we make this happen? Like, you know, if you need to, you know, it\'s a flight, you know, wherever it is.

\n\n\n\n

It doesn\'t have to be domestic, right? Like, it doesn\'t have to be in the us it could be anywhere in the world. Um, and, and that\'s, you know, that\'s like, [00:41:00] Post Status is thinking, and then GoDaddy is currently working through their process. Um, and I do believe that because of the fact that they have teams around the world that GoDaddy\'s reach will also be of, um, I think GoDaddy\'s reach will also extend beyond like the domestic, you know, like within the US and they\'ll be able to provide support as well toward camps.

\n\n\n\n

you know, around the world. I\'m anticipating it\'s possible that, um, GoDaddy\'s like impact could, you know, be especially strong with, um, Uh, WordCamps, like Word Camp Asia or Word Camp US or Word Camp Europe. Um, you know, because they\'ll have team members there and when they have team members there that can help facilitate and smooth the process over for, for those that they\'re going to be supporting, but they\'re working through their processes to make this established as well.

\n\n\n\n

And so I think [00:42:00] that, you know, just by me sharing that, you can tell that, you know, each of, you know, my partners are, are working within. Um, you know, like their business processes and their financial processes and also their vision for impact. Um, and I think that\'s really important.

\n\n\n\n

Cory Miller: So to recap, here\'s what I\'ve heard.

\n\n\n\n

So support inclusion and tech.com is the bridge between those that want have the desire to share their exper experience and expertise at word camps, but need some financial assistance to get their flights and hotel. That\'s what f support inclusion and tech.com does. Second, as a participant, as someone.

\n\n\n\n

Um, if you first need to apply and get, uh, approved to speak at work camp, then come to support inclusion and tech.com and, um, sign up, have a conversation.

\n\n\n\n

Winstina Hughes: Mm-hmm. , once you\'ve been approved.

\n\n\n\n

Cory Miller: Have a conversation with we, Tina. [00:43:00] And then third, the third recap is our ask for, um, well buzzer asked in our community.

\n\n\n\n

Uh, if you\'re looking to speak to Word Camp, go to support inclu or go apply, get approved, come to support inclusion in tech. And then second for those businesses out there. You know, you have a heart, you wanna support this. That\'s our community. That\'s who WordPress is. Uh, go to support inclusion and tech.

\n\n\n\n

Click on the sponsor link and have a conversation with ena. Think about your budget. Think about what you wanna do, uh, when Cena is so creative in helping just make these connections happen so you can really make a difference in our community. Did I get it all right?

\n\n\n\n

Winstina Hughes: You did. You did get it right And, okay.

\n\n\n\n

And I think that support inclusion tech also. It goes through vetting process as well to confirm that those who are seeking assistance, you know, to participate actually have been accepted. And that\'s why, that\'s why the steps are what they are. Um, partners aren\'t gonna [00:44:00] question, oh, is this need real? You know, that vetting is gonna happen in advance.

\n\n\n\n

So when you receive a speaker interest, You know that this is someone who has been accepted a Word camp, and they understand the process and they\'re working within, you know, your, your policies and your procedures, um, in order for them to participate. So it removes all those questions. Um, you know, so that and that, yeah, that\'s a part of it.

\n\n\n\n

Cory Miller: Well, Winston, my friend, thank you so much for this important work, uh, holding the banner up. I know this takes a lot of time. I know you\'ve got a full-time gig. I know you\'ve got a life

\n\n\n\n

Winstina Hughes: more,

\n\n\n\n

Cory Miller: um, But I so much appreciate you post. I just appreciate you, our members do for doing this vitally important work and making a difference in our world that can, like we said, can be a reflection in all these thousands of communities we go out to, to say, how can I be more inclusive?

\n\n\n\n

[00:45:00] How can I make sure everybody is represented as at least an opportunity to be represented? So I really appreciate you, Winstina, and your work and also just ringing the bell with me and teaching me and sharing, um, how we can make, make that difference. So I appreciate. Thanks for being. I\'m thanks for being on.

\n\n\n\n

Winstina Hughes: Sorry. No, no. I mean, I absolutely, like, this gives me life and it makes me wanna show up in the world, you know, different and energy. I wanna exercise more like , you know, like this is, this is, this is really in a lot of ways just like giving me energy to contribute. And so, um, to like, just to be able to like, work with you, you\'re, you\'re, you know, I\'m, I think you\'re awesome

\n\n\n\n

You know that, ditto. You\'re, you have a beautiful family. You know, like your energy is like, you have such great energy and so just a chance to work with you and like the amazing people that I\'ve had a chance to, it, it just, it gives me life and it makes me want to live more, you know? So like, let\'s, let\'s [00:46:00] see what we can do to continue to support our community so that the four freedoms, you know, I think that it\'s, , it\'s creating a fifth freedom, which is, you know, for all of us to be able to participate in a truly inclusive, um, community.

\n\n\n\n

And, you know, that speaks a lot to what the co-founders of WordPress. I think, um, you know, what, what they created and, and where they want, um, what their vision is and, you know, from their vision where we\'re, um, going and or how we\'re evolving as a community. I mean, to have 40% plus of a reach on the.

\n\n\n\n

There\'s so many people around the world that are impacted by this project, you know? So, um, yeah, I love

\n\n\n\n

Cory Miller: that. Let\'s, let\'s add the fifth Freedom. I love that win. Coined by Win, and I love that leadership vision for our community. We need it. Thank you. Thank you, ma\'am. You have a good rest of your year and we\'ll see you in the next year.

\n\n\n\n

For everybody listening, thanks for listening. Tune in, go to support inclusion in [00:47:00] tech.com, and also Winstina Hughes is in our post Slack community. So you can go at Wednesday and you can ping her and, um, get the conversation started there. So thank you, Eena.

\n\n\n\n

Winstina Hughes: Thank you. Thank you, Brent.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 09 Jan 2023 19:17:12 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Olivia Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:47;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"Do The Woo Community: Do the Woo is Headed to WordCamp Asia\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74283\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"https://dothewoo.io/do-the-woo-is-headed-to-wordcamp-asia/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:373:\"

We are looking forward to attending WordCamp Asia and also are proud to be a media partner this year.

\n

>> The post Do the Woo is Headed to WordCamp Asia appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 09 Jan 2023 10:19:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:48;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"Gutenberg Times: Gutenberg Changelog #78 -State of the Word, WordPress 6.2, Gutenberg 14.8 and 14.9\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://gutenbergtimes.com/?post_type=podcast&p=23141\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:114:\"https://gutenbergtimes.com/podcast/gutenberg-changelog-78-state-of-the-word-wordpress-6-2-gutenberg-14-8-and-14-9/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:41325:\"

Birgit Pauli-Haack and Hector Prieto talked State of the Word, Gutenberg releases 14.8 and 14.9, WordPress 6.2 and beyond. 

\n\n\n\n

Show Notes / Transcript

\n\n\n\n\n\n\n\n

Show Notes

\n\n\n\n

State of the Word

\n\n\n\n\n\n\n\n

Gutenberg Times Live Q & A

\n\n\n\n

Gutenberg Times Live Q & A: January 11th at 5 pm ET / 22:00 UTC Layout, Layout, Layout.

\n\n\n\n

Isabel Brison’s talk at WordCamp Asia

\n\n\n\n

WordPress and Gutenberg Releases

\n\n\n\n\n\n\n\n

Stay in Touch

\n\n\n\n
\n\n
\n\n\n\n

Transcript

\n\n\n\n

\n\n\n\n

Birgit Pauli-Haack: Hello, and welcome to the 78th episode of the Gutenberg Changelog podcast. In this first episode of 2023, I wish all our listeners a wonderful, happy, prosperous and healthy new year. In today’s episode, we will talk about Gutenberg releases 14.8, 14.9, WordPress 6.2 and beyond. I’m your host, Birgit Pauli-Haack, curator at the Gutenberg Times and WordPress developer advocate, a full-time contributor to WordPress Open Source project. My guest today is Hector Prieto, full-time contributor on the WordPress Core team, coordinating multiple WordPress and Gutenberg releases. And it’s a great pleasure to finally have you on the show, Hector. Having a conversation about Gutenberg and WordPress with you is a wonderful way for me to start this new year. Happy New Year, feliz año nuevo, Hector. How are you today?

\n\n\n\n

Hector Prieto: Happy New Year. Hi, Birgit. I’m excited to join you on the podcast. It’s my pleasure.

\n\n\n\n

Birgit Pauli-Haack: Oh, the pleasure is really all mine. Where are you right now? Did you have a great holiday break?

\n\n\n\n

Hector Prieto: I’m currently in Alicante in Spain, very close to the Mediterranean Sea. And today we have a lovely sunny winter day with nearly 20 degrees Celsius. I had a few days to recharge and spend time with the family. What about you, did you enjoy your holidays?

\n\n\n\n

Birgit Pauli-Haack: Yeah. Well, that’s some warm weather there in Alicante. I would love to have that. But here in Florida it’s balmy, too. It’s about 27 degrees, so we are in the air conditioning right now. Yes, my husband and I, we spent the week in Mexico City between Christmas and New Year’s. We saw some great art, powerful murals from the ’50s and ’70s and ’60s. And we had fantastic food and a fabulous New Year’s event. It was great, at a restaurant over the roofs of Mexico City, so we really liked it.

\n\n\n\n

Hector Prieto: Wow, sounds really nice.

\n\n\n\n

Birgit Pauli-Haack: Yeah. Well, Hector, as you are the first time on the show, maybe you can share briefly with our listeners your WordPress origin story. When did you come across WordPress the first time and what do you work on now?

\n\n\n\n

Hector Prieto: Well, my first time working with WordPress was around 2015 when I worked at the startup agency building sites. However, it wasn’t until 2020 that I first moved into the contributor space, and here we are. I am currently sponsored by Automattic to work full-time in Core in project management-related duties and supporting the development of WordPress.

\n\n\n\n

Birgit Pauli-Haack: That’s wonderful. Well, thank you. So 2015, that’s just about two years before Gutenberg was introduced into the community. Did you, at your agency, have Gutenberg on the radar already, or did you heed the call to learn JavaScript deeply?

\n\n\n\n

Hector Prieto: It wasn’t until 2018 that we started using Gutenberg for the first time, when it was first released in 5.0.

\n\n\n\n

Birgit Pauli-Haack: Yeah. Then the time between learning about WordPress and then starting contributing, that’s about five years. That’s pretty much the time that it took me to really embrace the contributing on WordPress, but I started at the Community Project in 2014. 

\n\n\n\n

Announcements

\n\n\n\n

All right, so there are a few announcements that were happening since the last podcast episode. If you haven’t watched it yet, the recording of Matt Mullenweg’s State of the Word is available on WordPress TV. The transcript and answers to the questions that didn’t make it into the recording can be read on the follow-up post, State of the Word Reflections. Josepha Haden Chomphosy kicked off the State of the Word with a reminder on the four freedoms of WordPress, that you are free to run the program, you’re free to study and change the code, you’re free to distribute your code and also redistribute WordPress.

\n\n\n\n

She also recorded a separate WP briefing with her reflections in episode 45, State of the Word Reflections in which she highlights, among other things, learn WordPress, that 12,000 students actually went through the courses and the workshops already since the inception. And she also highlighted the WordPress Playground, which is a tool to run WordPress in the browser. You don’t need a server, you don’t need a database. You can run it in the browser and test plugins and themes. I think that changes how we approach some of the discovery for WordPress. We talked about it on the show here as well, but it’s definitely something that will have so many ramifications in the WordPress space later on when it’s still very raw and very not production ready. It’s just an idea that has already a proof of concept. And then the recap posts from the community are linked in the Gutenberg Weekend Edition 239 from December 17th, and you can check it out from there.

\n\n\n\n

I also have a side note that the Pew Research Center received a shoutout for the politology quiz that they built with blocks and had one million people already taking it. Seth Rubenstein is the lead developer and was a guest on a Gutenberg Times Live Q&A last year. And he gave a great demonstration about their team’s work with the block editor, so as they went for the Gutenberg first approach building the website. The recording is available on the Gutenberg Times YouTube channel, and also we have a post here on the Gutenberg Times website as well. So as always, all these links are in the show notes of the 78th episode. So Hector, do you have any comments on this? What is your most exciting topic from the State of the Word? You had a few takeaways?

\n\n\n\n

Hector Prieto: Yeah, there were a handful of them. I would actually highlight everything, starting with WordPress Playground. It’s such amazing technology and it’s going to open so many doors. But if I had to pick something, maybe for me because it is the thing I’m the closest to, it was a great recap about the progress WordPress made in the site editing front during the last year, to the point nowadays we can create themes directly in the editor just with blocks and patterns. This brings us very close to wrapping phase two and starting exploration around phase three in 2023. So it’s great to see that all these progress.

\n\n\n\n

Birgit Pauli-Haack: Yeah, you’re right, you’re right. And it’s been such a long journey as well. I look back at some of the history on the Gutenberg Times and the Gutenberg podcasts, and we first started talking about full-site editing in January of 2020. That was even pre-pandemic, and we had quite a few developers on our live Q&A talking about the first concepts about that. So now, three years later, it’s almost finished and it’s really cool. There are still some things to be done, but I am really excited about the start of phase three of collaboration and I have been constantly trying to unify all the various tools and methods and interfaces to streamline my workflow to produce content for the web. And if I don’t have to use multiple tools to collaborate with people, I will have arrived on internet nirvana. Yeah, it’s a high calling of course, but yeah, we are all in a space where we could maybe make it happen. So I’m really excited about that.

\n\n\n\n

Hector Prieto: Yeah. Also, it’s worth noting that even when we move to phase three and we can call a wrap on phase two, phase two will not be fully finished because there’s always going to be things to do related to site editing improvements, new tools. So I can see contributors working in new features for phase three and also iterating on phase two items. Another big takeaway for me during State of the Word was seeing how much Gutenberg itself has matured. And it’s now been used in more projects such as Tumblr, bbPress, and even in some mobile apps like Day One. Also, let’s not forget how WordCamps have made a comeback after COVID hit and stopped all the in-person events. And we went from one single WordCamp in 2021 to up to 22 in the last year, in 2022. That’s amazing.

\n\n\n\n

Birgit Pauli-Haack: Yeah, that’s a nice iteration of the numbers. 22 WordCamps in ’22.

\n\n\n\n

Hector Prieto: Exactly. Especially since the community is what makes WordPress what it is, it’s the most important part of WordPress. So that’s really good to see.

\n\n\n\n

Birgit Pauli-Haack: Absolutely. Having the first WordPress in-person event in WordCamp Europe, I realized how much I missed interacting with everybody else in the community and seeing new faces and interacting with old friends. I looked up the number of WordCamps that were done in 2019, in-person WordCamps, and there were 148, or 145, something like that. So there is quite a bit of time to go between 22 to 142 or something like that.

\n\n\n\n

But it’s coming back especially because all those WordPress meetups, the local meetups, are all coming back as well. I think there was a note in the State of the Word that out of the 500, 260 have already come back to in-person events. And we know that WordPress meetups are actually the prerequisite to actually have local WordCamp organizers together to organize a WordCamp. So yeah, it’s all coming back and I’m glad that it’s coming back because of the connection that you have in the community. Yeah.

\n\n\n\n

Hector Prieto: As I mentioned earlier, I came to the contributing space in 2020. It was during the pandemic, so actually my first WordCamp was the only WordCamp in 2021. And my second WordCamp was for computer ware in last year. So it was really nice and refreshing for me to meet all the other contributors. It is something special, for sure.

\n\n\n\n

Birgit Pauli-Haack: Absolutely, yeah. It was great to meet you, Hector, although we had so many meetings with people on Zoom. Yeah.

\n\n\n\n

Hector Prieto: Fun times.

\n\n\n\n

Birgit Pauli-Haack: Yeah.

\n\n\n\n

Hector Prieto: Well, circling back to State of the Word, I would also like to point out that, last but not least, it’s really cool to see how Openverse has grown since joined WordPress about a year and a half ago. And I’m super excited to see that coming, Openverse integration in WordPress that will allow users to directly search and add images from Openverse into their WordPress site without leaving the editor at all. That’s super cool.

\n\n\n\n

Birgit Pauli-Haack: Yeah, that’s super cool. And I think it would also be really cool to have that also go back to if somebody uploads an image to WordPress and checks the check mark, also put it into Openverse. I think that part would really make it to a 360 kind of integration. I also love that there’s not only for images, but there is a lot of audio already uploaded to the Openverse that you can use on podcasts or on videos, and add free without having to think about royalties and buying for it and all that. Yeah, so free to the community.

\n\n\n\n

Hector Prieto: There’s so many possibilities there. The future is exciting.

\n\n\n\n

Birgit Pauli-Haack: Yeah, it’s really exciting. And I’m glad that it’s all happening in conjunction with WordPress. The same with the WordPress photos library, where people can just upload their photos and have it be it in the public domain and make it available to the broader community. It’s really cool.

\n\n\n\n

Hector Prieto: Yeah.

\n\n\n\n

WordPress 6.2

\n\n\n\n

Birgit Pauli-Haack: All right. So between Christmas and New Year’s, Hector, you published the release schedule proposal for 6.2. I think it was something we were all waiting for. Kind of, okay, how do we plan first quarter when we don’t know when the release is coming? So you provided. So if the release team concurs, what’s the plan? When will we see the first Beta?

\n\n\n\n

Hector Prieto: If the proposed plan is approved, the first Beta release will be on February 7th, which is 10 days before the first of our WordCamp Asia takes place.

\n\n\n\n

Birgit Pauli-Haack: Excellent. In the planning schedule, you also have a call for contributors to volunteer for the release squad. So if you, dear listeners, are inclined to take part in it and you already have a little experience in contributing, throw your hat in the ring by commenting on the release post on the scheduled proposal post. And also throw your hat in the ring also means for those who English is their second language, also means raise your hand, you want to volunteer to be part of it, and then the release team is coming together. When do you expect that you will have a final plan?

\n\n\n\n

Hector Prieto: The call for volunteers is open as we speak. Considering the end of the year vacation people are taking, contributors taking, I think we won’t have anything until end of next week or the following one. We’re leaving some extra time for people to come back from the holidays and chime in.

\n\n\n\n

Birgit Pauli-Haack: All right. Okay. Yeah, so there are only two more Gutenberg releases before the feature freeze, if I calculate that correctly. We better get started in reviewing all the great new features that are coming in, in a more consolidated way.

\n\n\n\n

Hector Prieto: Definitely. I encourage all of our listeners to start testing and giving feedback. It’s always super helpful. Also, compared to the past releases, the proposed 6.2 schedule both include a fourth Beta release compared to the previous three ones to leave some extra buffer time between WordCamp Asia and release candidate one, which will be on March 7th for a final release on March 28th.

\n\n\n\n

Birgit Pauli-Haack: Oh, okay. Yeah, so contributor day at WordCamp Asia is definitely going to be part of it and that is really cool to have. Maybe we need to organize some tables that do some testing there. I don’t know how far the work of Asia contributor day team is about that, but having that plan definitely gives us all focus on that contributor day. All right, cool. So to repeat that, final release could be March 28th, so that’s about three months from today. And we will have a 6.2 release, provided everything works out as we anticipate now.

\n\n\n\n

And I have a reminder for our listeners now for next week. The Gutenberg Times Live Q&A, Layout, Layout Layout will be happening on January 11th at 5:00 PM Eastern. That’s 22:00 UTC. And in this show, Isabel Brison, Andrew Serong, Justin Tadlock and I will discuss the opportunities and challenges for all the layout features for site builders. And we will be available for questions and answer them.

\n\n\n\n

And Isabel Brison will also give us a demo of the various layout scenarios to use. She has, with Andrew, been instrumental in building all the features into the site editor and the blocks, and it’s going to be a very interesting show. It’s also going to be a little preview on Isabel Brison’s talk at WordCamp Asia in February 2023. So join us, link us in the show notes, and don’t forget you need to register there and to be… We will have a recording, of course, with the show notes and as well as a transcript, but it’s always good to have your questions answered live by the experts on the panel, and we have some great experts there. 

\n\n\n\n

What’s Released

\n\n\n\n

So, that brings us to the latest Gutenberg releases. First, there’s Gutenberg 14.8. That was released in December 12th. Ryan Welcher was release lead and it had 167 PRs merged by 42 contributors, five of which were first contributors. So welcome to the project, first contributors. So Hector, what’s the most significant enhancement in this release?

\n\n\n\n

Hector Prieto: Well, Gutenberg 14.8, so several changes to the site editor user interface, and introduce something I’m super excited about, which is browse mode. Thanks to this first iteration of browse mode, users can switch between editing and browsing modes in the site editor, making it much easier to navigate through templates and template parts or even add new ones through the sidebar. It’s a feature that has been long awaited and it’s finally here and I’m super excited.

\n\n\n\n

Birgit Pauli-Haack: Yeah. And it helps you with where you land when you click on the site editor. You are now not landing into editing your homepage and so now you have a better entrance into the site editor. And I really like that because it gets you better settled into what you’re going to do.

\n\n\n\n

Hector Prieto: It makes for a nicer onboarding and it’s less dangerous, let’s say, because it’s much more difficult to break your design just as soon as you land on the site editor.

\n\n\n\n

Birgit Pauli-Haack: Yeah, totally. So the navigation block also had some enhancements, especially with the migration from the old menu. So if you have a location primary, it will now fall back to the navigation menu from the classic menu. That is really helpful on the transition. There are other fallback updates made that it also uses the most recently created menu from the classic theme when you start migrating to a block theme.

\n\n\n\n

Enhancements

\n\n\n\n

So that is definitely a good help for transitioning from a classic theme to a block theme. But also it kind of decreases the mental load that you don’t have to recreate all your menus when you switch out the theme, which is something that was sometimes really critical in the classic menu, in the classic theme space, where everybody had different menu locations. And so I don’t think that it’s completely solved yet, but this is definitely a first step.

\n\n\n\n

Hector Prieto: Yes, it’s a step on the right direction. We all know building menus is one of the most challenging aspects of building your site. And contributors are making a huge step for making the menu building process much easier.

\n\n\n\n

Birgit Pauli-Haack: Yeah, absolutely. Yeah. There’s also one that came with 14.8 that is for the query block. The parent block is they removed the color block support just because it was always clashing up against the other blocks that are in the query block for the post template for the title and the excerpt. You could kind of get lost in which color did we do, and where do we do that? So removing it from the wrapper query block is definitely a good choice because it removes some of that confusion of where colors are actually set.

\n\n\n\n

Hector Prieto: Exactly. Contributors have seen a few inconsistencies when adding the color to the wrapper query block, between the title, between the navigation links. So now the colors block supports are all in the inner blocks and there’s no space for confusion.

\n\n\n\n

Birgit Pauli-Haack: So what else do we see there? Yeah, I think that was it on the 14.8 release, on the highlights. There are certainly the sidebar tabs for the navigation blocks. There is great work on the experimentation that happened. So right now we have five areas of experimentation in the Gutenberg plugin and there is only two more freeze, two more releases to get them out of experimentation into the production of the Gutenberg plugin. One of them is the sidebar for the navigation block. The other one is the separated settings tab in the sidebar that separates the styles from the features. And then the others, I don’t recall right now. Hang on, I’m going to check them out. I just had it there and then I closed my browser because, I don’t know, sometimes I just randomly close browser tabs, which is a really good way to confuse myself.

\n\n\n\n

Hector Prieto: The type interface is making good progress and it’s something we would likely see out of experimental very soon.

\n\n\n\n

Birgit Pauli-Haack: Oh, yeah. And then is the global styles for custom CSS is actually in… We all wait for that, but it’s now in the experimental stage and need to be switched on through experiments menu item on the Gutenberg plugin. And then the other one is the color randomizer utility that lets you mix the current color palette randomly and change it out. That’s kind of a funky way of handling your website to do a randomized color palette, but it certainly is a proof of concept of something bigger. Was there anything else in the 14.8 you want to mention, Hector?

\n\n\n\n

Hector Prieto: Well, there are a few other main highlights that you might have seen, our listeners might have seen in the release post. One of them is super interesting, which is the custom CSS rules for your site. There’s now a tiny CSS text field where you can add your custom CSS directly in the editor. As we all know, with great power comes responsibility. So it’s nice that you can add a custom CSS directly in the editor, but let’s not overuse the important.

\n\n\n\n

Birgit Pauli-Haack: Yeah, that’s definitely a way to… But that was before, so site editors or site users or site owners who used the custom CSS piece found that that was the missing piece to actually sign on to the full site editing, because they couldn’t do those very fast changes like changing a font size somewhere or changing a space somewhere or change the color of a border very easily by just using the developer tools, identifying the marker, the selector, and then just change the color in a custom CSS. Yeah, it opens up the capabilities for that.

\n\n\n\n

You need definitely have file editing capabilities on the server and that sometimes was not available to anybody. But those who used it, they really missed it in the file site editing, in the site editing features, so that is really a good thing. And there is also a… It’s not yet released and it’s not merged yet in, but I know that Carolina Nymark is actually working on custom CSS for single blocks. And I think that’s also a good way to, in the paradigm of getting atomic design going, that that’s probably a better approach than having custom CSS being pulled in for every site page or page with the custom CSS. Rather do it per block.

\n\n\n\n

Either way, it’s kind of a interesting feature that people want to have some control or at least go back to that what they are used to do and figure out how they can change it. Well, that definitely was a changelog of 14.8. I don’t think we’ve forgotten anything. I think we, in the release post by Ryan, it was a reorganized…. Oh, the style book is definitely something that was in 4.8. We haven’t talked about it. So do you want to talk about it, what that does?

\n\n\n\n

Hector Prieto: Oh yeah, definitely. The style book is a super cool new feature, which is extension of the style site editor. The style book in a nutshell gives you an overview of all the available blocks you have in a single place so that you can easily browse all the blocks you have available and play with their design.

\n\n\n\n

Birgit Pauli-Haack: When Gutenberg first came out, there were quite a few initiatives where you could have a unit test for blocks, where I think Rich Tabor actually had a plugin and I also worked with some of our clients back then when that we had a list of all the blocks in a page and then looked at it, how the theme works with it. And that was kind of a block unit testing in design. And with the additional features that come with site editing, it was a hard time to figure out what is a change in color on the paragraph block will have additional ramification throughout the site, or when you change style variations.

\n\n\n\n

So I’m really glad that the style book, that’s a menu item in the site editor. You can go there and then see all the blocks that you have. And you get an access to the style variations of your theme so you can select them and then see how the blocks change. And that is so powerful that you don’t have this save and surprise effect anymore. You really look at it and say, “Oh yeah, I like it.” And you also see where the style variations may not be entirely working for your site because there are some things that are left out. So this is so powerful for the experience with the block editor.

\n\n\n\n

Hector Prieto: For our listeners to picture it in their mind, it’s like having a page with demo content with all the blocks. You have it registered either core blocks or third party blocks. So as soon as you install it, applying that provides blocks, all these third party blocks will appear in the style book. And you will be able to see all of them together, play with the global styles, play with the accelerations, and see how they affect all these first party and third party blocks in a single place as if you have demo content page but automatically generated for you.

\n\n\n\n

Birgit Pauli-Haack: Yeah. So it really offsets the need for these block unit testings and it’s very, very powerful. Yeah, I so agree. I think we’ve got it all now. Let’s move on to the next release, which was 14.9. And it has at the time of this recording not been released, but it will come out any hour now. For those who use the Gutenberg plugin on their sites, it’s the first Gutenberg release for 2023. 132 PRs by 46 contributors. Again, five new contributors in there. Congrats for your merge of your PR, and welcome to the project. Thank you so much for your contributions, for all of them.

\n\n\n\n

Hector Prieto: It’s refreshing to see all these new contributors, even in these more maintenance oriented releases that happen during holidays. So congratulations to you all, and welcome.

\n\n\n\n

Birgit Pauli-Haack: What are the highlights? What did you see, or what’s in the release?

\n\n\n\n

Hector Prieto: There are a few changes. They’re mostly iterative, building on top of past features and enhancements. One of them, one very cool, is a new push to global styles button that appears in the cyber blocks, which allows users to, once they edit the blocks’ style and they like it and they say, “Hey, I like this how this is looking or how this image is looking. I would like all my image blocks to look like this.” It allows them to push those styles to global styles so that they automatically affect all the blocks of that type.

\n\n\n\n

Birgit Pauli-Haack: Right. And that’s also why it’s good to have this style book handy so you can actually see if you made a mistake or something like that and said, “Oh no, I didn’t consider this, so let’s do one more time.” Yeah. So that’s a great feature. Yeah, absolutely.

\n\n\n\n

Hector Prieto: Also, for those who like building patterns, now when registering patterns, there’s a new property that allows you to specify in which template a pattern makes sense. Let’s suppose, for example, we are building a 404 pattern. Previously it would be released everywhere, so it would appear everywhere in all kinds of templates. Now you can limit it to only appear on a 404 template, so it doesn’t bring noise to other templates where it doesn’t make sense. So this is going to improve pattern discoverability in general as patterns.

\n\n\n\n

Birgit Pauli-Haack: And it also improves separation of concerns. As you said, it will not show up on every page where even if it’s not suitable for the pattern. But it also themes can then, or plugin can now create custom post types and that all, and just make those patterns available for certain custom post types. I think that is definitely a missing piece that has now been added to it. Excellent. Yeah, I’m really excited about that.

\n\n\n\n

Hector Prieto: Yes, there’s a minor update following up on Gutenberg 14.5. So we are thinking, we’re looking at two months ago, three months ago, when the list view and the document outline were merged in a single panel. We have seen there are a few improvements that can be made in the design. So now, for example, the word count has been moved to the top of the outline for more clarity.

\n\n\n\n

Birgit Pauli-Haack: Yeah, there was some confusion. Where is it now? Yeah. And then you didn’t see it at first because when you hit on update, you post and then the little notification ball totally covered that piece. So it took a while till that goes away so you see the word count and the time to read and also the block count. There were a few pieces missing. I don’t know why they’re missing, but they probably don’t seem to be very important for content creators to see. And the outline, having the other one on the list here in one it definitely makes sense to have that.

\n\n\n\n

So if you haven’t seen that yet, it was in Gutenberg 14.5. It will come to 6.2, so checking it out through the Gutenberg plugin is definitely worth trying, worth a look so your site owners or the clients are prepared to find it in a different space. What I’m also very excited about is that there is now an option to import widgets from the sidebars into template parts. And that is in the whole idea of transitioning from a classic theme to block themes or make a site be better prepared to move to a site, to full site editing block theme. This is definitely a step forward. Any additional thoughts on that?

\n\n\n\n

Hector Prieto: Oh yeah, absolutely. This is a very important milestone towards block adoption because it allows users to migrate from classic widgets to native blocks. It’s worth noting that it doesn’t work on template focus mode yet, it’s only available for the block inspector. But this is definitely a step on the right direction to increase block adoption.

\n\n\n\n

Birgit Pauli-Haack: Excellent, yeah. And George Mamadashvili, who heads… That’s his PR. He also has a nice video on how he demonstrates how it’s going to work. So I hear quite a few people celebrating this piece to make the transition over. Another one is, this is minor thing, but the configurable settings for the fluid typography in the theme JSONs now has a minimum font size, so it can be anchored on the smallest font size. So the fluidity then can increase the font size on a bigger screen. There was a hard coded value of 14 pixels before, with no way to change. And now you can have the minimum font size, like 16 pixels or 18 pixels depending on your site needs. That’s a minor thing, but I think it is something that quite a few designers were missing.

\n\n\n\n

Hector Prieto: Yes, absolutely. It’s a minor improvement, but we’ve seen lots of these minor improvements in the last, I don’t know, four or five Gutenberg releases building on top of free typography. And when you look at them altogether combined, you can see huge improvements on how the feature is becoming more and more powerful by the day.

\n\n\n\n

Birgit Pauli-Haack: Yeah, absolutely, absolutely. I think that was at 14 point… No, one thing is still really important from the release and that is the adding shadow presets support for the theme JSON. So you can do box shadows on your blocks or wrapper blocks, and that is now available for designers of themes. There is no user interface for that yet. But as we said repeatedly here on the podcast, things will be…

\n\n\n\n

Hector Prieto: It will come.

\n\n\n\n

Birgit Pauli-Haack: Hmm?

\n\n\n\n

Hector Prieto: It will come.

\n\n\n\n

Birgit Pauli-Haack: But it’s important to make it work for the theme developers first. Before you have all the added implementation for site owners that want to change it, you first need to know how it’s actually working so you can see where the pieces are that need to be surfaced in a user interface. So there is a new setting object called shadow, and then you can add different palettes to it for natural and crisp and sharp and soft shadows. And the PR has quite a few information about how that’s implemented. It gives you quite a few use cases on how you can do the shadow boxes for the buttons, for cover block, for menu block. If you have a sticky menu, then you can put a little shadow underneath it to see the difference between the page and the menu. So there are quite a few design use cases to try that out.

\n\n\n\n

Hector Prieto: I’m curious to see what designers come up with thanks to this new setting. I can see lots of 3D buttons and shadow buttons and all these cool things.

\n\n\n\n

Birgit Pauli-Haack: You could even do the outlines of the shadows, kind of, if you have an outline… Yeah, there are some great designs out there right now. So, from the changelog we are on, anything else that you wanted to talk about here that we missed? I know that Tonya Mark has updated the tracking issue for the web fonts API. And what’s merged in this release is the change of architecture to use the Core’s dependencies API with the web fonts API. And there’s a call for testing out there, or it will be out there, and making sure that how you use it. She has an update where she asked how you can help. And that is if you have an idea about naming the API, should it be webfonts, or web fonts, two words, or just the fonts API, which I tend to be the fonts API, but there is a renaming before everything gets into the Core that we’ll be name things right.

\n\n\n\n

And then the other one is a call to test the new architecture and share feedback on your testing reports and using the web fonts API. I’m not quite sure how the planning is because it seems to be still blocked furthermore through additional architectural work. Hector, do you think that that will come with 6.2, or is it now a little late for 6.2?

\n\n\n\n

Hector Prieto: Well, Tony and the other contributors are making their best to have this feature land in 6.2, so I’m pretty positive it can make it in 6.2. And the best way to ensure it can land in that version is to help with testing and with feedback. That will help unlock the architecture redesign and the renaming and everything that’s currently being discussed right now.

\n\n\n\n

Birgit Pauli-Haack: All right. Okay. Yeah, if you all are contributing to things, dear listeners, help getting that over the finish line. It definitely needs some testing. So I think that’s the end of talking about Gutenberg 14.9. We’re coming also up on the hour, so I think we can go to closing things. Are there anything that you want to point out that are on the roadmap for 6.2 that you want to have our listeners know? And if not, how can the listeners get in contact with you, where to best meet you online?

\n\n\n\n

Hector Prieto: Well, you can reach out to me in WordPress Slack. Handle is Prieto. I guess it will be written in the show notes. So please feel free to ping me there or in GitHub or in Track. I’m using the handle everywhere, so that’s easy. I would just like to circle back to the 6.2 planning and reminding everyone the call for volunteers is open. So if you’re interested in participating in the squad, you are more than welcome. We will assist you if it’s your first time. If you’re an assistant contributor, you are also welcome and we can learn from you. So everybody’s welcome, that’s the long story short.

\n\n\n\n

Birgit Pauli-Haack: Yeah. And the only thing that I want to remind you is about the next week’s Gutenberg Live Q&A with Isabel Brison, Andrew Serong and Justin Tadlock on Layout, Layout, Layout. January 11th at 5:00 PM Eastern and 20:22 UTC. That’s 10:00 PM on UTC. And as always, the show notes will be published on gutenbergtimes.com/podcast. This is episode 78. And if you have questions and suggestions or news you want us to include, send them to changelog@gutenbergtimes.com. That’s changelog@gutenbergtimes.com. So thank you so much, Hector, for joining me here for the first Changelog podcast in 2023 to spend the time on preparation as well as in the show. Thank you all for listening and goodbye and again, Happy New Year.

\n\n\n\n

Hector Prieto: Thank you for having me and see you soon. Happy New Year, everybody.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 07 Jan 2023 19:14:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:19:\"Gutenberg Changelog\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:49;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:121:\"Gutenberg Times: 209 Block Themes, Query Block Variations, Forms with Blocks, Block Art and more – Weekend Edition #240\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://gutenbergtimes.com/?p=23042\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:124:\"https://gutenbergtimes.com/209-block-themes-query-block-variations-forms-with-blocks-block-art-and-more-weekend-edition-240/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:27679:\"

Howdy,

\n\n\n\n

Tomorrow is the 5-year anniversary of Gutenberg Times. It feels like I just started yesterday to be fascinated by the possibilities of the block editor. For many people, it actually was just yesterday that they dipped their toes into the world of the new thing. Not you of course. You have been a wonderful subscriber and reader for a while now, and I am infinitely grateful for your support. Thank you!

\n\n\n\n

Welcome to all new subscribers this year. So glad you are here.

\n\n\n\n

Let’s dive into the sixth year together, and learn what will be next for the block editor and what other people make with it and for it. The ecosystem seems to keep expanding quite a bit with the block editor.

\n\n\n\n

Wishing you and yours a fabulous 2023. May you be prosperous, happy, and healthy!

\n\n\n\n

Yours, 💕
Birgit

\n\n\n\n

PS: Reminder: Hope to see you next week at the Gutenberg Times Live Q & A. Get your seats now for January 11, 2023, at 5pm / ET 22:00 UTC

\n\n\n\n\n\n\n\n\n\n

Developing Gutenberg and WordPress

\n\n\n\n

Hector Prieto published the WordPress 6.2 Planning Schedule Proposal, and it’s also a call for volunteers for the release squad. The 6.2 release squad will then decide on the final release schedule. For now, Feature Freeze and Beta 1 would be on February 7th, 2023. Tthere are four Beta releases planned before release candidate 1 will be available on March 7th, and a final release on March 28th, 2023.

\n\n\n\n
\n\n\n\n

Reminder: January 10, 2023, at 9:30 ET / 14:30 UTC: Hallway Hangout: Performance Considerations for Block Themes Anne McCarthy wrote: “At a high level, we’ll go through general intros (what each person does/focuses on), current work underway to address performance, what work is being done specifically for block themes, and general open Q&A. Hallway hangouts are meant to be casual and collaborative so come prepared with a kind, curious mind along with any questions or items you want to demo/discuss.”

\n\n\n\n

From the WordPress Developer Blog

\n\n\n\n

Justin Tadlock published a tutorial for building a book review grid with a Query Loop block variation. WordPress 6.1 introduced an extension to the Query Loop block, which allows plugin developers to filter existing functionality in core WordPress rather than building custom blocks to query posts. This tutorial shows how to build a WordPress plugin that display a list of book review posts including post_meta` data, using a block variation for the Query Loop and set up rendering it on the front end.

\n\n\n\n

Nick Diego tweeted: I always knew the Query Loop block was incredibly powerful, but I had never explored integrating post metadata into custom block variations! Learn how in this fantastic article by @justintadlock on the new WordPress Developer Blog.

\n\n\n\n
\n\n\n\n

Micheal Burridge composed a Roundup post to review 2022 from a block developer’s perspective in is post. You’ll find a select list of resources, to get started or to catch up on the development from the last 12 months, via the Make Blog, WordPress TV and the Learn WordPress site.

\n\n\n\n

Gutenberg plugin releases

\n\n\n\n

Gutenberg 14.8 was released on December 22, 2022, and release lead Ryan Welcher highlighted in his post What’s new in Gutenberg 14.8? (21 December)

\n\n\n\n\n\n\n\n

Sarah Gooding reported on the release as well via the WPTavern: Gutenberg 14.8 Overhauls Site Editor Interface, Adds Style Book

\n\n\n\n
\n\n\n\n

Gutenberg 14.9 is the first release of 2023, and release lead Justin Tadlock pointed out a few new features in his post What’s new in Gutenberg 14.9? (4 January):

\n\n\n\n\n\n\n\n

On the WPTavern, Sarah Gooding took the version for spin and reported on the new magic: Gutenberg 14.9’s New Magic: Push Block Changes to Global Styles

\n\n\n\n
\n\n\n\n

In the upcoming Gutenberg Changelog episode 78, Hector Prieto was my guest. He is a full-time core contributor and coordinator of multi-release WordPress and Gutenberg releases. We discussed Gutenberg 14.8 and 14.9 as well as 6.2 release schedule proposal and other topics. The episode will hit your favorite podcast app over the weekend.

\n\n\n\n\n\n\n\n
\n

🎙️ New episode: Gutenberg Changelog #78 -State of the Word, WordPress 6.2, Gutenberg 14.8 and 14.9 with Birgit Pauli-Haack and special guest Hector Prieto

\n
\n\n\n\n

Plugins, Themes, and Tools for #nocode site builders and owners

\n\n\n\n
\n\n\n\n

Sarah Gooding wrote about Block Protocol Announces New WordPress Plugin Coming in 2023 It will allow users to embed interactive blocks that are compatible with Gutenberg, and will include blocks for drawing, GitHub pull request overview, timer, calculation, and more. The plugin will also include new blocks powered by OpenAI DALL-E and GPT .

\n\n\n\n

The Block Protocol project is open source and designed to be an open protocol, and WordPress hopes to integrate more with it in the future.

\n\n\n\n
\n\n\n\n

In the latest WPTavern Jukebox podcast episode, Damon Cook, developer advocate at WPEngine, discussed with Nathan Wrigley the future of website styling in WordPress. Wrigley wrote in the introduction: ” Block-based themes are revolutionizing website styling. You’re going to be able to change any aspect of your website from the UI that you’re familiar with. The hope is that it’ll make styling more accessible to a wider audience.

\n\n\n\n

Damon talks about the fact that we’re in a period of flux right now. The documentation and tooling needed to work with website styles is maturing, but is by no means complete.”

\n\n\n\n
\n\n\n\n

Torsten Landsiedel scratched his personal itch and built the plugin Ignore block name in search, after finding that the WordPress built-in search included in the findings posts where the search keywords are in the HTML comments of blocks, and with that skews, the search result less relevant. It’s particular helpful when your blog is about working with the block editor or about content creation with WordPress. Landsiedel feels that the block editor makes the shortcomings of the built-in search feature worse because blocks contain full words, and not just HTML tags. It’s been a long-standing issue, that this plugin now solves.

\n\n\n\n
\n\n\n\n

209 Block Themes are now available in the WordPress repository with new submissions by Themeisle, sparklewpthemes, olivethemes, deothemes, sonalsinha21, Blockify, hamidxazad, WPZOOM.

\n\n\n\n\n\n\n\n

Ana Segota of Anariel Design also announced Yuna, a block theme for Nonprofits that comes with 100+ Design Patterns, you can add to your page with a simple drag and drop. Use built-in options to arrange and style them any way you want. It also includes built-in styles for the popular GiveWP donations plugin and is also ready to house your ecommerce store.

\n\n\n\n

Making Block Art

\n\n\n\n

Curious about some art behind Matt Mullenweg during State of the Word? Below are those pieces designed for the Museum of Block Art which represent the creativity that Gutenberg blocks inspire. Be sure to stop by and experience the museum’s digital interactive exhibit.

\n\n\n\n

You can see

\n\n\n\n\n\n\n\n\n\n\n\n

Anne McCarthy, instigator and curator of the Museum of Block Art, wrote an insightful blog post about how she approached making art with the Block Editor. Take a look Behind the scenes of creating art with WordPress.

\n\n\n\n

Chuck Grimmett has more examples of WP Block Art on his blog.

\n\n\n\n
\n\n\n\n

Rich Tabor and Courtney Portnoy discussed The creative side of blocks on WordPressTV. Rich Tabor walks the viewers through one of his block art creations. It’s quite inspiring to watch Tabor’s exploratory creative process using the block editor. I learned quite a few things about the power of the various color features: gradient, nested group blocks, and how to replace the theme’s primary and secondary colors for the whole site. You’ll also get an introduction to the Museum of Block Art, where Rich and other block artists showcase their creations. (also mentioned in GT 239)

\n\n\n\n

Form Plugins working with Blocks

\n\n\n\n

Two plugins emerged that take advantage of the block editor and its components and scripts so site owners and builders can use them to create forms.

\n\n\n\n

Munir Kamal, created a block integration for the popular CF7 Forms. It’s aptly names CF Blocks and available in the WordPress repository. He wrote in the description: “With CF7 Blocks, you can easily create and customize contact forms within the familiar block editor interface. No more fiddling with short codes or HTML – just drag and drop blocks to build your forms exactly how you want them.” Sounds spectacular, doesn’t it?

\n\n\n\n

In here article New CF7 Blocks Plugin Brings Blocks to Contact Form 7, Sarah Gooding, took a more in-depth look and shares her findings.

\n\n\n\n
\n\n\n\n

On Twitter, JR Tashjian developer at GoDaddy, introduced OmniForm, the next-generation Form Builder for your website. Sign up for early access now and be among the first to try it. The plan is to make the plugin available in the WordPress plugin directory at the end of January, with early access provided to users the week prior. Tashjian continues: “OmniForm embraces the block editor to the fullest extent and unlike any solution right now. The block editor is the future of editing in WordPress and building any kind of form will be no different from creating a post or page.” Tall order. Looking forward to doing some testing, too.

\n\n\n\n

Theme Development for Full Site Editing and Blocks

\n\n\n\n

Anne McCarthy has a new video up on YouTube: Building a site with WordPress 5.9 vs. WordPress 6.2 (in progress features) – To better show what’s changed with the Site Editor from when it was first introduced in WordPress 5.9, this video goes through both a demo of the original state and a brief look at what’s in place today and what’s to come, especially as 6.2 looks to wrap up much of the work around site editing/phase 2 of Gutenberg. Keep in mind that WordPress 6.2 is not out yet and much of what’s being shown is very much a work in progress with big opportunities to provide feedback along the way. Either way, I hope you enjoy taking a peak back and a look forward.

\n\n\n\n
\n\n\n\n\n

 “Keeping up with Gutenberg – Index 2022” 
A chronological list of the WordPress Make Blog posts from various teams involved in Gutenberg development: Design, Theme Review Team, Core Editor, Core JS, Core CSS, Test, and Meta team from Jan. 2021 on. Updated by yours truly. The index 2020 is here

\n\n\n\n\n

In this video tutorial, Jonathan Bossenger gives you an Introduction to theme.json. You will learn how the theme.js file works, and how you can control these settings and styles.

\n\n\n\n
\n\n\n\n

Daisy Olsen started a new Live Stream schedule and will show off Block Themes in WordPress every Friday at 10:30 am ET / 15:30 UTC on Twitch.

\n\n\n\n

The inaugural show took place Friday, January 6th, 2023 with the topic: Building a Block Theme. It’s a great opportunity to follow along with Daisy and ask questions along the way.

\n\n\n\n\n\n\n\n

Building Blocks and Tools for the Block editor.

\n\n\n\n

Munir Kamal takes you on a journey of From WordPress to the World: Intro to the Standalone Gutenberg Block Editor. In his new plugin, Kamal made the journey and found a few challenges along the way, overcame them and new put it all together for others to follow. Using the app ‘Isolated block editor, from the public repo, maintained by Automattic. Matt Mullenweg in the State of the Word emphasized that the block editor is also used outside of WordPress, with Tumblr, Day One app and with bbPress instance.

\n\n\n\n
\n\n\n\n

The team working on GiveWP went on a similar route on the revamp of the highly popular donation plugin. Post Status recently posted an article about that: The Future of GiveWP and the Block Editor

\n\n\n\n

GiveWP will hold a Town hall event about the new version on January 25th, 2023 at 10am PT / 18:00 UTC – in case someone is interested. Learn more Town Hall: GiveWP Design Mode and What’s Next for 3.0

\n\n\n\n


Kyle Johnson, JavaScript developer at GiveWP will present his talk: Using Gutenberg as a Development Foundation, Not Just a Block Builder at WordCamp Birmingham on February 4th, 2023. As far as we know, the talks will be recorded, but not livestreamed. So, they will show up on WordPress TV in the weeks after the WordCamp.

\n\n\n\n
\n\n\n\n

Mohammed Noufal of Hubspot wrote about How to Create Custom Blocks in WordPress, providing answers to the questions: why use a custom block, how to make Custom Block Templates and how to use custom blocks on your site.

\n\n\n\n
\n\n\n\n

Jonathan Bossenger‘s last section of his series: Let’s code: developing blocks without React!  is now also available on WordPress TV. Let’s code: developing blocks without React! – Review. If you followed along over the past few weeks, you would have learned to build a small WordPress block using plain (vanilla) JavaScript. In this session, we will review everything we’ve learned so far, by rebuilding the entire block from scratch.

\n\n\n\n

The other editions for the series are in order of broadcast/

\n\n\n\n\n\n\n\n\n

Need a plugin .zip from Gutenberg’s master branch?
Gutenberg Times provides daily build for testing and review.
Have you been using it? Hit reply and let me know.

\n\n\n\n

\"GitHub

\n\n\n\n\n

Upcoming WordPress events

\n\n\n\n

January 10, 2023 – 9:30 ET / 14:30 UTC
Hallway Hangout: Performance Considerations for Block Themes with Anne McCarthy

\n\n\n\n

January 11, 2023 – 5 pm ET / 22:00 UTC
Gutenberg Times Live Q & A: Layout, layout, layout
Panel discussion with Isabel Brison, Andrew Serong, Justin Tadlock and Birgit Pauli-Haack

\n\n\n\n

February 4 + 5, 2023
WordCamp Birmingham, AL

\n\n\n\n

February 17 – 19, 2023
WordCamp Asia 2023 

\n\n\n\n

Learn WordPress Online Meetups

\n\n\n\n

January 17, 2023 – 3pm / 20:00 UTC
Patterns, reusable blocks and block locking

\n\n\n\n

January 19, 2023 – 7 pm ET / 24:00 UTC
Let’s make custom templates in the Site Editor!

\n\n\n\n

January 31, 2023 – 3pm ET / 20:00 UTC
Creating a photography website with the block editor

\n\n\n\n
\n\n\n\n\n

Featured Image:

\n\n\n\n
\n\n\n\n

Don’t want to miss the next Weekend Edition?

\n\n\n\n

We hate spam, too and won’t give your email address to anyone except Mailchimp to send out our Weekend Edition

Thanks for subscribing.
\n\n\n\n
\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 07 Jan 2023 14:25:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Birgit Pauli-Haack\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:8:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Wed, 25 Jan 2023 13:14:29 GMT\";s:12:\"content-type\";s:8:\"text/xml\";s:13:\"last-modified\";s:29:\"Wed, 25 Jan 2023 13:00:35 GMT\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:16:\"content-encoding\";s:4:\"gzip\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20211220193300\";}','no'),(143,'_transient_timeout_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1674695670','no'),(144,'_transient_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1674652470','no'),(145,'_transient_timeout_dash_v2_88ae138922fe95674369b1cb3d215a2b','1674695670','no'),(146,'_transient_dash_v2_88ae138922fe95674369b1cb3d215a2b','','no'),(147,'recently_activated','a:0:{}','yes'),(148,'can_compress_scripts','1','no'),(149,'_site_transient_update_plugins','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1674652484;s:8:\"response\";a:0:{}s:12:\"translations\";a:0:{}s:9:\"no_update\";a:2:{s:19:\"akismet/akismet.php\";O:8:\"stdClass\":10:{s:2:\"id\";s:21:\"w.org/plugins/akismet\";s:4:\"slug\";s:7:\"akismet\";s:6:\"plugin\";s:19:\"akismet/akismet.php\";s:11:\"new_version\";s:5:\"5.0.2\";s:3:\"url\";s:38:\"https://wordpress.org/plugins/akismet/\";s:7:\"package\";s:56:\"https://downloads.wordpress.org/plugin/akismet.5.0.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:60:\"https://ps.w.org/akismet/assets/icon-256x256.png?rev=2818463\";s:2:\"1x\";s:60:\"https://ps.w.org/akismet/assets/icon-128x128.png?rev=2818463\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:61:\"https://ps.w.org/akismet/assets/banner-772x250.jpg?rev=479904\";}s:11:\"banners_rtl\";a:0:{}s:8:\"requires\";s:3:\"5.0\";}s:9:\"hello.php\";O:8:\"stdClass\":10:{s:2:\"id\";s:25:\"w.org/plugins/hello-dolly\";s:4:\"slug\";s:11:\"hello-dolly\";s:6:\"plugin\";s:9:\"hello.php\";s:11:\"new_version\";s:5:\"1.7.2\";s:3:\"url\";s:42:\"https://wordpress.org/plugins/hello-dolly/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/plugin/hello-dolly.1.7.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-256x256.jpg?rev=2052855\";s:2:\"1x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-128x128.jpg?rev=2052855\";}s:7:\"banners\";a:2:{s:2:\"2x\";s:67:\"https://ps.w.org/hello-dolly/assets/banner-1544x500.jpg?rev=2645582\";s:2:\"1x\";s:66:\"https://ps.w.org/hello-dolly/assets/banner-772x250.jpg?rev=2052855\";}s:11:\"banners_rtl\";a:0:{}s:8:\"requires\";s:3:\"4.6\";}}s:7:\"checked\";a:3:{s:19:\"akismet/akismet.php\";s:5:\"5.0.2\";s:19:\"datadog/datadog.php\";s:5:\"0.0.0\";s:9:\"hello.php\";s:5:\"1.7.2\";}}','no'),(152,'theme_mods_twentytwentythree','a:1:{s:18:\"custom_css_post_id\";i:-1;}','yes'),(157,'finished_updating_comment_type','1','yes'),(171,'_transient_timeout_global_styles_twentytwentythree','1674661327','no'),(172,'_transient_global_styles_twentytwentythree','body{--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--base: #ffffff;--wp--preset--color--contrast: #000000;--wp--preset--color--primary: #9DFF20;--wp--preset--color--secondary: #345C00;--wp--preset--color--tertiary: #F6F6F6;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--duotone--dark-grayscale: url(\'#wp-duotone-dark-grayscale\');--wp--preset--duotone--grayscale: url(\'#wp-duotone-grayscale\');--wp--preset--duotone--purple-yellow: url(\'#wp-duotone-purple-yellow\');--wp--preset--duotone--blue-red: url(\'#wp-duotone-blue-red\');--wp--preset--duotone--midnight: url(\'#wp-duotone-midnight\');--wp--preset--duotone--magenta-yellow: url(\'#wp-duotone-magenta-yellow\');--wp--preset--duotone--purple-green: url(\'#wp-duotone-purple-green\');--wp--preset--duotone--blue-orange: url(\'#wp-duotone-blue-orange\');--wp--preset--font-size--small: clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 0.24), 1rem);--wp--preset--font-size--medium: clamp(1rem, 1rem + ((1vw - 0.48rem) * 0.24), 1.125rem);--wp--preset--font-size--large: clamp(1.75rem, 1.75rem + ((1vw - 0.48rem) * 0.24), 1.875rem);--wp--preset--font-size--x-large: 2.25rem;--wp--preset--font-size--xx-large: clamp(4rem, 4rem + ((1vw - 0.48rem) * 11.538), 10rem);--wp--preset--font-family--dm-sans: \"DM Sans\", sans-serif;--wp--preset--font-family--ibm-plex-mono: \'IBM Plex Mono\', monospace;--wp--preset--font-family--inter: \"Inter\", sans-serif;--wp--preset--font-family--system-font: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif;--wp--preset--font-family--source-serif-pro: \"Source Serif Pro\", serif;--wp--preset--spacing--30: clamp(1.5rem, 5vw, 2rem);--wp--preset--spacing--40: clamp(1.8rem, 1.8rem + ((1vw - 0.48rem) * 2.885), 3rem);--wp--preset--spacing--50: clamp(2.5rem, 8vw, 4.5rem);--wp--preset--spacing--60: clamp(3.75rem, 10vw, 7rem);--wp--preset--spacing--70: clamp(5rem, 5.25rem + ((1vw - 0.48rem) * 9.096), 8rem);--wp--preset--spacing--80: clamp(7rem, 14vw, 11rem);}body { margin: 0;--wp--style--global--content-size: 650px;--wp--style--global--wide-size: 1200px; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :where([class*=\"wp-block-\"]:not(.alignfull):not([class*=\"__\"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*=\"wp-block-\"]:not(.alignfull):not([class*=\"__\"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 1.5rem; }body { --wp--style--block-gap: 1.5rem; }body .is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-flow > * + *{margin-block-start: 1.5rem;margin-block-end: 0;}body .is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-constrained > * + *{margin-block-start: 1.5rem;margin-block-end: 0;}body .is-layout-flex{gap: 1.5rem;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body{background-color: var(--wp--preset--color--base);color: var(--wp--preset--color--contrast);font-family: var(--wp--preset--font-family--system-font);font-size: var(--wp--preset--font-size--medium);line-height: 1.6;--wp--style--root--padding-top: var(--wp--preset--spacing--40);--wp--style--root--padding-right: var(--wp--preset--spacing--30);--wp--style--root--padding-bottom: var(--wp--preset--spacing--40);--wp--style--root--padding-left: var(--wp--preset--spacing--30);}a:where(:not(.wp-element-button)){color: var(--wp--preset--color--contrast);text-decoration: underline;}a:where(:not(.wp-element-button)):hover{text-decoration: none;}a:where(:not(.wp-element-button)):focus{text-decoration: underline dashed;}a:where(:not(.wp-element-button)):active{color: var(--wp--preset--color--secondary);text-decoration: none;}h1, h2, h3, h4, h5, h6{font-weight: 400;line-height: 1.4;}h1{font-size: clamp(2.719rem, 2.719rem + ((1vw - 0.48rem) * 1.742), 3.625rem);line-height: 1.2;}h2{font-size: clamp(2.625rem, calc(2.625rem + ((1vw - 0.48rem) * 8.4135)), 3.25rem);line-height: 1.2;}h3{font-size: var(--wp--preset--font-size--x-large);}h4{font-size: var(--wp--preset--font-size--large);}h5{font-size: var(--wp--preset--font-size--medium);font-weight: 700;text-transform: uppercase;}h6{font-size: var(--wp--preset--font-size--medium);text-transform: uppercase;}.wp-element-button, .wp-block-button__link{background-color: var(--wp--preset--color--primary);border-radius: 0;border-width: 0;color: var(--wp--preset--color--contrast);font-family: inherit;font-size: inherit;line-height: inherit;padding: calc(0.667em + 2px) calc(1.333em + 2px);text-decoration: none;}.wp-element-button:visited, .wp-block-button__link:visited{color: var(--wp--preset--color--contrast);}.wp-element-button:hover, .wp-block-button__link:hover{background-color: var(--wp--preset--color--contrast);color: var(--wp--preset--color--base);}.wp-element-button:focus, .wp-block-button__link:focus{background-color: var(--wp--preset--color--contrast);color: var(--wp--preset--color--base);}.wp-element-button:active, .wp-block-button__link:active{background-color: var(--wp--preset--color--secondary);color: var(--wp--preset--color--base);}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-base-color{color: var(--wp--preset--color--base) !important;}.has-contrast-color{color: var(--wp--preset--color--contrast) !important;}.has-primary-color{color: var(--wp--preset--color--primary) !important;}.has-secondary-color{color: var(--wp--preset--color--secondary) !important;}.has-tertiary-color{color: var(--wp--preset--color--tertiary) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-background-color{background-color: var(--wp--preset--color--base) !important;}.has-contrast-background-color{background-color: var(--wp--preset--color--contrast) !important;}.has-primary-background-color{background-color: var(--wp--preset--color--primary) !important;}.has-secondary-background-color{background-color: var(--wp--preset--color--secondary) !important;}.has-tertiary-background-color{background-color: var(--wp--preset--color--tertiary) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-border-color{border-color: var(--wp--preset--color--base) !important;}.has-contrast-border-color{border-color: var(--wp--preset--color--contrast) !important;}.has-primary-border-color{border-color: var(--wp--preset--color--primary) !important;}.has-secondary-border-color{border-color: var(--wp--preset--color--secondary) !important;}.has-tertiary-border-color{border-color: var(--wp--preset--color--tertiary) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-dm-sans-font-family{font-family: var(--wp--preset--font-family--dm-sans) !important;}.has-ibm-plex-mono-font-family{font-family: var(--wp--preset--font-family--ibm-plex-mono) !important;}.has-inter-font-family{font-family: var(--wp--preset--font-family--inter) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-source-serif-pro-font-family{font-family: var(--wp--preset--font-family--source-serif-pro) !important;}','no'),(173,'_transient_timeout_global_styles_svg_filters_twentytwentythree','1674661327','no'),(174,'_transient_global_styles_svg_filters_twentytwentythree','','no'); +INSERT INTO `wp_options` VALUES (1,'siteurl','http://localhost','yes'),(2,'home','http://localhost','yes'),(3,'blogname','Datadog Test WP DB','yes'),(4,'blogdescription','','yes'),(5,'users_can_register','0','yes'),(6,'admin_email','test@gmail.com','yes'),(7,'start_of_week','1','yes'),(8,'use_balanceTags','0','yes'),(9,'use_smilies','1','yes'),(10,'require_name_email','1','yes'),(11,'comments_notify','1','yes'),(12,'posts_per_rss','10','yes'),(13,'rss_use_excerpt','0','yes'),(14,'mailserver_url','mail.example.com','yes'),(15,'mailserver_login','login@example.com','yes'),(16,'mailserver_pass','password','yes'),(17,'mailserver_port','110','yes'),(18,'default_category','1','yes'),(19,'default_comment_status','open','yes'),(20,'default_ping_status','open','yes'),(21,'default_pingback_flag','0','yes'),(22,'posts_per_page','10','yes'),(23,'date_format','F j, Y','yes'),(24,'time_format','g:i a','yes'),(25,'links_updated_date_format','F j, Y g:i a','yes'),(26,'comment_moderation','0','yes'),(27,'moderation_notify','1','yes'),(28,'permalink_structure','/%postname%','yes'),(29,'rewrite_rules','a:93:{s:11:\"^wp-json/?$\";s:22:\"index.php?rest_route=/\";s:14:\"^wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:21:\"^index.php/wp-json/?$\";s:22:\"index.php?rest_route=/\";s:24:\"^index.php/wp-json/(.*)?\";s:33:\"index.php?rest_route=/$matches[1]\";s:17:\"^wp-sitemap\\\\.xml$\";s:23:\"index.php?sitemap=index\";s:17:\"^wp-sitemap\\\\.xsl$\";s:36:\"index.php?sitemap-stylesheet=sitemap\";s:23:\"^wp-sitemap-index\\\\.xsl$\";s:34:\"index.php?sitemap-stylesheet=index\";s:48:\"^wp-sitemap-([a-z]+?)-([a-z\\\\d_-]+?)-(\\\\d+?)\\\\.xml$\";s:75:\"index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]\";s:34:\"^wp-sitemap-([a-z]+?)-(\\\\d+?)\\\\.xml$\";s:47:\"index.php?sitemap=$matches[1]&paged=$matches[2]\";s:47:\"category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:42:\"category/(.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:52:\"index.php?category_name=$matches[1]&feed=$matches[2]\";s:23:\"category/(.+?)/embed/?$\";s:46:\"index.php?category_name=$matches[1]&embed=true\";s:35:\"category/(.+?)/page/?([0-9]{1,})/?$\";s:53:\"index.php?category_name=$matches[1]&paged=$matches[2]\";s:17:\"category/(.+?)/?$\";s:35:\"index.php?category_name=$matches[1]\";s:44:\"tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:39:\"tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?tag=$matches[1]&feed=$matches[2]\";s:20:\"tag/([^/]+)/embed/?$\";s:36:\"index.php?tag=$matches[1]&embed=true\";s:32:\"tag/([^/]+)/page/?([0-9]{1,})/?$\";s:43:\"index.php?tag=$matches[1]&paged=$matches[2]\";s:14:\"tag/([^/]+)/?$\";s:25:\"index.php?tag=$matches[1]\";s:45:\"type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:40:\"type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?post_format=$matches[1]&feed=$matches[2]\";s:21:\"type/([^/]+)/embed/?$\";s:44:\"index.php?post_format=$matches[1]&embed=true\";s:33:\"type/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?post_format=$matches[1]&paged=$matches[2]\";s:15:\"type/([^/]+)/?$\";s:33:\"index.php?post_format=$matches[1]\";s:12:\"robots\\\\.txt$\";s:18:\"index.php?robots=1\";s:13:\"favicon\\\\.ico$\";s:19:\"index.php?favicon=1\";s:48:\".*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\\\\.php$\";s:18:\"index.php?feed=old\";s:20:\".*wp-app\\\\.php(/.*)?$\";s:19:\"index.php?error=403\";s:18:\".*wp-register.php$\";s:23:\"index.php?register=true\";s:32:\"feed/(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:27:\"(feed|rdf|rss|rss2|atom)/?$\";s:27:\"index.php?&feed=$matches[1]\";s:8:\"embed/?$\";s:21:\"index.php?&embed=true\";s:20:\"page/?([0-9]{1,})/?$\";s:28:\"index.php?&paged=$matches[1]\";s:41:\"comments/feed/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:36:\"comments/(feed|rdf|rss|rss2|atom)/?$\";s:42:\"index.php?&feed=$matches[1]&withcomments=1\";s:17:\"comments/embed/?$\";s:21:\"index.php?&embed=true\";s:44:\"search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:39:\"search/(.+)/(feed|rdf|rss|rss2|atom)/?$\";s:40:\"index.php?s=$matches[1]&feed=$matches[2]\";s:20:\"search/(.+)/embed/?$\";s:34:\"index.php?s=$matches[1]&embed=true\";s:32:\"search/(.+)/page/?([0-9]{1,})/?$\";s:41:\"index.php?s=$matches[1]&paged=$matches[2]\";s:14:\"search/(.+)/?$\";s:23:\"index.php?s=$matches[1]\";s:47:\"author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:42:\"author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:50:\"index.php?author_name=$matches[1]&feed=$matches[2]\";s:23:\"author/([^/]+)/embed/?$\";s:44:\"index.php?author_name=$matches[1]&embed=true\";s:35:\"author/([^/]+)/page/?([0-9]{1,})/?$\";s:51:\"index.php?author_name=$matches[1]&paged=$matches[2]\";s:17:\"author/([^/]+)/?$\";s:33:\"index.php?author_name=$matches[1]\";s:69:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:64:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:80:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]\";s:45:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$\";s:74:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true\";s:57:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:81:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]\";s:39:\"([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$\";s:63:\"index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]\";s:56:\"([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:51:\"([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$\";s:64:\"index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]\";s:32:\"([0-9]{4})/([0-9]{1,2})/embed/?$\";s:58:\"index.php?year=$matches[1]&monthnum=$matches[2]&embed=true\";s:44:\"([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$\";s:65:\"index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]\";s:26:\"([0-9]{4})/([0-9]{1,2})/?$\";s:47:\"index.php?year=$matches[1]&monthnum=$matches[2]\";s:43:\"([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:38:\\([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?year=$matches[1]&feed=$matches[2]\";s:19:\"([0-9]{4})/embed/?$\";s:37:\"index.php?year=$matches[1]&embed=true\";s:31:\"([0-9]{4})/page/?([0-9]{1,})/?$\";s:44:\"index.php?year=$matches[1]&paged=$matches[2]\";s:13:\"([0-9]{4})/?$\";s:26:\"index.php?year=$matches[1]\";s:27:\".?.+?/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\".?.+?/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\\.?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\".?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\".?.+?/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"(.?.+?)/embed/?$\";s:41:\"index.php?pagename=$matches[1]&embed=true\";s:20:\"(.?.+?)/trackback/?$\";s:35:\"index.php?pagename=$matches[1]&tb=1\";s:40:\"(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:35:\"(.?.+?)/(feed|rdf|rss|rss2|atom)/?$\";s:47:\"index.php?pagename=$matches[1]&feed=$matches[2]\";s:28:\"(.?.+?)/page/?([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&paged=$matches[2]\";s:35:\"(.?.+?)/comment-page-([0-9]{1,})/?$\";s:48:\"index.php?pagename=$matches[1]&cpage=$matches[2]\";s:24:\"(.?.+?)(?:/([0-9]+))?/?$\";s:47:\"index.php?pagename=$matches[1]&page=$matches[2]\";s:27:\"[^/]+/attachment/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:37:\"[^/]+/attachment/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:57:\"[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:52:\"[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:33:\"[^/]+/attachment/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";s:16:\"([^/]+)/embed/?$\";s:37:\"index.php?name=$matches[1]&embed=true\";s:20:\"([^/]+)/trackback/?$\";s:31:\"index.php?name=$matches[1]&tb=1\";s:40:\"([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:35:\"([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:43:\"index.php?name=$matches[1]&feed=$matches[2]\";s:28:\"([^/]+)/page/?([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&paged=$matches[2]\";s:35:\"([^/]+)/comment-page-([0-9]{1,})/?$\";s:44:\"index.php?name=$matches[1]&cpage=$matches[2]\";s:24:\"([^/]+)(?:/([0-9]+))?/?$\";s:43:\"index.php?name=$matches[1]&page=$matches[2]\";s:16:\"[^/]+/([^/]+)/?$\";s:32:\"index.php?attachment=$matches[1]\";s:26:\"[^/]+/([^/]+)/trackback/?$\";s:37:\"index.php?attachment=$matches[1]&tb=1\";s:46:\"[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$\";s:49:\"index.php?attachment=$matches[1]&feed=$matches[2]\";s:41:\"[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$\";s:50:\"index.php?attachment=$matches[1]&cpage=$matches[2]\";s:22:\"[^/]+/([^/]+)/embed/?$\";s:43:\"index.php?attachment=$matches[1]&embed=true\";}','yes'),(30,'hack_file','0','yes'),(31,'blog_charset','UTF-8','yes'),(32,'moderation_keys','','no'),(33,'active_plugins','a:1:{i:0;s:19:\"datadog/datadog.php\";}','yes'),(34,'category_base','','yes'),(35,'ping_sites','http://rpc.pingomatic.com/','yes'),(36,'comment_max_links','2','yes'),(37,'gmt_offset','0','yes'),(38,'default_email_category','1','yes'),(39,'recently_edited','','no'),(40,'template','twentytwentythree','yes'),(41,'stylesheet','twentytwentythree','yes'),(42,'comment_registration','0','yes'),(43,'html_type','text/html','yes'),(44,'use_trackback','0','yes'),(45,'default_role','subscriber','yes'),(46,'db_version','53496','yes'),(47,'uploads_use_yearmonth_folders','1','yes'),(48,'upload_path','','yes'),(49,'blog_public','0','yes'),(50,'default_link_category','2','yes'),(51,'show_on_front','posts','yes'),(52,'tag_base','','yes'),(53,'show_avatars','1','yes'),(54,'avatar_rating','G','yes'),(55,'upload_url_path','','yes'),(56,'thumbnail_size_w','150','yes'),(57,'thumbnail_size_h','150','yes'),(58,'thumbnail_crop','1','yes'),(59,'medium_size_w','300','yes'),(60,'medium_size_h','300','yes'),(61,'avatar_default','mystery','yes'),(62,'large_size_w','1024','yes'),(63,'large_size_h','1024','yes'),(64,'image_default_link_type','none','yes'),(65,'image_default_size','','yes'),(66,'image_default_align','','yes'),(67,'close_comments_for_old_posts','0','yes'),(68,'close_comments_days_old','14','yes'),(69,'thread_comments','1','yes'),(70,'thread_comments_depth','5','yes'),(71,'page_comments','0','yes'),(72,'comments_per_page','50','yes'),(73,'default_comments_page','newest','yes'),(74,'comment_order','asc','yes'),(75,'sticky_posts','a:0:{}','yes'),(76,'widget_categories','a:0:{}','yes'),(77,'widget_text','a:0:{}','yes'),(78,'widget_rss','a:0:{}','yes'),(79,'uninstall_plugins','a:0:{}','no'),(80,'timezone_string','','yes'),(81,'page_for_posts','0','yes'),(82,'page_on_front','0','yes'),(83,'default_post_format','0','yes'),(84,'link_manager_enabled','0','yes'),(85,'finished_splitting_shared_terms','1','yes'),(86,'site_icon','0','yes'),(87,'medium_large_size_w','768','yes'),(88,'medium_large_size_h','0','yes'),(89,'wp_page_for_privacy_policy','3','yes'),(90,'show_comments_cookies_opt_in','1','yes'),(91,'admin_email_lifespan','1690204371','yes'),(92,'disallowed_keys','','no'),(93,'comment_previously_approved','1','yes'),(94,'auto_plugin_theme_update_emails','a:0:{}','no'),(95,'auto_update_core_dev','enabled','yes'),(96,'auto_update_core_minor','enabled','yes'),(97,'auto_update_core_major','enabled','yes'),(98,'wp_force_deactivated_plugins','a:0:{}','yes'),(99,'initial_db_version','53496','yes'),(100,'wp_user_roles','a:5:{s:13:\"administrator\";a:2:{s:4:\"name\";s:13:\"Administrator\";s:12:\"capabilities\";a:61:{s:13:\"switch_themes\";b:1;s:11:\"edit_themes\";b:1;s:16:\"activate_plugins\";b:1;s:12:\"edit_plugins\";b:1;s:10:\"edit_users\";b:1;s:10:\"edit_files\";b:1;s:14:\"manage_options\";b:1;s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:6:\"import\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:8:\"level_10\";b:1;s:7:\"level_9\";b:1;s:7:\"level_8\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;s:12:\"delete_users\";b:1;s:12:\"create_users\";b:1;s:17:\"unfiltered_upload\";b:1;s:14:\"edit_dashboard\";b:1;s:14:\"update_plugins\";b:1;s:14:\"delete_plugins\";b:1;s:15:\"install_plugins\";b:1;s:13:\"update_themes\";b:1;s:14:\"install_themes\";b:1;s:11:\"update_core\";b:1;s:10:\"list_users\";b:1;s:12:\"remove_users\";b:1;s:13:\"promote_users\";b:1;s:18:\"edit_theme_options\";b:1;s:13:\"delete_themes\";b:1;s:6:\"export\";b:1;}}s:6:\"editor\";a:2:{s:4:\"name\";s:6:\"Editor\";s:12:\"capabilities\";a:34:{s:17:\"moderate_comments\";b:1;s:17:\"manage_categories\";b:1;s:12:\"manage_links\";b:1;s:12:\"upload_files\";b:1;s:15:\"unfiltered_html\";b:1;s:10:\"edit_posts\";b:1;s:17:\"edit_others_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:10:\"edit_pages\";b:1;s:4:\"read\";b:1;s:7:\"level_7\";b:1;s:7:\"level_6\";b:1;s:7:\"level_5\";b:1;s:7:\"level_4\";b:1;s:7:\"level_3\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:17:\"edit_others_pages\";b:1;s:20:\"edit_published_pages\";b:1;s:13:\"publish_pages\";b:1;s:12:\"delete_pages\";b:1;s:19:\"delete_others_pages\";b:1;s:22:\"delete_published_pages\";b:1;s:12:\"delete_posts\";b:1;s:19:\"delete_others_posts\";b:1;s:22:\"delete_published_posts\";b:1;s:20:\"delete_private_posts\";b:1;s:18:\"edit_private_posts\";b:1;s:18:\"read_private_posts\";b:1;s:20:\"delete_private_pages\";b:1;s:18:\"edit_private_pages\";b:1;s:18:\"read_private_pages\";b:1;}}s:6:\"author\";a:2:{s:4:\"name\";s:6:\"Author\";s:12:\"capabilities\";a:10:{s:12:\"upload_files\";b:1;s:10:\"edit_posts\";b:1;s:20:\"edit_published_posts\";b:1;s:13:\"publish_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_2\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;s:22:\"delete_published_posts\";b:1;}}s:11:\"contributor\";a:2:{s:4:\"name\";s:11:\"Contributor\";s:12:\"capabilities\";a:5:{s:10:\"edit_posts\";b:1;s:4:\"read\";b:1;s:7:\"level_1\";b:1;s:7:\"level_0\";b:1;s:12:\"delete_posts\";b:1;}}s:10:\"subscriber\";a:2:{s:4:\"name\";s:10:\"Subscriber\";s:12:\"capabilities\";a:2:{s:4:\"read\";b:1;s:7:\"level_0\";b:1;}}}','yes'),(101,'fresh_site','1','yes'),(102,'user_count','1','no'),(103,'widget_block','a:6:{i:2;a:1:{s:7:\"content\";s:19:\"\";}i:3;a:1:{s:7:\"content\";s:154:\"

Recent Posts

\";}i:4;a:1:{s:7:\"content\";s:227:\"

Recent Comments

\";}i:5;a:1:{s:7:\"content\";s:146:\"

Archives

\";}i:6;a:1:{s:7:\"content\";s:150:\"

Categories

\";}s:12:\"_multiwidget\";i:1;}','yes'),(104,'sidebars_widgets','a:4:{s:19:\"wp_inactive_widgets\";a:0:{}s:9:\"sidebar-1\";a:3:{i:0;s:7:\"block-2\";i:1;s:7:\"block-3\";i:2;s:7:\"block-4\";}s:9:\"sidebar-2\";a:2:{i:0;s:7:\"block-5\";i:1;s:7:\"block-6\";}s:13:\"array_version\";i:3;}','yes'),(105,'cron','a:7:{i:1674663182;a:1:{s:34:\"wp_privacy_delete_old_export_files\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"hourly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:3600;}}}i:1674695582;a:4:{s:18:\"wp_https_detection\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_version_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:17:\"wp_update_plugins\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}s:16:\"wp_update_themes\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674695658;a:1:{s:21:\"wp_update_user_counts\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:10:\"twicedaily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:43200;}}}i:1674738782;a:2:{s:30:\"wp_site_health_scheduled_check\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:6:\"weekly\";s:4:\"args\";a:0:{}s:8:\"interval\";i:604800;}}s:32:\"recovery_mode_clean_expired_keys\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738858;a:2:{s:19:\"wp_scheduled_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}s:25:\"delete_expired_transients\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}i:1674738859;a:1:{s:30:\"wp_scheduled_auto_draft_delete\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:3:{s:8:\"schedule\";s:5:\"daily\";s:4:\"args\";a:0:{}s:8:\"interval\";i:86400;}}}s:7:\"version\";i:2;}','yes'),(106,'widget_pages','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(107,'widget_calendar','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(108,'widget_archives','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(109,'widget_media_audio','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(110,'widget_media_image','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(111,'widget_media_gallery','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(112,'widget_media_video','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(113,'widget_meta','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(114,'widget_search','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(115,'widget_recent-posts','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(116,'widget_recent-comments','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(117,'widget_tag_cloud','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(118,'widget_nav_menu','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(119,'widget_custom_html','a:1:{s:12:\"_multiwidget\";i:1;}','yes'),(121,'recovery_keys','a:0:{}','yes'),(122,'https_detection_errors','a:1:{s:20:\"https_request_failed\";a:1:{i:0;s:21:\"HTTPS request failed.\";}}','yes'),(123,'_site_transient_update_core','O:8:\"stdClass\":4:{s:7:\"updates\";a:1:{i:0;O:8:\"stdClass\":10:{s:8:\"response\";s:6:\"latest\";s:8:\"download\";s:59:\"https://downloads.wordpress.org/release/wordpress-6.1.1.zip\";s:6:\"locale\";s:5:\"en_US\";s:8:\"packages\";O:8:\"stdClass\":5:{s:4:\"full\";s:59:\"https://downloads.wordpress.org/release/wordpress-6.1.1.zip\";s:10:\"no_content\";s:70:\"https://downloads.wordpress.org/release/wordpress-6.1.1-no-content.zip\";s:11:\"new_bundled\";s:71:\"https://downloads.wordpress.org/release/wordpress-6.1.1-new-bundled.zip\";s:7:\"partial\";s:0:\"\";s:8:\"rollback\";s:0:\"\";}s:7:\"current\";s:5:\"6.1.1\";s:7:\"version\";s:5:\"6.1.1\";s:11:\"php_version\";s:6:\"5.6.20\";s:13:\"mysql_version\";s:3:\"5.0\";s:11:\"new_bundled\";s:3:\"6.1\";s:15:\"partial_version\";s:0:\"\";}}s:12:\"last_checked\";i:1674652480;s:15:\"version_checked\";s:5:\"6.1.1\";s:12:\"translations\";a:0:{}}','no'),(126,'_site_transient_timeout_theme_roots','1674654196','no'),(127,'_site_transient_theme_roots','a:3:{s:15:\"twentytwentyone\";s:7:\"/themes\";s:17:\"twentytwentythree\";s:7:\"/themes\";s:15:\"twentytwentytwo\";s:7:\"/themes\";}','no'),(128,'_site_transient_update_themes','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1674652481;s:7:\"checked\";a:3:{s:15:\"twentytwentyone\";s:3:\"1.7\";s:17:\"twentytwentythree\";s:3:\"1.0\";s:15:\"twentytwentytwo\";s:3:\"1.3\";}s:8:\"response\";a:0:{}s:9:\"no_update\";a:3:{s:15:\"twentytwentyone\";a:6:{s:5:\"theme\";s:15:\"twentytwentyone\";s:11:\"new_version\";s:3:\"1.7\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentytwentyone/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentytwentyone.1.7.zip\";s:8:\"requires\";s:3:\"5.3\";s:12:\"requires_php\";s:3:\"5.6\";}s:17:\"twentytwentythree\";a:6:{s:5:\"theme\";s:17:\"twentytwentythree\";s:11:\"new_version\";s:3:\"1.0\";s:3:\"url\";s:47:\"https://wordpress.org/themes/twentytwentythree/\";s:7:\"package\";s:63:\"https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip\";s:8:\"requires\";s:3:\"6.1\";s:12:\"requires_php\";s:3:\"5.6\";}s:15:\"twentytwentytwo\";a:6:{s:5:\"theme\";s:15:\"twentytwentytwo\";s:11:\"new_version\";s:3:\"1.3\";s:3:\"url\";s:45:\"https://wordpress.org/themes/twentytwentytwo/\";s:7:\"package\";s:61:\"https://downloads.wordpress.org/theme/twentytwentytwo.1.3.zip\";s:8:\"requires\";s:3:\"5.9\";s:12:\"requires_php\";s:3:\"5.6\";}}s:12:\"translations\";a:0:{}}','no'),(130,'_site_transient_timeout_browser_894dc60a4e148f4652615ed246d3e298','1675257258','no'),(131,'_site_transient_browser_894dc60a4e148f4652615ed246d3e298','a:10:{s:4:\"name\";s:6:\"Chrome\";s:7:\"version\";s:9:\"109.0.0.0\";s:8:\"platform\";s:9:\"Macintosh\";s:10:\"update_url\";s:29:\"https://www.google.com/chrome\";s:7:\"img_src\";s:43:\"http://s.w.org/images/browsers/chrome.png?1\";s:11:\"img_src_ssl\";s:44:\"https://s.w.org/images/browsers/chrome.png?1\";s:15:\"current_version\";s:2:\"18\";s:7:\"upgrade\";b:0;s:8:\"insecure\";b:0;s:6:\"mobile\";b:0;}','no'),(132,'_site_transient_timeout_php_check_ce267f3653936506950ae9448202043a','1675257259','no'),(133,'_site_transient_php_check_ce267f3653936506950ae9448202043a','a:5:{s:19:\"recommended_version\";s:3:\"7.4\";s:15:\"minimum_version\";s:6:\"5.6.20\";s:12:\"is_supported\";b:1;s:9:\"is_secure\";b:1;s:13:\"is_acceptable\";b:1;}','no'),(135,'_site_transient_timeout_community-events-1de8873aa0984c1dbee47981d08b0def','1674695662','no'),(136,'_site_transient_community-events-1de8873aa0984c1dbee47981d08b0def','a:4:{s:9:\"sandboxed\";b:0;s:5:\"error\";N;s:8:\"location\";a:1:{s:2:\"ip\";s:10:\"172.21.0.0\";}s:6:\"events\";a:1:{i:0;a:10:{s:4:\"type\";s:8:\"wordcamp\";s:5:\"title\";s:15:\"WordCamp Torino\";s:3:\"url\";s:33:\"https://torino.wordcamp.org/2023/\";s:6:\"meetup\";N;s:10:\"meetup_url\";N;s:4:\"date\";s:19:\"2023-04-14 00:00:00\";s:8:\"end_date\";s:19:\"2023-04-15 00:00:00\";s:20:\"start_unix_timestamp\";i:1681423200;s:18:\"end_unix_timestamp\";i:1681509600;s:8:\"location\";a:4:{s:8:\"location\";s:12:\"Turin, Italy\";s:7:\"country\";s:2:\"IT\";s:8:\"latitude\";d:45.050238;s:9:\"longitude\";d:7.669286;}}}}','no'),(137,'_transient_timeout_feed_9bbd59226dc36b9b26cd43f15694c5c3','1674695664','no'),(138,'_transient_feed_9bbd59226dc36b9b26cd43f15694c5c3','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:52:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:8:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"The latest news about WordPress and the WordPress community\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:13:\"lastBuildDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 11:56:32 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"en-US\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"generator\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"https://wordpress.org/?v=6.2-alpha-55136\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:5:\"image\";a:1:{i:0;a:6:{s:4:\"data\";s:11:\"\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:3:\"url\";a:1:{i:0;a:5:{s:4:\"data\";s:29:\"https://s.w.org/favicon.ico?2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"WordPress News\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:26:\"https://wordpress.org/news\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:5:\"width\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"32\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:6:\"height\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"32\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}s:4:\"item\";a:10:{i:0;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"The Month in WordPress – December 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"https://wordpress.org/news/2023/01/the-month-in-wordpress-december-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:18:\"month in wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14191\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:339:\"Last month at State of the Word, WordPress Executive Director Josepha Haden Chomphosy shared some opening thoughts on “Why WordPress” and the Four Freedoms of open source. In this recent letter, she expands on her vision for the WordPress open source project as it prepares for the third phase of Gutenberg: “We are now, as […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"rmartinezduque\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:12820:\"\n

Last month at State of the Word, WordPress Executive Director Josepha Haden Chomphosy shared some opening thoughts on “Why WordPress” and the Four Freedoms of open source. In this recent letter, she expands on her vision for the WordPress open source project as it prepares for the third phase of Gutenberg:

\n\n\n\n
\n

“We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.”

\nJosepha Haden Chomphosy
\n\n\n\n

December brought with it a time for reflection—a time to look back, celebrate, and start planning new projects. Read on to find out what 2023 holds for WordPress so far.

\n\n\n\n
\n\n\n\n

WordPress is turning 20!

\n\n\n\n

2023 marks the 20th anniversary of WordPress’ launch. The project has come a long way since the first release as it continues to advance its mission to democratize publishing. From its beginnings as a blogging platform to a world-leading open source CMS powering over 40% of websites.

\n\n\n\n

Join the WordPress community in celebrating this important milestone. As the anniversary date approaches, there will be events, commemorative swag, and more.

\n\n\n\n
\n

Stay tuned for updates.

\n
\n\n\n\n
\n\n\n\n

WordPress 6.2 is scheduled for March 28, 2023

\n\n\n\n

Work on WordPress 6.2, the first major release of 2023, is already underway. It is expected to launch on March 28, 2023, and will include up to Gutenberg 15.1 for a total of 10 Gutenberg releases.

\n\n\n\n

The proposed schedule includes four Beta releases to accommodate the first WordCamp Asia and avoid having major release milestones very close to this event.

\n\n\n\n
\n

Read more about the 6.2 schedule and release team.

\n
\n\n\n\n
\n\n\n\n

What’s new in Gutenberg

\n\n\n\n

Two new versions of Gutenberg have shipped in the last month:

\n\n\n\n
    \n
  • Gutenberg 14.8 was released on December 21, 2022. This version features a reorganized Site Editor interface with a Browse Mode that facilitates navigation through templates and template parts. In addition, it includes the ability to add custom CSS via the Style panel and a Style Book that provides an overview of all block styles in a centralized location.
  • \n\n\n\n
  • Gutenberg 14.9 became available for download on January 4, 2023. It introduces a new “Push changes to Global Styles” button in the Site Editor, which allows users to apply individual block style changes to all blocks of that type across their site. Other features include typography support for the Page List block, and the ability to import sidebar widgets into a template part when transitioning from a classic theme.
  • \n
\n\n\n\n
\n

Learn how Gutenberg’s latest releases are advancing the Site Editor experience to be more intuitive and scalable.

\n
\n\n\n\n
\n\n\n\n

Team updates: WordPress big picture goals, new Incident Response Team, and more

\n\n\n\n\n\n\n\n
\n

Check out the 2022 State of the Word Q&A post, which answers submitted questions that Matt could not address at the live event.

\n
\n\n\n\n
\n\n\n\n

Feedback & testing requests

\n\n\n\n\n\n\n\n
\n

Have thoughts for improving the Five for the Future contributor experience? This post calls for ideas on how this initiative can better support the project and the people behind it.

\n
\n\n\n\n
\n\n\n\n

WordPress events updates

\n\n\n\n\n\n\n\n
\n

Would you like to be a speaker at WordCamp Europe 2023? Submit your application by the first week of February.

\n
\n\n\n\n
\n\n\n\n
\n\n\n\n

Have a story we should include in the next issue of The Month in WordPress? Fill out this quick form to let us know.

\n\n\n\n

The following folks contributed to this edition of The Month in WordPress: @cbringmann, @laurlittle, @rmartinezduque.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"WP Briefing: Episode 47: Letter from the Executive Director\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/episode-47-letter-from-the-executive-director/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:11:\"wp-briefing\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14175\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:114:\"Hear from WordPress Executive Director Josepha Haden Chomphosy on her vision for the open source project in 2023. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:60:\"https://wordpress.org/news/files/2023/01/WP-Briefing-047.mp3\";s:6:\"length\";s:1:\"0\";s:4:\"type\";s:0:\"\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:8912:\"\n

On episode forty-seven of the WordPress Briefing podcast, Executive Director Josepha Haden Chomphosy shares her vision and current thinking for the WordPress open source project in 2023. Rather read it? The full letter is also available.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

Show Notes

\n\n\n\n

make.WordPress.org/core
Join the 6.2 Release!
Submit Topics for the Community Summit!

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress Briefing, the podcast where you can catch quick explanations of the ideas behind the WordPress open source project, some insight into the community that supports it, and get a small list of big things coming up in the next two weeks. I’m your host, Josepha Haden Chomphosy. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:40] 

\n\n\n\n

Last month at State of the Word, I shared some opening thoughts about why WordPress. For me, this is an easy question, and the hardest part is always knowing which lens to answer through. Though I always focus on the philosophical parts of the answer, I know that I often speak as an advocate for many types of WordPressers.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:00] 

\n\n\n\n

So as we prepare ourselves for the start of a new year, I have a few additional thoughts that I’d like to share with you, my WordPress community, to take into the year with you. 

\n\n\n\n

Firstly, the Four Freedoms. If you have already listened to State of the Word, you have heard my take on the philosophical side of open source and the freedoms it provides.

\n\n\n\n

But if you didn’t, then the TL;DR on that is that open source provides protections and freedoms to creators on the web that I really think should just be a given. But there are a couple of other things about the Four Freedoms, and especially the way that WordPress does this kind of open source-y thing that I think are worth noting as well.

\n\n\n\n

One of those things is that WordPress entrepreneurs, those who are providing services or designing sites, building applications, they have proven that open source provides an ethical framework for conducting business. No one ever said that you aren’t allowed to build a business using free and open source software, and I am regularly heartened by the way that successful companies and freelancers make the effort to pay forward what they can.

\n\n\n\n

[Josepha Haden Chomphosy 00:02:02]

\n\n\n\n

Not always for the sole benefit of WordPress, of course, but often for the general benefit of folks who are also learning how to be entrepreneurs or how to kind of navigate our ecosystem. And the other thing that I love about the Four Freedoms and the way that WordPress does it is that leaders in the WordPress community, no matter where they are leading from, have shown that open source ideals can be applied to the way we work with one another and show up for one another.

\n\n\n\n

As a community, we tend to approach solution gathering as an us-versus-the-problem exercise, which not only makes our solutions better, it also makes our community stronger. 

\n\n\n\n

As I have witnessed all of these things work together over the years, one thing that is clear to me is this: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, but open source methodologies represent a process that can change the way we approach our work and our businesses.

\n\n\n\n

[Josepha Haden Chomphosy 00:03:01] 

\n\n\n\n

The second big thing that I want to make sure you all take into the year with you is that we are preparing for the third phase of the Gutenberg project. We are putting our backend developer hats on and working on the APIs that power our workflows. That workflows phase will be complex. A little bit because APIs are dark magic that binds us together, but also because we’re going to get deep into the core of WordPress with that phase.

\n\n\n\n

If you want to have impactful work for future users of WordPress, though, this is the phase to get invested in. This phase will focus on the main elements of collaborative user workflows. If that doesn’t really make sense to you, I totally get it. Think of it this way, this phase will work on built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and things like programmable editorial, pre-launch checklists.

\n\n\n\n

[Josepha Haden Chomphosy 00:04:00] 

\n\n\n\n

So phases one and two of the Gutenberg project had a very ‘blocks everywhere’ sort of vision. And phase three and, arguably, phase four will have more of a ‘works with the way you work’ vision.

\n\n\n\n

And my final thought for you all as we head into the year is this, there are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was State of the Word 2013, where Matt dreamed on stage of a true WYSIWYG editor for WordPress. Some say it was State of the Word 2016, where we were all encouraged to learn JavaScript deeply. For a lot of us though, it was at WordCamp Europe in 2018 when the Gutenberg feature plugin first made its way to the repo.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like it’s been a long time because it has been a long time. But I can also confirm that it takes many pushes to knock over a refrigerator. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:00] 

\n\n\n\n

For early adopters, both to the creation of Gutenberg as well as its use, hyperfocus on daily tasks makes it really hard to get a concept of scale.

\n\n\n\n

And so I encourage everyone this year to look out toward the horizon a bit more and up toward our guiding stars a bit more as well. Because we are now, as we ever were, securing opportunity for those who come after us because of the opportunity that was secured for us by those who came before us. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:33] 

\n\n\n\n

That brings us now to our small list of big things. It’s a very small list, but two pretty big things. The first thing on the list is that the WordPress 6.2 release is on its way. If you would like to get started contributing there, you can wander over to make.WordPress.org/core. You can volunteer to be part of the release squad. You can volunteer your time just as a regular contributor, someone who can test things — any of that. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

We’ll put a link in the show notes. And the second thing that I wanted to remind you of is that today is the deadline to submit topics for the Community Summit that’s coming up in August. That comes up in the middle of August, like the 22nd and 23rd or something like that. 

\n\n\n\n

We’ll put a link to that in the show notes as well. If you already have chatted with a team rep about some things that you really want to make sure get discussed at the community summit, I think that we can all assume that your team rep has put that in. But if not, it never hurts to give it a second vote by putting a new submission into the form.

\n\n\n\n

And that, my friends, is your small list of big things. Thank you for tuning in today for the WordPress Briefing. I’m your host, Josepha Haden Chomphosy, and I’ll see you again in a couple of weeks.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:57:\"\n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:49:\"Letter from WordPress’ Executive Director, 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/letter-from-wordpress-executive-director-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14180\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:127:\"If Phases 1 and 2 had a \"blocks everywhere\" vision, think of Phase 3 with more of a “works with the way you work” vision. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:5903:\"\n

Last month at State of the Word, I shared some opening thoughts about “Why WordPress.” For me, this is an easy question, and the hardest part is knowing which lens to answer through. The reasons that a solopreneur will choose WordPress are different than the reasons a corporation would. And while artists and activists may have a similar vision for the world, their motivations change their reasons, too. That’s why I always focus on the philosophical parts of the answer because I know that I am speaking as an advocate for many types of WordPressers. I have a few other reasons, too, which you may not be aware of as you use our software every day.

\n\n\n\n

Why WordPress?

\n\n\n\n

Most importantly, the Four Freedoms of Open Source. If you have already listened to State of the Word, you have heard my thoughts on the philosophical side of open source and the freedoms it provides. If you didn’t, then the tl;dr on that is that open source provides protections and freedoms to creators on the web that should be a given. There’s an extent to which the idea of owning your content and data online is a radical idea. So radical, even, that it is hard for folks to grasp what we mean when we say “free as in speech, not free as in beer.” Securing an open web for the future is, I believe, a net win for the world especially when contrasted to the walled gardens and proprietary systems that pit us all against one another with the purpose of gaining more data to sell.

\n\n\n\n

A second reason is that WordPress entrepreneurs (those providing services, designing sites, and building applications) have proven that open source offers an ethical framework for conducting business. No one ever said that you cannot build a business using free and open source software. And I am regularly heartened by the way successful companies and freelancers make an effort to pay forward what they can. Not always for the sole benefit of WordPress, but often for the general benefit of folks learning how to be an entrepreneur in our ecosystem. Because despite our competitive streaks, at the end of the day, we know that ultimately we are the temporary caretakers of an ecosystem that has unlocked wealth and opportunity for people we may never meet but whose lives are made infinitely better because of us.

\n\n\n\n

And the final reason is that leaders in the WordPress community (team reps, component maintainers, and community builders) have shown that open source ideals can be applied to how we work with one another. As a community, we tend to approach solution gathering as an “us vs. the problem” exercise, which not only makes our solutions better and our community stronger. And our leaders—working as they are in a cross-cultural, globally-distributed project that guides or supports tens of thousands of people a year—have unparalleled generosity of spirit. Whether they are welcoming newcomers or putting out calls for last-minute volunteers, seeing the way that they collaborate every day gives me hope for our future.

\n\n\n\n

As I have witnessed these three things work together over the years, one thing is clear to me: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, open source methodologies represent a process that can change the way we approach our work and our businesses. 

\n\n\n\n

WordPress in 2023

\n\n\n\n

As we prepare for the third phase of the Gutenberg project, we are putting on our backend developer hats and working on the APIs that power our workflows. Releases during Phase 3 will focus on the main elements of collaborative user workflows. If that doesn’t make sense, think of built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and programmatic editorial and pre-launch checklists.

\n\n\n\n

If Phases 1 and 2 had a “blocks everywhere” vision, think of Phase 3 with more of a “works with the way you work” vision. 

\n\n\n\n

In addition to this halfway milestone of starting work on Phase 3, WordPress also hits the milestone of turning 20 years old. I keep thinking back to various milestones we’ve had (which you can read about in the second version of the Milestones book) and realized that almost my entire experience of full-time contributions to WordPress has been in the Gutenberg era.

\n\n\n\n

I hear some of you already thinking incredulous thoughts so, come with me briefly.

\n\n\n\n

There are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was at State of the Word 2013 when Matt dreamed of “a true WYSIWYG” editor for WordPress. Some say it was at State of the Word 2016 where we were encouraged to “learn Javascript deeply.” For many of us, it was at WordCamp Europe in 2017 when the Gutenberg demo first made its way on stage.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like a long time because it has been a long time. I can also confirm that it takes many pushes to knock over a refrigerator. For early adopters (both to the creation of Gutenberg and its use), hyper-focus on daily tasks makes it hard to get a concept of scale.

\n\n\n\n

So I encourage you this year to look out toward the horizon and up toward our guiding stars. We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.

\n\n\n\n

Rather listen? The abbreviated spoken letter is also available.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14180\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:63:\"\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:43:\"WordPress is Turning 20: Let’s Celebrate!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"https://wordpress.org/news/2023/01/wordpress-is-turning-20-lets-celebrate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 21:38:49 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:3:{i:0;a:5:{s:4:\"data\";s:6:\"Events\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:4:\"WP20\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14155\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"2023 marks the 20th year of WordPress. Read on to learn about how WordPress is celebrating this milestone.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Dan Soschin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:1476:\"\n

2023 marks the 20th year of WordPress. Where would we all be without WordPress? Just think of that! While many technologies, software stacks, and fashion trends have come and gone throughout the past two decades, WordPress has thrived. This is due to the fantastic work and contributions of the WordPress community, comprised of thousands of contributors; and millions of users who have embraced the four freedoms of WordPress and the mission to democratize publishing.

\n\n\n\n

Let’s celebrate!

\n\n\n\n

Throughout the beginning of 2023, leading up to the official anniversary date of WordPress’s launch (May 27, 2003), a number of different events will celebrate this important milestone, reflect on the journey, and look toward the future.

\n\n\n\n

Please join in!

\n\n\n\n

Over the next few months, be sure to check WordPress’s official social media accounts along with the official anniversary website for updates on how you can be involved in this exciting celebration by contributing content, collecting cool anniversary swag, and much more. 

\n\n\n\n

Use the hashtag #WP20 on social media so the community can follow along.

\n\n\n\n

If you have something planned to celebrate that you would like to be considered for inclusion on the official website, please use this form to share the details.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14155\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"WP Briefing: Episode 46: The WP Bloopers Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wordpress.org/news/2022/12/episode-46-the-wp-bloopers-podcast/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 31 Dec 2022 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:11:\"wp-briefing\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14123\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:115:\"This episode of the WP Briefing features all the Josepha bloopers our little elves have stored away over the year. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:60:\"https://wordpress.org/news/files/2022/12/WP-Briefing-046.mp3\";s:6:\"length\";s:1:\"0\";s:4:\"type\";s:0:\"\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:9636:\"\n

This episode of the WP Briefing features all the Josepha bloopers our little elves have stored away over the year.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress Briefing, the podcast where you can normally catch quick explanations of the ideas behind the WordPress open source project with the hope that deeper understanding creates deeper appreciation.

\n\n\n\n

But on today’s bonus episode, instead of catching quick explanations, you’ll catch some quick bloopers. 

\n\n\n\n

The end of the year is a time when many people and many cultures gather together, and whether you observe traditions of light or faith, compassion, or celebration from everyone here at the WordPress Briefing Podcast, we’re wishing you a happy, festive season and a very happy New Year.

\n\n\n\n

Sit back, relax, and enjoy some of the laughs and outtakes from recording the WP Briefing over the year.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress. This is the thing I’ve done 25 times, and I know how to do it for reals.

\n\n\n\n

Welcome to WordPress Briefing, episode 20. Oh no, 7? 27? 26? Episode 27. I know how many things I’ve done.

\n\n\n\n

Ooh, neat. This is Josepha recording episode 46 of the WP Bonus Briefings. Not because we’ve had 46 bonus Briefings, but because this is the 46th one and it is a bonus, it will also have a fancy name. But right now, I’m just calling it the bonus. It’s gonna be quick. Here I go. 

\n\n\n\n

Group them into two big buckets, themes, uh, themes and tools. Mmm, I’m gonna have to redo the whole thing! No! I thought I could save it, and I didn’t save it. I had a typo in my script, and then I messed it up. I, it said into you big buckets instead of into two big buckets. 

\n\n\n\n

[Josepha Haden Chomphosy 00:02:00] 

\n\n\n\n

I’m gonna start over from the target release date because I kind of smeared it all together, um, despite what I intended to do.

\n\n\n\n

And gives everyone, no. What is this ringing of phones? Oh, I was doing so well. Where was I? Let’s see if I can just pick it up.

\n\n\n\n

All righty, live from my closet. It’s episode 20, the WordPress Briefing, WP Briefing. So I have a title for this, and when I started writing it, I really had every intention of writing it to the title. And then what I wrote doesn’t fit the title at all, but does really hang together well. And so we’re gonna have to come up with a new title, but at the moment, it’s called So Many Ways to WordPress.

\n\n\n\n

Here in a minute, you will see why it doesn’t fit. Also, at the end, I feel like I get very, like, angry nerd leader.

\n\n\n\n

[Josepha Haden Chomphosy 00:03:00]  

\n\n\n\n

And so I may, I may at the end, give that a second go and see if there’s a way that I can soften it a little bit, but, I, I don’t know that I can soften it. I feel very strongly about it. So, maybe I am just an angry nerd leader.

\n\n\n\n

Oh, okay. I’ll get us started now that I apparently have filled the room with apologies, not the room, the closet. 

\n\n\n\n

We’ll figure out something very catchy as a title or as an alternative. Very descriptive, and people will click on it because they must know, but we’ll figure out the title later.

\n\n\n\n

@wordpress.org. However, I don’t know why I decided to do an invitation to email me in the middle of that. I’m gonna start from the top of that paragraph. I just got too excited by the opportunity to get mail.

\n\n\n\n

I gotta slow it down. I’m like the fastest talker, had too much coffee. Okay, slowing it down now. 

\n\n\n\n

Huh? What am I saying? No, no, that’s what I’m saying. It’s fine. I, I can do this. 

\n\n\n\n

[Josepha Haden Chomphosy 00:04:00]

\n\n\n\n

Hold on. Oww. Sorry. I was adjusting my microphone, and then it fell down. I happened to be holding it at the time, so it didn’t, like, slam down, I think, and hurt your ears and so I apologize. Good thing I stopped so it didn’t just, like, slam down in the middle of a recording.

\n\n\n\n

That’s all right. I’m gonna give myself that win, even though it’s a hollow one. All right. Trying again. Starting right there, at now since.

\n\n\n\n

This year, it starts on October 18th, 2001. That’s the year? No, 2021. That’s the year. Oh man. I’m doing such a great job of this.

\n\n\n\n

Um, I’m recording this slightly before, um, you’re hearing it? What, how am I gonna start this? Hold on. I don’t know how to start this. All right. I’m, I can do it.

\n\n\n\n

Oh, I’m so glad I remembered. We had guests that could have been so embarrassing.

\n\n\n\n

Now for me, the trade-offs work well. How many times can I say now?

\n\n\n\n

[Josepha Haden Chomphosy 00:05:00] 

\n\n\n\n

Do I just start every sentence with now now? Is this just how I do things? Uh, now, now, now, now. I’m gonna start all over again because I’m in my head about the words in my mouth now. So.

\n\n\n\n

In some near timeframe, some near timeframe. This is not a thing that people say, Dustin, I’m sorry. That’s not a thing people say. I’m just gonna retry that one sentence to sound like I speak with other human beings sometimes.

\n\n\n\n

Today is the start of… I can do these things.

\n\n\n\n

This was a terrible ending. I need to just finish that last part. I’m gonna redo the part where I started with my name and not the name of the podcast. Um, and we’ll do that.

\n\n\n\n

And if you’re supporting or building anything to hand off to clients, you know that timely, easy to ship changes on a site are considered a vital part of any overarching brand and marketing strategy. Wow. It’s like, I don’t know what words are right there. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

I tripped over my own tongue a lot. I’m gonna sit, I’m gonna do that paragraph again because I didn’t do a very good job of it.

\n\n\n\n

I’ll do a better job.

\n\n\n\n

I literally digress, and now I don’t know. I am in my thing. What was I saying? Oh, there we go. 

\n\n\n\n

Topher DeRosia, who founded Word not WordPress. Holy moly. That was a, I knew I was gonna say that, and I was like, don’t say that when you actually get around to saying this, but here I am, and I did it. Even though I knew I was gonna do it and I told myself not to. Doing it again. Right from there.

\n\n\n\n

Not which audiench segment. Oh man. Audiench is not a word, folks. I was on a roll. I’m gonna start right from the primary thing.

\n\n\n\n

I don’t even remember how I started this podcast. What is the last thing I said? I said, here we go. All right. 

\n\n\n\n

Kind of covered some interesting ground, and so, oh no, this is not where I’m gonna start it. I know exactly where I’m gonna start it. Okay. I’m really ready now. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:07:00] 

\n\n\n\n

I suddenly, I’m gonna pause right here because I suddenly got really worried that I didn’t actually hit record. Oh my gosh. I did. Woo. I’m all over the place. Okay. We’ll now continue. Wait, did I? Oh my goodness. I did, super sorry.

\n\n\n\n

Of the WordPress Briefing. I’m gonna do some singing in the middle of some talking, but I keep trying to talk myself out of the singing, so I’m gonna go ahead and do the singing, and then I’ll do the talking before I talk myself out of the singing. Here I go, probably.

\n\n\n\n

I added a word. That was so good. I’m gonna start again. I’m gonna get some water, and then I’m gonna start again. Not again. Again. Just from the ‘and finally.’

\n\n\n\n

I don’t know how I finish my show. Y’all, I do this literally every week. I never know how to finish my show. Here we go.

\n\n\n\n

I don’t know why I shouted at you from the other side of the tiny closet. I apologize. I’m gonna start again from ‘and finally.’

\n\n\n\n

Tada we did it.

\n\n\n\n

[Josepha Haden Chomphosy 00:08:00] 

\n\n\n\n

Ha. I hate it. I hate the whole podcast. It’s gonna be fine. 

\n\n\n\n

Done. Nailed it.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

With that, I’m your host, Josepha Haden Chomphosy. Merry Christmas from me. Happy holidays to you, and we’ll see you again in the new year.

\n\n\n\n

Done.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14123\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n\n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:7:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:54:\"WP Briefing: Episode 45: State of the Word Reflections\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:76:\"https://wordpress.org/news/2022/12/episode-45-state-of-the-word-reflections/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 22 Dec 2022 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"Podcast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:11:\"wp-briefing\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14070\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:91:\"Josepha reflects on this year\'s State of the Word address here on the WP Briefing podcast. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:9:\"enclosure\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:3:\"url\";s:60:\"https://wordpress.org/news/files/2022/12/WP-Briefing-045.mp3\";s:6:\"length\";s:1:\"0\";s:4:\"type\";s:0:\"\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:14564:\"\n

In the forty-fifth episode of the WordPress Briefing, WordPress Executive Director Josepha Haden Chomphosy discusses highlights from this year’s State of the Word address.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

References

\n\n\n\n

LearnWP
WordPress Playground
ICYMI: State of the Word Recap
Take the 2022 WordPress Survey!
Exploring WordPress Certifications
Community Summit WordCamp Site
Submit Topics for the 2023 Community Summit
20th Anniversary– Stay Tuned for Updates
Check Out Style Variations and the 2023 Theme

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello, everyone, and welcome to the WordPress Briefing, the podcast where you can catch quick explanations of the ideas behind the WordPress open source project, some insight into the community that supports it, and get a small list of big things coming up in the next two weeks. I’m your host, Josepha Haden Chomphosy. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:39]

\n\n\n\n

Last week, WordPress hosted its annual State of the Word. As usual, this was delivered by our project co-founder Matt Mullenweg and represented a year-long labor of love from the WordPress community as a whole. There are many things I love about State of the Word, but consistently the thing I love the most is being able to shine spotlights on the great work of our global network of contributors.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:02] 

\n\n\n\n

Since that presentation goes by at the speed of light, I wanted to highlight a few things as well. First things first, I wanted to highlight that we had nearly 1,400 contributors, and by nearly, I mean just one too few. We had 1,399 contributors. So that is a big deal in general, but it’s an especially big deal to me because that’s before we start looking at any contributions that aren’t specifically tied to a release. 

\n\n\n\n

You may be wondering what those non-release contributions are. An incomplete list of those contributions would include organizing WordPress events, training others how to use WordPress, the myriad podcasts, articles, and newsletters that make up the WordPress media community, and any participant in a call for testing. Not to mention the unglamorous ways to contribute, like reviewing themes or reviewing plugins.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:58] 

\n\n\n\n

Things like patching security vulnerabilities and the bazillion things that Meta does to make sure that our community has all the tools that it needs to function. So I want to echo, once again, the huge, huge thanks that Matt already shared in State of the Word, and thank all of you for showing up for our project and for each other this way.

\n\n\n\n

The next thing I wanted to be sure to highlight was LearnWP. It was briefly noted that 12,000 learners had found their way to courses on learn.wordpress.org, and then during the Q&A, there was a related question about certifications in WordPress. 

\n\n\n\n

The need for certifications has been a regular topic in our project, and I mentioned that there are two different ongoing discussions at the moment. One of those discussions is happening directly on the make.wordpress.org/training site, so I’ll share a link in the show notes for that.

\n\n\n\n

But I’ve also been personally chatting on and off with Training team reps and other members of the community about what makes that so hard. In case you have not heard my whole spiel about what makes it difficult, it’s the logistics and our speed of iteration, and public perception. 

\n\n\n\n

[Josepha Haden Chomphosy 00:03:05]

\n\n\n\n

So not exactly a small set of hurdles. I’ll be doing a more complete post on this in the New Year so that we can get some solid documentation of the state of things and not let it be lost forever in this podcast. But I do know that it is something that we are very interested in as a community and something that, historically, I have really been resistant to.

\n\n\n\n

Not because I think it’s a bad idea, but because as someone who’s looking out for our operations side of things and our logistics side of things, it is not clear how we’re gonna get that done. Like I said, in the New Year, keep an eye out for a big, big post that takes a look at the benefits versus the costs and everything that we can do to help make those match each other a bit better.

\n\n\n\n

And then the last thing I wanted to highlight was the WordPress Playground. Okay, so this was the last thing that Matt mentioned, and I want to be sure that it’s clear what’s going on with this project because when I first heard about it, I very nearly lept from my chair! 

\n\n\n\n

[Josepha Haden Chomphosy 00:04:03] 

\n\n\n\n

It was such a remarkably big deal. Okay, so the WordPress Playground uses technological magic called ‘web assembly.’ I don’t know what it is, but it’s magic. And when I say magic, I mean that this tool makes it possible to run WordPress, an instance of WordPress, including a theme and a handful of plug-ins entirely inside your browser as a logged-in admin.

\n\n\n\n

You don’t need a server. You don’t need to select a host. You don’t need to download anything at all. You don’t need to know what your domain’s going to be. You simply select the theme you want to test. Add some dummy content and see how all of the posts and pages function as though we’re a real live WordPress site running on your favorite top-tier host.

\n\n\n\n

Then when you close the tab, it’s gone forever. Poof. Just like that. Now, this is a brand new project. It’s brand new to us and has a long way to go. So if working on that sounds cool, stop by the Meta Playground channel in the Making WordPress Slack. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:09] 

\n\n\n\n

But this, in my mind, changes the way that we stage sites.

\n\n\n\n

It could change the way we determine whether a theme or plugin is right for us. And arguably, it can become a stress-free way to introduce new or undecided users to WordPress’s admin area so that they can tell what they’re getting into. So when I say that this is a mind-blowing thing, and when I say that it is powered by magic, like it is astounding, it is astounding.

\n\n\n\n

And the applications for our users as a whole, I think, are untapped yet, and potentially even the applications for our learners and future learners of WordPress– equally untapped. I’m very excited to see what we can do with this project in the future. So stop by the Meta channel. Stop by Meta Playground.

\n\n\n\n

See what’s going on over there. We would love to have you. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

So those are my highlights of the day for State of the Word. Like I said, there are a few things I want to do more of a deep dive on in the text, so keep an eye out on make.wordpress.org/projects for most of those. But right now, let’s make some time for the small list of big things.

\n\n\n\n

[Josepha Haden Chomphosy 00:06:17] 

\n\n\n\n

Today I actually have kind of like a big list of big things. But I pretended it was small, so you didn’t turn off the podcast. So the first thing that I have is that in case you missed State of the Word, if you didn’t have a Watch Party to go to, or you didn’t know it was happening and so you didn’t really tune in at the time, I’m going to drop in a link of the recording.

\n\n\n\n

It’s gonna probably start right when everything gets going. And so you shouldn’t have to scrub through anything. If you end up on one of the recordings that includes like the whole live stream, there is jazz for the first 30 minutes, and just, you know, skip through that.

\n\n\n\n

[Josepha Haden Chomphosy 00:07:00]

\n\n\n\n

The second thing on my big list of big things is our annual community survey. So Matt mentioned this in State of the Word, and he pointed out that one of the things that makes WordPress and open source in general so effective is that we have a way to communicate with people who are using our software and we make every effort to be responsive to it.

\n\n\n\n

So the annual survey that we send out, it used to be quite big, and we’ve cut it down to 20 questions. If you want, you can think of it as like a census, so have your type of work and how long you’ve been working in WordPress, and what you wish to do with WordPress– have all those things be counted so we have a good idea of the type of person who’s currently using WordPress, and we can account for your needs and wants.

\n\n\n\n

But also, if you want to think of it more as an opportunity to share the things that were especially useful for you in the project this year or especially valuable for you as a contributor, this is also an excellent place to do that.

\n\n\n\n

[Josepha Haden Chomphosy 00:08:01] 

\n\n\n\n

There’s a QR code running around on the internet somewhere, but I’ll also put a link in the show notes. If you do not know where the show notes are, by the way, they are at wordpress.org/news/podcast, and you’ll be able to get to the survey.

\n\n\n\n

The third thing on my big list of big things is that next year we’re hosting a community summit. So if you’ve never been to a community summit, Matt mentioned that it is an opportunity for the best and most prolific contributors that we have to show up and discuss the things that are the biggest problems for the WordPress project right now.

\n\n\n\n

But we also want to make sure that we are making space for the voices that we know that we are missing from the community as well as contributors who look like they are probably excellent future stewards of this open source project that we are taking care of together. And so there is a whole website for that.

\n\n\n\n

[Josepha Haden Chomphosy 00:08:55] 

\n\n\n\n

I believe it’s communitysummit.wordcamp.org. Right now, there is a form up asking for topics that you want to be able to discuss while we are there, but it’s taking place, if I recall correctly, on August 22nd and 23rd of 2023.

\n\n\n\n

Number four on my big list of big things is that next year is WordPress’s 20th anniversary. So on May 27th of next year, WordPress will officially be 20 years old. So on our 10th birthday, anniversary rather, and our 15th anniversary, we pulled together some parties all across the world. 

\n\n\n\n

We had some images, some logos, and things that were specific to the celebration that we printed into stickers and that folks put on, like, mugs and backpacks and cakes and stuff. So if you want to learn more about that, keep an eye out in the community channel in making WordPress Slack. They will keep you posted on how to one, find any of those logos and designs so that your local community can join in the celebrations.

\n\n\n\n

[Josepha Haden Chomphosy 00:10:03] 

\n\n\n\n

But they will also help you learn how to have any sort of WordPress celebration party that we’re doing there in May of 2023. 

\n\n\n\n

And then the final thing on my big list of big things, it was mentioned that on the 2023 theme that was shipped with a bunch of style variations and there was this really, I think, excellent illustrative video that Rich Tabor put together for us that shows that you can switch through style variations on a single theme and have a site that looks totally different.

\n\n\n\n

Now, that feels like that’s just a thing that should always have been in WordPress, but it is new this year. And so, if you have not yet had a chance to look at the 2023 theme, it is the default theme that shipped with 6.1. And so, if you have it on your website and just haven’t had a look at it yet, I encourage you to do that.

\n\n\n\n

[Josepha Haden Chomphosy 00:11:00]

\n\n\n\n

It’s a really interesting implementation that makes a single theme potentially look like an infinite number of other themes, and those style variations can be specific to the theme or can just kind of be around and about in the patterns that are also available in Core. 

\n\n\n\n

Give that a look. I think it’s super worthwhile.

\n\n\n\n

And that, my friends, is your big list of big things. Thank you for tuning in today for the WordPress Briefing. I’m your host, Josepha Haden Chomphosy, and I’ll see you again in the New Year.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14070\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:40:\"The Month in WordPress – November 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"https://wordpress.org/news/2022/12/the-month-in-wordpress-november-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 20 Dec 2022 12:05:04 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:18:\"Month in WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:18:\"month in wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14124\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:317:\"WordPress enthusiasts tuned in last week for the State of the Word address to celebrate the project\'s yearly accomplishments and explore what 2023 holds. But that’s not the only exciting update from the past month. New proposals and ideas are already emerging with an eye on the year ahead—let’s dive into them!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"rmartinezduque\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:13931:\"\n

WordPress enthusiasts tuned in last week for the State of the Word address to celebrate the project’s yearly accomplishments and explore what 2023 holds. But that’s not the only exciting update from the past month. New proposals and ideas are already emerging with an eye on the year ahead—let’s dive into them!

\n\n\n\n
\n\n\n\n

Highlights from State of the Word 2022

\n\n\n\n

WordPress co-founder Matt Mullenweg delivered the annual State of the Word address on December 15, 2022, before a live audience in New York City. Most attendees joined the event via livestream or one of the 33 watch parties held across 11 countries.

\n\n\n\n

Josepha Haden Chomphosy, Executive Director of WordPress, kicked off this year’s event with an introduction to the Four Freedoms of open source and the importance of WordPress in ensuring “a free, open and interconnected web for the future.”

\n\n\n\n

Similar to past State of the Word events, Matt reflected on the project’s achievements over the past year, including Gutenberg’s adoption beyond WordPress, the steady progress in advancing the site editing experience, and the return to in-person events. In addition, he took the opportunity to remind everyone of the 2023 Community Summit and the 20th anniversary of WordPress coming up next year.

\n\n\n\n

Ahead of 2023, Matt announced new taxonomies in the WordPress.org theme and plugin directories to help users identify the extensions that best fit their needs and plans for Phase 3 of Gutenberg—Collaboration—among other notable updates.

\n\n\n\n

People who watched the State of the Word enjoyed a demo of WordPress Playground, an experimental project to explore, experiment, and build apps with a WordPress instance that runs entirely in the browser.

\n\n\n\n
\n

Missed the event? Read the recap or watch the State of the Word recording and Q&A session on WordPress.tv.

\n
\n\n\n\n
\n\n\n\n

The 2022 WordPress Survey is open

\n\n\n\n

The annual WordPress survey helps project leadership and those who build WordPress understand more about the contributor experience, how the software is used, and by whom.

\n\n\n\n

This year’s survey will remain open through the end of 2022 and is available in English, French, German, Italian, Japanese, Russian, and Spanish.

\n\n\n\n
\n

Take the 2022 WordPress Survey to help make an impact on the project.

\n
\n\n\n\n
\n\n\n\n

What’s new in Gutenberg

\n\n\n\n

Two new versions of Gutenberg have shipped in the last month:

\n\n\n\n
    \n
  • Gutenberg 14.6, released on November 23, 2022, came with many refinements to core blocks. Notable highlights include a variation picker that allows users to choose a desired layout when a Group block is inserted on a page, a new list view for editing the Navigation block, and a keyboard shortcut to transform paragraph blocks into headings.
  • \n\n\n\n
  • Gutenberg 14.7, released on December 7, 2022, introduced an experimental tabbed sidebar, colors to help identify some block types in list view, and improvements to the Page List block to make it easier to manage page links in the content.
  • \n
\n\n\n\n
\n

Follow the “What’s new in Gutenberg” posts to stay on top of the latest enhancements.

\n
\n\n\n\n
\n\n\n\n

Team updates: Introducing the block editor in the support forums, a revamped Showcase page, and more

\n\n\n\n\n\n\n\n
\n

Curious about why WordPress has so many releases? Tune in to Episode 44 of WP Briefing to learn about the role of major and minor releases in the project.

\n
\n\n\n\n
\n\n\n\n

Feedback & testing requests

\n\n\n\n\n\n\n\n
\n

The Community Team is calling on WordPress contributor teams to suggest topics for the 2023 Community Summit by January 16, 2023.

\n
\n\n\n\n
\n\n\n\n

WordPress events updates

\n\n\n\n
    \n
  • The #WPDiversity working group organized several workshops during the past few months. Among other highlights, attendees of the Speaker Workshop for Women Voices in Latin America reported a 52% increase in self-confidence to speak in public. Stay tuned for the next events.
  • \n\n\n\n
  • The WordCamp Europe 2023 organizing team shared their content vision for next year’s flagship event in Athens, Greece.
  • \n\n\n\n
  • WordCamp Asia 2023 is just a few months away, scheduled for February 17-19, 2023, in Bangkok, Thailand. Organizers have announced the first recipient of the WordCamp Asia Diversity Scholarship, Awais Arfan.
  • \n\n\n\n
  • Three more WordCamps are happening in the next few months:\n\n
  • \n
\n\n\n\n
\n

WordCamp Europe 2023 is calling for sponsors and speakers.

\n
\n\n\n\n
\n\n\n\n
\n\n\n\n

Have a story we should include in the next issue of The Month in WordPress? Fill out this quick form to let us know.

\n\n\n\n

The following folks contributed to this edition of The Month in WordPress: @cbringmann, @webcommsat, @sereedmedia, and @rmartinezduque.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14124\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"State of the Word 2022: A Celebration of the Four Freedoms of Open Source\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"https://wordpress.org/news/2022/12/state-of-the-word-2022-recap/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 16 Dec 2022 22:11:15 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:6:\"Events\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:17:\"state of the word\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14110\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:355:\"WordPress belongs to all of us, but really we’re taking care of it for the next generation.” Matt Mullenweg A small audience of WordPress contributors, developers, and extenders gathered on December 15 for the annual State of the Word keynote from WordPress co-founder Matt Mullenweg. Those who could not join in person joined via livestream […]\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Chloe Bringmann\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:5677:\"\n
\n

WordPress belongs to all of us, but really we’re taking care of it for the next generation.”

\nMatt Mullenweg
\n\n\n\n
\n\n
\n\n\n\n

A small audience of WordPress contributors, developers, and extenders gathered on December 15 for the annual State of the Word keynote from WordPress co-founder Matt Mullenweg. Those who could not join in person joined via livestream or one of 33 watch parties held across 11 countries, with more than 500 RSVPs.

\n\n\n\n
\"The
\n\n\n\n

Executive Director, Josepha Haden Chomphosy, introduced the event with a reminder of why so many of those gathered choose WordPress—the Four Freedoms of open source. As Haden Chomphosy noted, open source is an idea that can change our generation, and WordPress is one of the most consistent and impactful stewards of those freedoms.

\n\n\n\n

As with past State of the Word events, Matt reflected on the year’s accomplishments, learnings, and aspirations as the project moves into 2023. From Gutenberg concluding its second phase of site editing in preparation for phase three—Collaborative Workflows, to the reactivation of meetups and global WordCamps, to the introduction of a new theme and plugin taxonomy, to musings on the potential of machine learning, WordPress enters its 20th year continuing to define bleeding edge technology in thanks to the ecosystem’s vibrant community. 

\n\n\n\n

The one-hour multimedia presentation was followed by an interactive question and answer session where Matt fielded questions from the livestream and studio audience. All questions will be responded to in a follow-up post on Make.WordPress.org/project

\n\n\n\n

Discover everything that was covered by watching the official event recording and join the ongoing #StateOfTheWord conversation on Tumblr, Instagram, Facebook, Linkedin, and Twitter. For another way to get involved, consider sharing your experience with WordPress in the 2022 WordPress Community Survey.

\n\n\n\n\n\n\n\n

Referenced Resources 

\n\n\n\n\n\n\n\n

Special thanks to @laurlittle and @eidolonnight for review and collaboration.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14110\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:60:\"\n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"Share Your Experience: The 2022 WordPress Survey is Open\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:57:\"https://wordpress.org/news/2022/12/2022-wordpress-survey/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 01 Dec 2022 16:00:19 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:2:{i:0;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:6:\"survey\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14062\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:131:\"The 2022 WordPress survey is open for your input and available in English, French, German, Italian, Japanese, Russian, and Spanish.\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:15:\"Chloe Bringmann\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:4584:\"\n

Each year, members of the WordPress community (users, site builders, extenders, and contributors) provide valuable feedback through an annual survey. Key takeaways and trends that emerge from this survey often find their way into the annual State of the Word address, are shared in the public project blogs, and can influence the direction and strategy for the WordPress project.

\n\n\n\n

Simply put: this survey helps those who build WordPress understand more about how the software is used, and by whom. The survey also helps leaders in the WordPress open source project learn more about our contributors’ experiences.  

\n\n\n\n

To ensure that your WordPress experience is represented in the 2022 survey results, take the 2022 annual survey now.

\n\n\n\n\n\n\n\n

You may also take the survey in French, German, Italian, Japanese, Russian, or Spanish, thanks to the efforts of WordPress polyglot contributors. These are the most frequently installed languages based on the number of WordPress downloads. 

\n\n\n\n

The survey will be open through the end of 2022, and then WordPress plans to publish the results sometime in 2023. This year, the survey questions have been refreshed for more effortless survey flow, completion, and analysis. Some questions have been removed, while a few new ones are now present, reflecting the present and future of WordPress. If you’re looking for the analysis of the 2021 survey results, those will also be shared in early 2023.

\n\n\n\n

Spread the word

\n\n\n\n

Help spread the word about the survey by sharing it with your network, through Slack, or within your social media accounts. The more people who complete the survey and share their experience with WordPress, the more the project as a whole will benefit in the future.

\n\n\n\n

Security and privacy

\n\n\n\n

Data security and privacy are paramount to the WordPress project and community. With this in mind, all data will be anonymized: no email addresses nor IP addresses will be associated with published results. To learn more about WordPress.org’s privacy practices, view the privacy policy.

\n\n\n\n

Thank you

\n\n\n\n

Thank you to the following WordPress contributors for assisting with the annual survey project, including question creation, strategy, survey build-out, and translation:

\n\n\n\n

dansoschin, _dorsvenabili, angelasjin, arkangel, audrasjb, atachibana, bjmcsherry, chanthaboune, eidolonnight, fernandot, fierevere, fxbenard, jdy68, jpantani, laurlittle, nao, nielslange, peiraisotta, piermario, rmartinezduque, santanainniss.

\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"14062\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:72:\"\n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:4:{s:0:\"\";a:6:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"People of WordPress: Huanyi Chuang\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"https://wordpress.org/news/2022/11/people-of-wordpress-huanyi-chuang/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 30 Nov 2022 20:09:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"category\";a:6:{i:0;a:5:{s:4:\"data\";s:9:\"Community\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:1;a:5:{s:4:\"data\";s:8:\"Features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:2;a:5:{s:4:\"data\";s:7:\"General\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:3;a:5:{s:4:\"data\";s:10:\"Interviews\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:4;a:5:{s:4:\"data\";s:9:\"HeroPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}i:5;a:5:{s:4:\"data\";s:19:\"People of WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=13562\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:11:\"isPermaLink\";s:5:\"false\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:144:\"The latest People of WordPress story features Huanyi Chuang, from #Taiwan, on his journey to become a digital marketer and front end developer. \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Abha Thakor\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:40:\"http://purl.org/rss/1.0/modules/content/\";a:1:{s:7:\"encoded\";a:1:{i:0;a:5:{s:4:\"data\";s:11689:\"\n

This month we feature Huanyi (Eric) Chuang, a front end developer from Taiwan, who helps connect local groups to WordPress and the worldwide open source community. He is part of the team helping to make the first WordCamp Asia a success in 2023.

\n\n\n\n

The People of WordPress series shares some of the inspiring stories of how people’s lives can change for the better through WordPress and its global network of contributors.

\n\n\n\n
\"Huanyi
\n\n\n\n

Discovering WordPress and the benefit of child themes

\n\n\n\n

Huanyi’s first footsteps in WordPress began in 2017 when he worked for a firm that built blogs and developed ad content for clients.

\n\n\n\n

After building a few sites using the platform, he discovered child themes and through them opened up a world of possibilities for his clients. To this day, he uses child themes to deliver truly custom designs and functionality for clients.

\n\n\n\n

Later in his career, Huanyi moved into digital marketing, integrating sites with massive ad platforms like Google and Facebook. This led him to learn to work with tracking code and JavaScript. He also began his learning journey in HTML, CSS, and PHP, to be able to improve his development skills and customize child themes.

\n\n\n\n

Meetups bring together software users to learn together

\n\n\n\n
\"Huanyi
Huanyi pictured in Australia during one of his travels meeting a koala bear.
\n\n\n\n

When Huanyi had a problem with a client’s site, he looked to WordPress meetups near where he lived in Taipei to help find the solutions.

\n\n\n\n
\n

“When I encountered an issue with the custom archive pages, a local meetup announcement showed up on my WordPress dashboard.”

\nHuanyi Chuang
\n\n\n\n

At the meetup, he met more experienced WordPress users and developers there, who answered his questions and helped him learn.

\n\n\n\n

“When I encountered an issue with the custom archive pages, a local meetup announcement showed up on my WordPress dashboard. That was my original connection with the local community,” Huanyi said.

\n\n\n\n

The WordPress community gave Huanyi a chance to connect with people, feed his curiosity about the software, and join a circle of people he could share this interest.

\n\n\n\n

At first, he thought meetups were an opportunity to source new clients, and he took his business cards to every event. However, he soon found that these events offered him the opportunity to make friends and share knowledge.

\n\n\n\n

From then on, Huanyi started focusing more on what he could give to these events and networks, making new friends, and listening to people. This led him to share as a meetup speaker his own commercial website management experience.

\n\n\n\n

The road to WordCamp

\n\n\n\n

It was going to his first meetup and then getting involved with WordCamps that changed Huanyi’s whole relationship with WordPress.

\n\n\n\n
\"Huanyi
\n\n\n\n

In 2018, he took the step to help as an organizer, having joined the Taoyuan Meetup in Taiwan. He played several parts across the organizing team, and the welcoming feeling he got in every situation encouraged him to get more involved.

\n\n\n\n

He recalls meeting new friends from different fields and other countries, which gave him a great sense of achievement and strengthened his passion for participating in the community.

\n\n\n\n

When the team started this meetup, numbers were much lower than in the group in the city of Taipei, but they were not disheartened and gradually grew the local WordPress community.

\n\n\n\n

They created a pattern of ‘multiple organizers,’ which spread the workload and grew friendships. 

\n\n\n\n
\n

“Being connected to and from meetups is the most valuable part of the community. Having these friends makes me gather more information. We share information and benefit from others’ information, and thus we gain more trust in each other. With such credibility, we share more deeply and build deeper relations.”

\nHuanyi Chuang
\n\n\n\n

Before the pandemic, the meetup met every month and grew to become the second largest meetup in Taiwan. Huanyi also contributed to the WordPress community as an organizer of WordCamp Taipei 2018 in the speaker team and lead organizer of WordCamp Taiwan 2021.

\n\n\n\n

So why should you join the community?

\n\n\n\n

According to Huanyi, you will always have something to take home with you. It might be new information or experiences. It might be plugins or theme ideas. But most of all, it is the chance to meet fascinating people and make new friends.

\n\n\n\n
\n

Huanyi’s message to other contributors:
“Keep participating, and you will find more you can achieve than you expect.”

\n
\n\n\n\n

He added that long-term participation will ‘let you feel the humanity behind the project’.

\n\n\n\n

Localize: the road ahead for WordPress

\n\n\n\n
\"Huanyi
\n\n\n\n

Huanyi believes WordPress has the power to break down the barriers between designers, project managers, developers, marketers, writers, and publishers. In Taiwan, he said WordPress is ‘a common protocol’ that lets people from all of these disciplines work and communicate together more easily than they ever have before.

\n\n\n\n

That is why he works on and encourages others to localize plugins today. He believes localization of the software is the foundation for the extension of the WordPress community as it enables people to ‘Flex their Freedom’ in a language they speak!

\n\n\n\n

He has helped to organize online events around previous WordPress Translation Day events.

\n\n\n\n

Huanyi said: “I think it’s important to localize WordPress because its very concept of ‘open source’ means that people can access it freely. In another way, free from the monopoly of knowledge and speech. To achieve it, it’s important that people can access it with their own language.

\n\n\n\n

“Localization is the foundation of the extension of WordPress community because it helps people using different languages to access the project and lowers the hurdle to understand how things work.”

\n\n\n\n

Share the stories

\n\n\n\n

Help share these stories of open source contributors and continue to grow the community. Meet more WordPressers in the People of WordPress series.

\n\n\n\n

Contributors

\n\n\n\n

Thank you to @no249a002 for sharing his adventures in WordPress.

\n\n\n\n

Thank you to Abha Thakor (@webcommsat), Mary Baum (@marybaum), Meher Bala (@meher), Chloe Bringmann (@cbringmann), Surendra Thakor (@sthakor), Adeeb Malik (@adeebmalik) for research, interviews, and contributing to this feature article.

\n\n\n\n

The People of WordPress series thanks Josepha Haden (@chanthaboune) and Topher DeRosia (@topher1kenobe) for their support.

\n\n\n\n
\"HeroPress
\n

This People of WordPress feature is inspired by an essay originally published on HeroPress.com, a community initiative created by Topher DeRosia. It highlights people in the WordPress community who have overcome barriers and whose stories might otherwise go unheard. #HeroPress

\n
\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:7:\"post-id\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"13562\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}s:27:\"http://www.w3.org/2005/Atom\";a:1:{s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:0:\"\";s:7:\"attribs\";a:1:{s:0:\"\";a:3:{s:4:\"href\";s:32:\"https://wordpress.org/news/feed/\";s:3:\"rel\";s:4:\"self\";s:4:\"type\";s:19:\"application/rss+xml\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:44:\"http://purl.org/rss/1.0/modules/syndication/\";a:2:{s:12:\"updatePeriod\";a:1:{i:0;a:5:{s:4:\"data\";s:9:\"\n hourly \";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:15:\"updateFrequency\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"\n 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:30:\"com-wordpress:feed-additions:1\";a:1:{s:4:\"site\";a:1:{i:0;a:5:{s:4:\"data\";s:8:\"14607090\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:11:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Wed, 25 Jan 2023 13:14:24 GMT\";s:12:\"content-type\";s:34:\"application/rss+xml; charset=UTF-8\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:25:\"strict-transport-security\";s:11:\"max-age=360\";s:6:\"x-olaf\";s:3:\"⛄\";s:13:\"last-modified\";s:29:\"Thu, 19 Jan 2023 12:00:00 GMT\";s:4:\"link\";s:63:\"; rel=\"https://api.w.org/\"\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:16:\"content-encoding\";s:4:\"gzip\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20211220193300\";}','no'),(139,'_transient_timeout_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1674695664','no'),(140,'_transient_feed_mod_9bbd59226dc36b9b26cd43f15694c5c3','1674652464','no'),(141,'_transient_timeout_feed_d117b5738fbd35bd8c0391cda1f2b5d9','1674695670','no'),(142,'_transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9','a:4:{s:5:\"child\";a:1:{s:0:\"\";a:1:{s:3:\"rss\";a:1:{i:0;a:6:{s:4:\"data\";s:3:\"\n\n\n\";s:7:\"attribs\";a:1:{s:0:\"\";a:1:{s:7:\"version\";s:3:\"2.0\";}}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:1:{s:7:\"channel\";a:1:{i:0;a:6:{s:4:\"data\";s:61:\"\n \n \n \n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:1:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:16:\"WordPress Planet\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:8:\"language\";a:1:{i:0;a:5:{s:4:\"data\";s:2:\"en\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WordPress Planet - http://planet.wordpress.org/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"item\";a:50:{i:0;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:169:\"HeroPress: Becoming A Better Me with Core Contribution – কোর কন্ট্রিবিউশন এবং জীবনের নতুন অধ্যায়\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=5055\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:160:\"https://heropress.com/essays/becoming-a-better-me-with-core-contribution/#utm_source=rss&utm_medium=rss&utm_campaign=becoming-a-better-me-with-core-contribution\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:25361:\"\"Pull\n

এই নিবন্ধটি বাংলায় পাওয়া যায়

\n\n\n\nHere is Robin reading his own story aloud.\n\n\n\n
\n\n\n\n

Few years back, my daily life started with 10am waking up and going to the office without having breakfast (lazy me). Then doing a 9 hours job with a pretty simple routine and without any major engagement with others.

\n\n\n\n

At present, I wake up with tons of Slack messages and end my day with various in person short/long meetings with my fellow colleagues / mates around the world.

\n\n\n\n

I used to scroll Facebook, you know. But now WordPress Slack has become Facebook to me. How things got changed and became more enjoyable. 

\n\n\n\n

Lucky Me \"😊\"

\n\n\n\n

Hello World : How it all started

\n\n\n\n

I wasn’t supposed to be an engineer in the first place. I was brought up in Cumilla, Bangladesh. Finished my School and College in my hometown. Everyone wanted me to be a Doctor. It is very common here in our country that parents want their child to be a doctor. I completed my 3 months preparation for the Medical exam but later I ended up in Engineering.

\n\n\n\n
\n\n\n\n

I have spent 5 years in Sylhet, a heavenly place to live in. Oh! How I miss Sylhet these days. It has been a few years since I had breakfast (khichuri) in Pach Bhai restaurant (a very popular restaurant in Sylhet) and had tea in chachar tong (a famous tea stall in Modina Market, Sylhet). These days I don’t go out at night but during my Sylhet life, midnight tea was a much desired thing for us and of course that tea from a tong (small tea stall in the roads).

\n\n\n\n\n\n\n\n

My five years at SUST (Shahjalal University of Science and Technology) was a blessing to me. It helped me to become a better person and better me. Sust was full of energy. Seniors and Juniors. Lal Tong (tea stall in our campus). There were almost 300 plus students in our department and we knew personally almost 90 percent of our seniors and juniors. That bond is still alive in Dhaka (most of us living here with our job). Everyone helps each other to get a job or with the recommendation for the best jobs. Almost in every software farm I see SUST CSE seniors or juniors.

\n\n\n\n

Thanks God I got a chance to live those fine memorable years in SUST and Sylhet.

\n\n\n\n

Hello Dolly : Meeting WordPress

\n\n\n\n

My first meeting with WordPress was in my 2nd job. I was facing difficulties with my earlier professional career but as soon as I met WordPress, I just fell for her (WordPress). I found it really easy to adopt and it has a pretty huge community I must say. There were tons of documentation in Codex (but frankly I couldn’t understand at first). Now the documentation (https://developer.wordpress.org/) is much better and much more user friendly. I was amazed with the term Code is Poetry. It felt like I was writing poems instead of doing jobs.

\n\n\n\n

I really enjoyed my early career with WordPress. I wanted to do all by myself (that’s what we call Full Stack these days, LOL).

\n\n\n\n

I used to write markups from design (PSD to Html, that’s write). Then converting that into WordPress. And the training phase which was given to me was really a learning experience. I still keep in my mind that, “You can take unlimited divs. It won’t cost you money”, LOL. I was struggling with CSS opacity. But as soon as I started using it It became Pani(water, means easy) later.

\n\n\n\n

In my earlier life with WordPress I wasn’t aware of the active community and contribution to the project. I did many theme and site customization. Fixed bugs for clients. Built features as per their needs. But I was missing something.

\n\n\n\n

I was missing the large community of WordPress and the inner beauty of the Open Source project.

\n\n\n\n

Code is Poetry : WordPress Core Contribution

\n\n\n\n

My life at WPDeveloper was a blessing to me. It is where I started meeting the large community and the exciting activities of this wonderful community of WordPress. It feels like I truly belong to this community. Everyone is so close and so helpful to each other. 

\n\n\n\n

I have started joining meetups. Taking meetups, yes that’s correct. Started networking with similar minded people. It felt great to see so many people who love the same thing that you love. Such a blessing community.

\n\n\n\n

After joining WordPress slack and attending a few meetings, I found it is actually helping me to improve my skills. I saw how they manage their projects, how they think, how they fix. So many things to learn. I got addicted \"😀\" I started browsing channels often. 

\n\n\n\n

I started attending all the meetings of almost all the Make WordPress teams (that’s funny but I did). I was enjoying my life. 

\n\n\n\n

Slowly I started contributing to the Core WordPress. I do complex tasks in my regular job life but at core a simple task accomplishment gives so much pleasure. 

\n\n\n\n

Everytime I see my name in the commit description it feels good.

I didn’t stop after doing my first contribution to the core. I continued and I checked almost all tickets and figured out what I can fix or help to fix. I got PR reviews from WordPress experts. Their every single suggestion helped me to know the WordPress and Coding standards better. Now I do practice those coding standards in my regular job tasks.

\n\n\n\n

In WordPress 6.1 I contributed to 20 plus core tickets and that was a pretty good number in Bangladesh. These days I take online workshops in the Make Learn team, in person workshops in our Dhaka community. Also taking in house (within company) workshops to show how to join Release Parties and attend meetings and write team meeting notes. 

\n\n\n\n

By the way, I am Marketing Team Representative for the year of 2023. I am excited and looking forward to it. Also a Training Team Faculty member. 

\n\n\n\n

I don’t think all of these would be possible without being an active contributor to the project. Thank you everyone who helped me in this wonderful journey \"😊\"

\n\n\n\n

Life Is Beautiful : Living Success

\n\n\n\n

When I was writing this essay, I became one of the Release Leads of WordPress 6.2 (Test Co-Lead).

\n\n\n\n

It is unbelievable for me even after the declaration. I keep checking that P2 blog post just to make sure I am truly there, funny I know. 

\n\n\n\n

Recently I took contributor days in our office and it felt like there was only one topic in the town and that is “Let’s Do Core Contribution”. It became trending here, loving it \"😊\"

\n\n\n\n

Thanks to WordPress and the community. Due to my outstanding contribution in Core, I recently got selected for the prestigious #YoastCareFund and here I am sharing my stories with our HeroPress friends.

\n\n\n\n

Oh! I am living my dream life. Just one thing is missing. Ronaldo isn’t in UCL and is getting older. I know \"😀\"

\n\n\n\n

WordPress Core Contribution helped me to become a better developer, a better me. It removes your fear of losing your job and instead you will fall in love with your job and definitely you will enjoy every minute of your coding life.

\n\n\n\n

Thank You WordPress.
Code is Poetry and you are the book full of Poems.
I can’t stop reading you \"😊\"

\n\n\n\n

কোর কন্ট্রিবিউশন এবং জীবনের নতুন অধ্যায়

\n\n\n\n

এইতো কয়েক বছর আগেও, আমার ডেইলি রুটিন ছিল সকাল ১০ টায় ঘুম থেকে ওঠা এবং নাস্তা না করে অফিসে যাওয়া (আলসেমির কারণে দেরি হয়ে যেত এবং বাসায় নাস্তা করা হত না)। তারপর ৯ ঘণ্টার অফিস শেষ হত গতানুগতিক কাজ দিয়ে।

\n\n\n\n

বর্তমানে, আমার ঘুম থেকে উঠেই দেখি স্ল্যাক ভর্তি মেসেজ এবং দিন শেষ হয় ছোট বড় বেশ কিছু টিম কোলাবোরেশান এবং মিটিং এর মাধ্যমে। 

\n\n\n\n

আমি ছিলাম ফেসবুক পাগল, ইংরেজিতে এডিক্টেড \"😀\"। কিন্তু এখন WordPress Slack হয়ে গেছে ফেসবুক আমার কাছে। কীভাবে ইন্টারেস্ট পরিবর্তিত হয় এবং পরিবর্তনটা উপভোগও করছি।

\n\n\n\n

Lucky Me \"😊\"

\n\n\n\n

Hello World : যেভাবে পথচলা শুরু

\n\n\n\n

প্রথমত আমার ইঞ্জিনিয়ার হবার কথাই ছিল না। আমার শৈশব কাটে কুমিল্লায়। স্কুল এবং কলেজ এলাকাতেই ছিল। সবার চাইছিল আমি যেন ডাক্তার হই।আমাদের দেশে এটা খুব কমন যে বাবা মা চায় তাদের ছেলেমেয়েরা যেন ডাক্তার হয়। আমি মেডিকেলের জন্য তিন মাস প্রিপারেশান নেয়ার পরেও ভাগ্যক্রমে চান্স পেয়ে যাই ইঞ্জিনিয়ারিং এর জন্য।

\n\n\n\n

সিলেটে ছিলাম পাঁচ বছর। আহা সিলেট, Where Heaven touches the Earth <3  

\n\n\n\n
\n\n\n\n

সিলেট নাম শুনলেই থমকে যাই।সে কবে গেলাম।কতদিন পাঁচ ভাইয়ের খিচুরি খাই না, কতদিন মদিনা মার্কেটের চাচার টং দেখি না। কতদিন মাঝ রাতে বের হয়ে টং এর চা খাই না। 

\n\n\n\n

আহা সিলেট!  

\n\n\n\n\n\n\n\n

SUST (Shahjalal University of Science and Technology) এর ৫ বছর ছিল আমার জন্য ব্লেসিং। আমাকে পরিণত করেছিল সাস্ট। সাস্ট ছিল এনার্জিতে ভরপুর।সিনিয়র জুনিয়রদের সম্পর্ক। লাল টং। ৩০০ এর বেশি স্টুডেন্ট ছিল আমাদের ডিপার্টমেন্টে। যাদের মধ্যে ৯০ ভাগই ছিল আমাদের ভাই ব্রাদার। অলমোস্ট সবাইকেই চিনতাম আমরা। বর্তমানে আমরা সবাই ঢাকায় কোন না কোন জবে আছি। দেখা কম হলেও সম্পর্ক এখনও আগের মতই। সবাই সবাইকে জবে হেল্প করছে। জবের বাইরে হেল্প করছে।ঢাকার মোটামোটি সব ফার্মে গেলেই দেখা যায় SUST CSE থেকে কেউ না কেউ আছে।

\n\n\n\n

আল্লাহর কাছে শুকরিয়া সিলেট এবং সাস্টে পরার সুযোগ হয়েছিল।

\n\n\n\n

Hello Dolly : WordPress এর সাথে পরিচয়

\n\n\n\n

WordPress এর সাথে আমার প্রথম পরিচয় যখন আমি আমার দ্বিতীয় জবে জয়েন করি। ক্যারিয়ারের শুরুতে আমার খাপ খাওয়াতে একটু সমস্যা হচ্ছিল। যখনই WordPress এর সাথে পরিচয় তখন থেকেই ফিদা হয়ে গেলাম।এটার ব্যবহার বিগিনার হিসাবে তখন আমার কাছে অনেক সহজ এবং উপকারী ছিল।অনেক বড় একটা কমিউনিটি। রিসোর্স অনেক। যদি Codex ছিল বেশ কঠিন বুঝার জন্য। কিন্তু বর্তমানে ডকুমেন্টেশান (https://developer.wordpress.org/) অনেক ভাল এবং সহজ হয়েছে। প্রথম যখন Code is Poetry শুনেছি এবং দেখেছি আমার অনেক পছন্দ হয়েছিল। মনে হচ্ছিল কোড না যেন কবিতা লিখতেসি।

\n\n\n\n

ক্যারিয়ারের শুরুতে আমি WordPress বেশ উপভোগ করেছি। চাইতাম সব নিজে নিজে করব (যাকে আমরা বলি এখন Full Stack, লোল)। 

\n\n\n\n

শুরু হয়েছিল PSD to Html দিয়ে যা আসলে আমাদের অনেকের ক্ষেত্রেই মিলে যাবে। তারপর তা WordPress এ কনভার্ট করতাম। শুরুতে আমাকে একটা ট্রেনিং দেয়া হয়েছিল যা ছিল খুবী কার্যকর।

\n\n\n\n

আমার এখনও একটা কথা মনে আছে “যত বেশি div নিবা। div নিতে টাকা লাগে না”, লোল।  

\n\n\n\n

আমার CSS opacity নিয়ে সমস্যা হচ্ছিল। কিন্তু যখনই কাজ শুরু করে দিয়েছি আস্তে আস্তে সব পানি (ইংরেজিতে Water, মানে সহজ) হয়ে গেসে। 

\n\n\n\n

প্রথমদিকে আমি WordPress কমিউনিটি নিয়ে ততটা অবগত ছিলাম না। অনেক থিম কাস্টমাইজেশান এবং সাইট কাস্টমাইজেশান করেছি। Bug ফিক্স করেছি অনেক ক্লায়েন্টদের জন্য। ফিচার তৈরি করেছি তাদের চাহিদা অনুযায়ী। কিন্তু কি যেন একটা মিসিং ছিল। 

\n\n\n\n

WordPress Open Source Project এবং WordPress এর বড় একটা কমিউনিটির সাথে যে তখনও আমার পরিচয় হয়ে উঠেনি। 

\n\n\n\n

Code is Poetry : WordPress Core Contribution

\n\n\n\n

WPDeveloper ছিল আমার জন্য ব্লেসিং। এখানে আসার পর থেকেই আমি WordPress এর বড় কমিউনিটির সাথে পরিচিত হই এবং দেখতে থাকি তাদের একের পর এক চমৎকার উদ্যোগ।

\n\n\n\n

মনে হচ্ছিল যেন এটাই এতদিন মিসিং ছিল। সবাই এত আন্তরিক এবং সাহায্য করার জন্য কতটা উদগ্রীব। 

\n\n\n\n

আমি meetup জয়েন করা শুরু করলাম। meetup নেয়াও শুরু করলাম, হা ঠিক শুনেছেন।লোল। 

\n\n\n\n

সবার সাথে নেটওয়ার্কিং হল।দেখে খুব ভাল লাগল যে একই চিন্তা ধারার সবাই একসাথে।

\n\n\n\n

Such a blessing community.

\n\n\n\n

WordPress স্ল্যাক জয়েন করি এবং মিটিং এটেন্ড করা শুরু করি। দেখি যে এটা আসলেই আমাকে সাহায্য করছে আমার স্কিল বাড়াতে।দেখতে পেলাম কিভাবে তারা প্রজেক্ট মেনেজ করে, কিভাবে চিন্তা করে, কিভাবে বাগ ফিক্স করে। কত কিছু শিখার। এডিক্টেড হয়ে গেলাম \"😀\"। চ্যানেলগুলো প্রায়ই ব্রাউজ করতে থাকতাম।

\n\n\n\n

সব টিমের মিটিং জয়েন করতে শুরু করলাম (ফানি বাট সত্য)। সবকিছু ভালই লাগছিল। 

\n\n\n\n

আস্তে আস্তে কোর কন্ট্রিবিউশান শুরু করলাম। যদিও অফিসে কমপ্লেক্স কাজগুলাই আমরা করতাম। কিন্তু যখন একটা ছোট খাটো কোর কন্ট্রিবিউশান করি তখন মনে অনেক আনন্দ কাজ করে। যতবার কমিটে আমার নাম দেখি ততবারই ভাল লাগে। আহা।

\n\n\n\n

প্রথম কন্ট্রিবিউশানের পর আমি থেকে থাকি নাই। কন্টিনিউ করেছি। প্রতিদিন টিকেট গুলো ব্রাউজ করতাম। খুঁজে দেখতাম কোনটা করতে পারব। WordPress expert দের কাছ থেকে রিভিউ পেতে থাকলাম যখনই PR দিতাম।তাদের প্রতিটা সাজেশান আমার পরবর্তিতে বেশ কাজে দিয়েছে। নিজের অফিসের কাজেও তখন সেগুলো ব্যবহার করতে থাকলাম।

\n\n\n\n

WordPress 6.1 এ আমি ২০ এর অধিক টিকেট ফিক্স করতে সাহায্য করেছি। যা বাংলাদেশের জন্য বেশ ভাল একটা নাম্বার। এখন আমি Make Learn টিমের জন্য অনলাইন ওয়ার্কশপ বানাই। ইন পারসন ওয়ার্কশপ নেই আমাদের ঢাকা কমিউনিটির জন্য। ইন হাউজ ওয়ার্কশপ নেই কলিগদের জন্য। দেখাতে সাহায্য করি কিভাবে রিলিজ পার্টিতে জয়েন করতে হয়, কিভাবে টেস্ট রিপোর্ট লিখতে হয়, কিভাবে মিটিং নোট নিতে হয়।

\n\n\n\n

ভালো কথা, আমি এখন Marketing Team Representative ২০২৩ সালের জন্য। এটা আমি বেশ উপভোগ করছি। এবং সাথে আমি Training Team Faculty মেম্বারও। 

\n\n\n\n

আমার মনে হয় না কোর কন্ট্রিবিউশান ছাড়া আমার এই দায়িত্বগুলো পাওয়া পসিবল হত । সবাইকে অনেক ধন্যবাদ আমাকে সাহায্য করার জন্য \"😊\"। 

\n\n\n\n

Life Is Beautiful : সফলতা

\n\n\n\n

যখন আমি এটি লিখছি ততদিনে আরেকটি সুখবর পেয়ে গেছি। আমি এখন WordPress 6.2 এর একজন Release Lead (Test Co-Lead).

\n\n\n\n

একদম অবিশ্বাস্য। প্রায়ই P2 blog post গিয়ে চেক করে দেখি আমার নামটা আছে কিনা, হাস্যকর শুনাবে জানি। 

\n\n\n\n

কিছুদিন আগে কন্ট্রিবিউটর ডে আয়োজন করেছি। মনে হচ্ছিল যেন শহরজুড়ে একটাই ডায়লগ,

\n\n\n\n

“Let’s Do Core Contribution”। ট্রেন্ডিং হতে দেখে বেশ ভালই লাগে \"😊\"

\n\n\n\n

WordPress এবং কমিউনিটিকে অনেক ধন্যবাদ। কিছুদিন আগে #YoastCareFund পাই করে আউটস্ট্যান্ডিং কন্ট্রিবিউশানের জন্য। এবং আজ HeroPress বন্ধুদের সাথে সব শেয়ার করছি।

\n\n\n\n

একেই বুঝে বলে লিভিং ড্রিম লাইফ। একটা জিনিসই শুধু মিসিং। রোনাদোকে আর হয়ত ইউসিএলে দেখা যাবে না \"😀\"

\n\n\n\n

WordPress Core Contribution আমাকে ভাল ডেভেলপার হতে সাহায্য করেছে।জব হারানোর ভয় বাদ দিয়ে জবকে এঞ্জয় করা এবং কোডিং এর প্রতিটা মুহুর্ত উপভোগ করতে সাহায্য করে কোর কন্ট্রিবিউশান। 

\n\n\n\n

Thank You WordPress.
Code is Poetry and you are the book full of Poems.
I can’t stop reading you \"😊\"

\n

The post Becoming A Better Me with Core Contribution – কোর কন্ট্রিবিউশন এবং জীবনের নতুন অধ্যায় appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 25 Jan 2023 02:00:06 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:25:\"A H M Nazmul Hasan Monshi\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:1;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:55:\"WPTavern: Yoast SEO 20.0 Introduces New Admin Interface\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141380\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:66:\"https://wptavern.com/yoast-seo-20-0-introduces-new-admin-interface\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2764:\"

Yoast SEO version 20.0 was released today with a new admin settings interface that also reorganizes the menu to into four main sections: General, Content types, Categories and Tags, and Advanced.

\n\n\n\n\n\n\n\n

In this update, the plugin did not add new features and settings but rather moved them to better match user workflows. The new sidebar menu should result in fewer clicks in accessing the most used settings.

\n\n\n\n

The individual settings pages are also sporting the new design, which is lighter and brighter than the previous screens. With such a large number of settings to re-learn, Yoast SEO has also added a quick search to assist users in finding settings pages faster.

\n\n\n\n\n\n\n\n

“We felt that the default WordPress admin design no longer suited us,” Yoast founder Joost de Valk said. “Our product team was itching to take our experience to the next level. WordPress’ interface was holding us back a bit, as the admin interface outside Gutenberg hasn’t progressed for years.”

\n\n\n\n

The new settings UI was built with Yoast SEO’s React component library, which the company has open sourced and made available on its website.

\n\n\n\n

Reaction to the new design was mostly positive, although some users are not keen on plugins building their own UI in the admin. If all plugins did this, the WordPress admin would become a wild buffet of disparate interfaces that add cognitive load to site management.

\n\n\n\n

“It was… surprising so I’ll reserve real judgement until I use it a while,” WordPress developer Jon Brown said. “First impression though was ‘this needs an advanced mode that hides all the useless banner images and text and just goes back to a list with toggles.’ It’s pretty, but feels overwhelming.”

\n\n\n\n

 The Yoast SEO plugin and the new settings UI work with WordPress version 6.0 or higher. Users who are struggling to adapt to the new settings pages can reference Yoast SEO’s documentation, which has a video and guide to navigating the new interface.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 24 Jan 2023 21:43:57 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:2;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"Do The Woo Community: The WP Community Collective with Sé Reed and Courtney Robertson\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74360\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"https://dothewoo.io/the-wp-community-collective/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:422:\"

Sé and Courtney share all things to do with the new WP Community Collective, a source for supporting contributions and initiatives.

\n

>> The post The WP Community Collective with Sé Reed and Courtney Robertson appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 24 Jan 2023 10:36:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:3;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:47:\"WPTavern: Awesome Motive Acquires Thrive Themes\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141347\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"https://wptavern.com/awesome-motive-acquires-thrive-themes\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1769:\"

Awesome Motive has acquired Thrive Themes, its second acquisition of 2023 following the Duplicator plugin deal that was announced earlier this month.

\n\n\n\n

Thrive’s premium plugin suite reports more than 200,000 users. This includes Thrive Architect, a visual drag and drop page builder, an LMS course builder, and other marketing-focused plugins for generating leads, creating quizzes and testimonials, and doing A/B testing.

\n\n\n\n

In 2013, Thrive Themes co-founders Shane Melaugh and Paul McCarthy began their company with early products Hybrid Connect, Viral Quiz Builder, and WP Sharely. Ten years later the product suite has grown to nearly a dozen conversion-focused tools that Thrive Themes sells for $299/year.

\n\n\n\n

Although the co-founders will not be joining Awesome Motive, the team that is currently maintaining and supporting the plugin is being acquired. In the Thrive Themes announcement, Melaugh said the company’s products will not be rebranded or replaced. No price hikes are planned for existing customers and Awesome Motive plans to honor legacy memberships.

\n\n\n\n

“It has always been our policy to reward loyal customers and that will not change,” Melaugh said.

\n\n\n\n

“I’ve been watching Thrive Themes from the sidelines for a long time anyway. So my stepping away changes nothing on that front.

\n\n\n\n

“It will still be the same people building the products, and the roadmap we laid out for 2023 and beyond won’t change because of this acquisition.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 24 Jan 2023 02:57:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:4;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"WPTavern: WP Migrate 2.6 Introduces Full-Site Exports and Import to Local\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141320\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:84:\"https://wptavern.com/wp-migrate-2-6-introduces-full-site-exports-and-import-to-local\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3672:\"

WP Migrate, formerly known as WP Migrate DB and recently acquired by WP Engine, has long since expanded beyond its initial release as a database migration tool. Users may be familiar with the push/pull workflow of installing the plugin on two sites and migrating database, media, themes, and plugin changes back and forth. The most recent 2.6 release expands the plugin’s capabilities to include full-site exports for integration with Local, a popular free WordPress development tool, also owned by WP Engine.

\n\n\n\n

This new remote-to-local workflow is included in both the free WP Migrate plugin and the pro version. The full-site exports bundle the database, media, themes, plugins, and other files into a ZIP archive, which can be seamlessly imported into Local.

\n\n\n\n\n\n\n\n

After clicking Export inside WP Migrate, users are taken to the next screen where they can configure what is included in the export file. This ZIP archive can be dragged and dropped into the Import screen in Local.

\n\n\n\n\n\n\n\n

The WP Migrate team collaborated with the Local team to match environments as closely as possible when exporting for Local import.

\n\n\n\n

“Each site exported with WP Migrate includes a wpmigrate-export.json file which contains metadata such as the PHP and MySQL versions that were last used on the site,” WP Migrate Product Manager Kevin Hoffman said. “During the import, Local reads this file and attempts to match the environment to that of the exported site, so the local site works (and breaks!) just like its remote counterpart.”

\n\n\n\n

In this migration scenario, the WP Migrate plugin can be included in the list of plugins so it is activated on the Local site, speeding up the workflow for setting up a local development site. Previously this required configuring plugins, add-ons, and license keys across both environments.

\n\n\n\n

“In the last year, we really embraced our new identity as a full-site migration solution,” Hoffman said. “One of the goals we set for ourselves was to handle the migration of an entire site from within WP Admin without ever having to touch cPanel, phpMyAdmin, or FTP. This new workflow is the culmination of those efforts delivered as a free end-to-end solution for the WordPress community.”

\n\n\n\n

Customers who have purchased the pro version may still opt for pushing and pulling directly between sites, but this new workflow makes it easier for users (both free and paid) to set up a local development environment for the first time.

\n\n\n\n

“When we realized how much simpler we could make the remote-to-local workflow by embracing full-site exports, we reached out to the Local team who helped make it happen,” Hoffman said.

\n\n\n\n

The WP Migrate team is looking at expanding the integration beyond matching the WordPress, PHP, and MySQL versions to give users the ability to predefine migration profiles for pushing local sites back to the remote host.

\n\n\n\n

“When configuring an export, we could also let users set up one-click admin access in Local,” he said. “Imagine dropping a ZIP into Local and landing in WP Admin without ever having to log in. There are lots of possibilities, and I’m sure more will pop up as the community starts to use it.”

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 23 Jan 2023 22:39:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:5;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:84:\"WPTavern: WordPress Community Team Proposes Adopting GitHub to Improve Collaboration\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141302\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"https://wptavern.com/wordpress-community-team-proposes-adopting-github-for-improved-collaboration\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4432:\"

Although GitHub is primarily used for code collaboration, WordPress’ Community team is considering adopting the platform to standardize their project management tools.

\n\n\n\n

Contributing to open source can already be challenging but when it requires signing up for multiple services in order to access the team’s many spreadsheets, trello boards, Slack groups, and other modes of communication, onboarding new contributors becomes needlessly difficult.

\n\n\n\n

A new proposal, authored by Community team rep Leo Gopal, outlines the benefits of using GitHub as a central communication tool. These benefits include improved collaboration and communication using the platform’s commenting system and the ability to track progress and assign tasks.

\n\n\n\n

Gopal contends that standardizing on GitHub would increase transparency and accountability while supporting better organization with tools like issues, labels, milestones, and project boards.

\n\n\n\n

“By adopting GitHub for project management and issue tracking, the Community Team will standardize our way of working, making it easier for new team members to get up to speed and enabling more effective cross-team collaboration,” Gopal said. “This standardization also makes it easier for Community Team members to track progress, identify issues and make data-driven decisions.”

\n\n\n\n

Other Make teams, such as Learn, Hosting, Meta, Marketing and more, are already successfully using GitHub to manage communication and prioritize projects. Gopal proposes the Community team learn from their efforts and adopt these tooling methods for a quarter as an experiment.

\n\n\n\n

“If after the first Quarter the consensus is that this does not suit our team, we will revert back to initial project and tracking practices and explore more,” Gopal said.

\n\n\n\n

A few participants in the resulting discussion have concerns about transparency and losing track of conversations, as they would not be linked to WordPress.org profiles.

\n\n\n\n

“The truth is that I am unsure about it,” Weglot-sponsored Community team contributor Juan Hernando said. “I think the community team is not particularly technical, and using GitHub may pose certain barriers we didn’t have so far. Maybe for many people opening an issue, requesting a pull request, or similar is their everyday life, but for others, it can be a bit blocking.

\n\n\n\n

“I’m also afraid that discussions will move from this Make site to GitHub, and we shouldn’t lose the spirit of owning our content (linked to our .org profile) and lose the use of this space for decision-making and public discussions like this one.”

\n\n\n\n

Gopal addressed this concern stating that there would be no code and that users who can work with Trello boards will have no problem adopting GitHub’s tools for planning.

\n\n\n\n

“Trello was used for planning and often forgotten until time for reviews or recaps,” Gopal said. “There was no way other teams would know what we are working on or add to the conversation unless they dug up our trello boards AND if we took their suggestion and weighed it in.”

\n\n\n\n

Gopal said using GitHub would allow the team to incorporate advantages like automations, assignments, and inter-team collaboration with advanced reporting capabilities. Overall, GitHub has the potential to increase the visibility of their work for those collaborating across teams.

\n\n\n\n

Milana Cap, who uses GitHub to help organize the Documentation team for reporting issues and automating tasks, recommended adopting the platform and shared how the Docs team is using it.

\n\n\n\n

“All the other benefits: version control, precise contribution tracking, all sorts of project management tools etc., can not be found all in one tool other than GitHub, and I can not recommend it enough – for everything,” Cap said.

\n\n\n\n

Discussion is still open on the proposal and Gopal has published a Proposal Poll for Community Team members to give their feedback on standardizing communications on GitHub.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 21 Jan 2023 04:32:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:6;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:103:\"WPTavern: Gutenberg 15.0 Introduces “Sticky” Position Block Support, Adds “Paste Styles” Option\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141268\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:101:\"https://wptavern.com/gutenberg-15-0-introduces-sticky-position-block-support-adds-paste-styles-option\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3623:\"

Gutenberg 15.0 was released this week with some exciting new features for working with blocks and an improved UI for managing controls in the inspector panel. This release marks the end of the block inspector tabs experiment, which is now stabilized in the plugin.

\n\n\n\n

Users will notice that some blocks will now have separate tabs in the inspector for displaying settings and design controls, and optionally a list view tab that is included in the “off canvas navigation editor” experiment. Taking the block inspector tabs out of experimentation paves the way for the Navigation block’s off-canvas editor to become the default experience.

\n\n\n\nimage credit: Gutenberg 15.0 release post\n\n\n\n

Version 15.0 introduces a new “paste styles” feature that works in a similar way to the “paste” or “paint” formatting function in Microsoft Word or Google Docs. Users can click on any block, select “Copy block” from the menu in the block settings panel and then paste those styles onto another block using the “Paste Styles” menu item.

\n\n\n\n\n\n\n\n

When using this feature, users may have to give the browser additional permissions in order to read from the clipboard. If permissions are denied, Gutenberg will display a warning snackbar to notify the user.

\n\n\n\n

Another major feature in this release is the ability for users to give blocks “sticky” positioning on the page. This will keep the block in the viewport even when scrolling down the page. The sticky/fixed positioning sticks the block to the top of the direct parent block. It can be previewed on the frontend and equally as well inside the editor.

\n\n\n\nvideo credit: Follow-up tasks for Sticky positioning\n\n\n\n

Gutenberg contributors concluded that although sticky positioning will be valuable for headers, footers, and creative instances, it is not likely to be used frequently. For this reason, it is de-emphasized in the UI. This is the first iteration of the sticky positioning feature, and contributors are tracking a list of follow-up tasks to improve it.

\n\n\n\n

A few other important changes in this release include the following:

\n\n\n\n
    \n
  • Edit block style variations from global styles (46343)
  • \n\n\n\n
  • Constrain image sizing to the width of the container (45775)
  • \n\n\n\n
  • Allow resizing the Site Editor’s sidebar and frame (46903)
  • \n\n\n\n
  • Activate copy/cut shortcut in the site editor (45752)
  • \n
\n\n\n\n

If you want to take advantage of these new features before they land in WordPress core, you will need to have the Gutenberg plugin installed. Check out the 15.0 release post to visually explore the highlights with more videos and links to all the pull requests for the release.

\n\n\n\n


\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 21 Jan 2023 00:37:23 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:7;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"Post Status: Launching a WordPress Product in Public: Session 1\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146618\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:73:\"https://poststatus.com/launching-a-wordpress-product-in-public-session-1/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:77120:\"

Corey Maass and Cory Miller go live to discuss the creation and launch of a WordPress product they have partnered to build. Crop.Express originated as a solution to a common problem Maass experienced. Miller loved the idea and wondered how to build this into a plugin to solve problems within the WordPress workflow. This is a candid conversation about the evolution of partnering to develop a WordPress product.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

In this episode, Corey Maass and Cory Miller discuss the origin of the WordPress product they are creating. Together they explore the benefits of partnership, the challenges of being a creator, and what it takes to build viable solutions. This is only the beginning of their process and partnership, but it’s loaded with experience and insight from the journeys they have had within WordPress that brought them to this moment, as well as takeaways they’ve discovered with their new undertaking.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • The Power of Partnering: Many entrepreneurs aren’t interested in partnership. But they create an opportunity to own and contribute the things you do well alongside someone who has other skill sets, strengths, and experiences. Partnerships offer space to practice open dialogue while showing respect and gaining perspective. They are a great solution for all the things you can’t do, don’t want to do, or shouldn’t do. 
  • \n\n\n\n
  • Build for a Need: Sometimes we create things believing we have brilliant ideas that will attract an audience. But where problems exist, so do the needs for solutions. You can trust if you have a problem, other people likely have the same problem and need a solution.
  • \n\n\n\n
  • Look to Make Things Easier: When you have to go out of your workflow to do a task, things feel frustrating and clunky. Finding ways to integrate tools within our natural workflow adds tremendous value to the user experience.
  • \n\n\n\n
  • Products Require Passion and Capacity: Yes, you may have the ability to create really cool, helpful things. But if you lack a sincere passion for the products you build or truly don’t have the time they require, they tend to fall flat somewhere along the way. You tap out at the end of your skillset or energy, and even though there may be real potential, the passion and time to carry things forward are missing.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n

\"🙏\" Sponsor: GoDaddy Pro

\n\n\n\n

Manage your clients, websites, and tasks from a single dashboard with GoDaddy Pro. Perform security scans, backups, and remote updates to many sites on any host. Check up on site performance, monitor uptime and analytics, and then send reports to your clients. GoDaddy Pro is free — and designed to make your life better.

\n
\n\n\n\n
\n\"GoDaddy\n
\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

Cory and Corey Episode 1
Cory Miller: [00:00:00] Hey everybody. Welcome to a cool series. Uh, my friend Corey and I have been talking about it for a couple months, a project, and we said, Hey, why don\'t we just broadcast this out, do it in public. And so this series is kind of called Launching a WordPress product in Public. This is session one we\'re gonna talk about.
First. I\'m gonna let Corey introduce himself in just a second, but we\'re gonna talk about the agenda is, um, kind of where we\'ve been, just to catch everybody up. And then second part, we\'re gonna talk about next steps for what we\'re doing. And we\'ll of course describe the project, uh, as we go. So, Corey, I think people know you, but let\'s, let\'s, uh, go ahead and share it.
Tell us more about, uh, who you are, what you\'ve done with WordPress.
Corey Maass: Of course. Uh, so I\'m Corey Moss, currently [00:01:00] residing in the northeast of the United States. Um, I\'ve been a developer and an entrepreneur for 25 years or so, and largely locked into the WordPress space for 10 years or more. It was the day job for a very long time, and I was pushing SaaS apps or BU building and pushing SaaS apps, uh, in evenings and weekends.
And then, I don\'t know, years ago at this point, I went to, uh, WordCamp in Atlanta, Georgia, and met a few WordPress entrepreneurs, including the, um, specifically the Ninja Forms guys down there. And suddenly a light bulb went off of like, oh, there\'s, you know, there\'s a lot more to WordPress products and the WordPress ecosystem than I realized.
And. It can be used to build SaaS apps, which I also do. Um, but [00:02:00] also these plugins that can be grown and built into pot, you know, sometimes, or potentially into, into businesses under themselves. So that really kind of got me started. And so, uh, around that time, I, I learned about the Post Status community.
Uh, I\'m, I am wearing the Post Status t-shirt underneath. It\'s just too cold. Um, being up here in the northeast. But, um, yeah, so it\'s been, you know, fun to be part of the community and fun to grow. Uh, I\'ve now grown and sold a couple of businesses or a couple of WordPress plugins. Um, and here we are about to launch.
Cory Miller: Yeah, I, I\'m trying to remember back when we actually met Corey, but I knew you were like this developer who loved to like launch stuff and you had the kbo, uh, plugin at that time. Mm-hmm. , and I remember talking through that and how passionate you were, you were about it. Um, so, and then we chatted the last year or so comparing notes and I\'m like, man,[00:03:00]
Corey and Cor, sorry, the broadcast system went off on my ears. Excuse me. Just one second. Okay. Whew. That was weird. I\'ve got hearing aids and my phone comes through and I was like, emergency broadcast system. Mm-hmm. Um, but anyway, um, so it was fun. We\'ve gotten to kind of get to know each other over the last year or so and member huddles and you shared this thing you were doing and I\'ve followed up and I was like, I need this, I want this.
Um, and it\'s funny too in parallel is how much stuff that we\'ve got in common or things were stages of life we\'re, we\'re going through. And so I think it was a couple months ago you mentioned on the huddle or, and then we started talking about it in Post Status dms, the project that we\'re launching in public today called Crop Express.
But um, you wanna share a little bit about that, how you came to it? And I can add a little, my perspective on it. Yeah, of course. This was your idea. Um, and I was like, oh my God, this has [00:04:00] to exist in the WordPress. Um, I need it because I need it. And that\'s a typically if I try to keep at the user level and I\'m like, if I like something and use something, I\'m like, maybe there\'s more people out there that would need it too.
But talk about the start of Crop Express.
Corey Maass: Well, before that, I want to fill in a couple of blanks. One, yeah. Uh, you and I met when you were the keynote speaker at, uh, what was it? Word? WordCamp, y\'all. The, the WordCamp in Bur Birmingham, Alabama. I have lots of friends in. Birmingham, England spelled the same, but pronounced very different.
So I have a hard time pronouncing Birmingham . Um, but anyway, um, I was living in Nashville at the time and drove down and uh, that\'s you And I went to lunch with a couple of other people and I, I, I must have had too much of the free coffee, cuz I remember talking your ear off while we were waiting for like barbecue or something [00:05:00] and then, You turned to me at one point you were being a very good listener, I have to say.
And then at one point you turned to me and are like, aren\'t you speaking in like four minutes ? And I looked down and realized that yes, indeed, my session was starting in minutes and I still hadn\'t gotten my food. Um, and so you and the folks we were with were nice enough to bring me my food halfway through the session.
Oh, chicken and waffles. I got chicken and waffles, the weird things you remember. Anyway, . Um, but yeah, I, you and I have, uh, kept in touch over the years and then, um, I think mostly caught up over on the huddles. Um, but I, I mean, I tell that cuz it\'s sort of a fun story and a little background, but I also, I think it\'s.
It\'s a great ex, uh, example of the longevity of a lot of the relationships that I\'ve had in WordPress, in the WordPress ecosystem, the [00:06:00] WordPress community. Um, you know, once in a while I, I get approached, I know you do too, of people who are like, you know, let\'s partner, or, I see you\'re doing a thing, let\'s do a thing together with no background, no context.
Um, and I, I\'m definitely not saying that people shouldn\'t reach out, always reach out. You know, you never know what good is gonna come from, from reaching out. Um, I love that people messaged me directly on Twitter and um, and in Post Status and stuff like that, but also, you know, the long-term. Being part of any, uh, any, uh, being part of the WordPress community and culminating these relationships and staying in touch with people over years.
Cuz at this point, I lived in Nashville like eight years ago, so you and I met eight years ago and I don\'t think talked really for five years Anyway, so that was one of the things that jumped out at me. So getting onto Crop Express. So yeah, I. I built a, [00:07:00] a conbon plug in a few years ago, sold that, um, have launched and been running a couple of others.
One I\'m about to sell. Um, and, and that might actually be something to talk about at another time because I, I built it because I could, um, very typical developer. I built it because I could, but I was never really passionate about it. And so at this point, I\'m, I\'m talking to some folks about, um, selling it because I\'ve just never been able to, man, I\'ve never been able to market it, meaning I\'ve never been able to make myself market it.
Um, and plugins and these businesses, to me are still side hustles. I\'ve never been able to grow them large enough to be the, you know, my primary source of income. And so I have clients and. Right now, I\'ve, I\'ve got clients who run, uh, a couple of pretty big sort of magazine style, pretty traditional blogs, but they\'re, you know, magazine style, full, beautiful, well-written, professionally written articles and [00:08:00] stuff like that.
And they are not technical at all. So they\'re, they\'re entrepreneurs, they\'re writers, they\'re content people. Um, but they. It\'s not that they don\'t understand, they\'re very smart people, but they\'re not experienced with, or they don\'t think in terms of like, oh, all images need to be squares, or all images need to be 16, nine, so that the site looks uniform and consistently good.
Um, and no matter what I did, I, I couldn\'t make it easy enough for them to crop their images consistently. I didn\'t want to get them into Photoshop, you know, other, and that cost of Fortune. Other free editors cost money, da, da da. So anyway, um, almost on a whim, over a weekend, I bought crop.express, the domain.
Um, Here\'s a industry secret. One of, one of my best kept secrets is the.express, um, what is it? Top level domain, [00:09:00] TLD. Um, there\'s so many words that have not been bought yet, so I actually own poll.express, crop.express reply.express. Um, screenshot.express is another project I\'m building out. Um, so if you, anybody listening, if you\'re looking for a good domain, I, I highly recommend it.
I keep wondering what I\'m doing wrong or like, are there companies that can\'t access this or something, you know? Yeah. But
Cory Miller: anyway, um, I think it\'s a hallmark of any, uh, tech entrepreneur in particular is to have like a too big of a. Portfolio that you have. That\'s very continuing. Well, that\'s too, yes. Um, I, I\'ve got way too many, um, my wife is always like, you should put some parking pages on this.
And I go, yeah, but it\'s a cool domain. What happens? I think there\'s two things. Uh, we definitely should, and we\'ll be talking about partnership along this whole way. Um, I\'ve had a good amount of experience with partners and like having [00:10:00] partners. Um, it\'s an anomaly in, in, I in a lot of the entrepreneurs I\'ve talked to is a lot of successful entrepreneurs go, no way.
I\'m not gonna partner with anybody. And I go, well, I kind of need to and want to. Um, but then, so I know we\'re gonna be. Some thoughts about the partnership and that\'s another thing is partnering in public is probably the subtext to this too on. Um, but as we\'ve talked, just real quick before we get back to the product, is, um, I\'m not a developer.
I should get the shirt. I\'m not a developer. Um, but I love products and I\'ve had a product business. Um, tried a bunch of products. I told you, I think yesterday I was like, my, my win rate is probably like in the one hundreds, uh, percentile. Um, we talked about baseball and I was like, you know, I\'m probably a strikeout king because I feel like I failed quite a bit.
But coming to someone, like it\'s an ideal match for me because I can, [00:11:00] you know, business and marketing, but it\'s not one you have to own in this partnership. I can own that and you contribute and obviously I can\'t even try to write code. Um, but I can contribute with product and, and experience and thoughts like that.
So now to the crop express. . Um, so when you shared this, I was like, yes. Because my experience in just talking about the user profile, I\'m so keen to the user profile cuz sometimes I think we come at it artificially and go, I have an idea. Let\'s go find a person for it. And I think some of the best ones come out of just, there\'s a need, and we talked about this, it\'s like, um, you hear the story is build it for your own itch or build it for yourself and all that kind of stuff.
We talked about Pi, PIP and Williamson yesterday, like he\'s a, he\'s the one I think of it\'s like, build it, build something for a need. Mm-hmm. for himself and grew into this great, uh, business called [00:12:00] EDD. Um, what struck me about this is I go, I have a. Like trying to find software that will crop, you know, I used to use, I was an early user of Photoshop, but I don\'t have Photoshop on my computer.
And I\'m like, well, I go to Mac preview and crop and export it out and then try to upload it to WordPress. So instantly I go, I need this. And then I thought, and we started having these discussions. I think other people do too. You know, the classic example I have just like your clients is my mom built a her own site about 10 years ago or so.
And we had a theme, don\'t cringe too much, but a theme that had rotating images in it at the top. Sure. And I tried to load the site . It was like, oh my God. She had 15 images all at like hop resolution. And this is something real quick. Uh, we both were like, this isn\'t something easy. It may be in WordPress, but it\'s not easy in WordPress.
And [00:13:00] my natural question was, If I have this problem, I bet you a lot of people have this problem. We talked, talked about images, we talked about agencies that turn sites over to clients and end up, why is this so slow? Or why isn\'t, you know, why doesn\'t this work? Right? And it\'s like, well, you loaded it native from your phone, , uh, the pick.
And so that was the thesis for me, for the, for the product is you already had the SaaS solution. I was like, yes. My question was, can I get it into a plugin where it\'s inside WordPress in my workflow?
Corey Maass: Yeah. And, and you helped, helped me turn that corner, honestly, cuz I, in a weekend I built. crop.express, which right now the website is the website.
It\'s exactly the first version that I built. Um, it\'s, it\'s not complicated. It\'s not well thought out, too well thought out. Like I have a, I\'ve been also working in product for years, and so I, I do [00:14:00] okay with going, oh, well, this, this will be intuitive enough that somebody could muddle through it. Um, but I really wanted to just solve the problem initially for my clients and yeah, threw it online.
I love doing this anyway. Start showing it to people, showed it to you, um, and you kept, you, you nudged me a couple of times in Post Status, like, how can we make this easier? And originally I was not thinking WordPress plugin, surprisingly. Um, I was thinking more. This is just a, a great little tool that people will use and it will hopefully, you know, maybe I could throw some ads on it or I, it will refer them to my other products.
Um, and so I was building a little Chrome extension and, and you\'re like, okay, that\'s a start. But you know what, if we really start to explore this and yeah, the conversations kind of flowed from there.
Cory Miller: And my premise with products, [00:15:00] particularly with WordPress or any tool is this, there\'s a workflow we all kind of have and you get in this system and when you have to veer out of that workflow, cropping an image, finding, cropping an image.
Yeah. So clunky within WordPress, and you have to go outside of that experience. You just added unnecessary time and energy for something frustration. When most times when I\'m creating content, I go, I want to get this out and edit it and press publish and put it out in the world. And anything that slows me down is a problem.
Um, So, you know, there\'s , our featured image on Post Status. I\'m not happy with it. We\'re still working on, on some of our design on the Post Status website. Uh, my personal side, I don\'t typically use images because of this. And so I think that was some of the, my, my perspective is like, there\'s enough use case here to say let\'s try it.
And I think what you and I go is like, we want to have, we wanna do something that is practical and useful [00:16:00] and then see where it goes. Um, we\'re not looking to get like mega rich on this or anything, but like, it\'s something we both have an interest in. Let\'s see where it, I\'m counting on it, man. . Hey, it would be nice to get me wrong.
Corey Maass: We, uh, we bought the Mega Millions ticket last night. You know, it\'s over a billion, but, uh, it hasn\'t been announced that we won this morning. So, you know, this is, this is the, the next best
Cory Miller: thing. Right. Yet, you haven\'t won yet. When we get some of that, carve off a little bit of that lottery money and we\'ll throw some, we\'ll do some cool, cool products.
Um, yeah. I, I\'m really addicted to products. I\'ve loved it for the longest time. Um, you said something earlier, you said I could build this and you did build things. Mm-hmm. , but the second part I wrote down was so interesting because it\'s, my experience too is I wasn\'t passionate about it. And I know when I\'ve gotten, um, those, that equation wrong is where I\'ve really failed miserably.
Um, the project I think about at Ithe was [00:17:00] called Exchange. It was e-commerce. I was passionate about a user experience that anybody could use, but I wasn\'t as passionate about the field. We just saw a big. I saw a big market potential there. WooCommerce was out there. It was the big, still is, the big behemoth.
And I go, man, it\'s really tough to like just create a new product in WordPress or, or in WooCommerce. Let\'s create an easier path to do that. Um, that didn\'t work. We didn\'t do it. And I think part of it was, I wasn\'t supremely passionate about the, the domain we\'re in. When we talk about this, I go, I have a, I have a lot of experience with images and cropping and content that\'s bulk of my career before I, themes and Post Status was, and communications work and newspapers, journalism.
And I\'m like, you know, it\'s a factor. Everybody wants an image on the site. And so what we decided was to start with the featured image [00:18:00] cropping, that making that experience, um, really smooth and easy.
Corey Maass: So that\'s the, yeah, I think the other thing to talk about here is as a developer, as a human being, I\'ve learned this lesson.
It\'s, it\'s just cuz you can doesn\'t mean you should. Um, and for I think people like you and I, I\'m speaking for you, but I, I hope I\'m right. We, we get excited about a lot of things. It\'s easy to, to dip a toe into a lot of things. Um, but then we end up taking on too much and we get overwhelmed and everything is, you know, what is it?
Do two things and you\'re doing two things half-assed instead of one thing, whole ass. Or, you know, and we\'re never gonna limit ourselves to one thing, let\'s be honest. But having, definitely having too many things. Um, and like I\'ve. Epic trips, um, you know, which is, I, [00:19:00] I was lucky enough to do, but I came home and people were like, was this amazing?
And I was like, I don\'t know why, but it wasn\'t. And I realized that it was like, just because I had the opportunity to take the trip, like I didn\'t, I, I wasn\'t in the right mindset. All I wanted to do was be home, you know? And so just cuz I could, um, doesn\'t mean I should have. And I, I keep trying, I try to think about that when I\'m taking classes or, you know, reading books or things like that.
Um, because time is precious, right? And, um, and we can only experience so much. So anyway, all that to say, um, yeah, with other products, I\'ve definitely built them, um, just because I could. And as a developer it\'s really dangerous because like, I look at that and I\'m like, oh, that\'d be really interesting to solve those problems.
Um, and then, uh, even as soon as you mentioned a WordPress plugin, uh, I was like, okay, well we need. X, y, z we need, you know, big da da da, and, [00:20:00] and that\'s great. Like a year from now, let\'s have all those bells and whistles and let\'s have all those features and, and, you know, and expand. Um, but of course, I\'m, again, I, I, I work, I have client work and w client work and family and obligations and stuff just as you do.
Um, and so you did a really good thing where we were chatting, we scratched our heads, and you were like, well, what if we, you know, what is the MVP here? And, and even that, I couldn\'t, I was like, well, da, da, da. And you were like, okay, featured image, one thing. Let\'s just start with that. Can we, and I, as soon as you said it, it clicked for me.
I was like, that\'s, that\'s the place to start. It\'s the one simple feature that, but it will solve the problem for a lot of people, and it will exemplify the problem we are trying to solve. . And so, and, and again, for me, it, it is tough at times as a developer, all [00:21:00] things are possible. Mm-hmm. , I mean, not literally, but, um, and that\'s, it\'s powerful but dangerous and I\'m, I\'m trying, you\'re, you are being, uh, non a not a developer and having a history of using this kind of thing is immensely valuable.
Um, keeping my feet grounded. And I\'m trying to do the same with thinking from the perspective of my clients, because again, they were the ones that inspired this, so what\'s gonna solve the problem for them? And that\'s where we, that\'s kind of where we\'ve landed and what we\'re getting pretty close to being able to launch.
Cory Miller: I, I think, um, the experience you talked about is like, everything is when , another shirt we should do, when everything is possible, everything sucks. Because when you have, when you\'re in the experience, I know this and I\'ve been. Uh, led teams of developers. I get it. Like, and I have the, I guess I\'ll say a gift in this sense of going, I don\'t know what all [00:22:00] is possible and it helps focus, but I think that\'s where, again, a partner comes in.
I struggle with this in different areas, um, where I\'m like, well, everything is possible. Everything sucks. And I, I lose focus in that. Um, and that\'s something I really enjoy being able to do is like, you worry about everything is possible and I can help just to ask questions. Um, and when we\'re, we talked about the MVP, I think about that iconic, um, like cartoon of this, the stages of an v mvp, how, how you start with an MVP and grow it.
And the one I like best, it feels a lot of theory and cool, like to try to plan this out like this, but it\'s like, what\'s the skateboard version of the. Bike or whatever, you know, the product becomes and it\'s not, uh, a skateboard. And then you add a seat and then you add handle bars to the skateboard and you try to build out.
And I\'m like, that\'s cool in theory. But [00:23:00] I think what this does is, the way we thought about this was what is a, a toe in the market that does solve that problem that can grow? Um, and, you know, marketing and technical and business questions come out of this. And I just saw one yesterday, uh, I can\'t remember his name on Twitter, but I replied to him.
He was trying to think like, where does this thing go? You know, like you start with the skateboard, but well, what if we want to do this with Crop Express and that with crop, you know? And, um, a lot of times, I think some of the best products have been part of grew organically instead of trying to say this is the end product, it was responding to customer needs and opportunities and grow out.
And sometimes maybe it grew into a little bit of a mess out here that we kind of had to make some hard decisions, um, with our ITM security product there for sure. And then backup Buddy over time. Um, we saw that, but it, I think it stays close to the customer [00:24:00] when somebody goes, I will pay money for this.
You go, oh, there\'s magic there because we, we might have something here. Um, and I, we decided, and we should talk about this decision too, we decided to release Crop Express as a free plug in first on the.org repo. We\'ll be talking about that experience as we go. We\'re not there yet, but we\'re really close to releasing the v mvp V1 in the repo.
Uh, and then, but what I like Corey, is we\'ve done this in a way to give us options or paths to go. We\'re not, we didn\'t try to build the bicycle and launch that as a premium product. We said, what time resources do we have? And that mvp all that went into this conversation you and I had of like this.
Okay, let\'s come down to if we can get this point, and that\'s in the stream of people\'s workflow. You know, you\'re firing and proposed headline, okay, I need my future. You\'re gonna go over here, click feature damage. And that\'s where [00:25:00] Crop Crop Express is gonna help you. And I don\'t, you know, you\'ve been great to navigate us technically, where we\'re not gonna hit a dead end on something.
Um, but that\'s the part of this adventure. You never know where you\'re gonna go with it. Right.
Corey Maass: And I\'ve, uh, you know, we\'ve already touched on a, a bunch of things that I see questions about all the time, like part of the MVP. Uh, I\'m, I\'m a, I\'m a good developer, but I have very limited experience with Gutenberg, um, excuse me, the block editor.
Um, and even, and so we, we are looking at doing a custom block down the road, version 1.2 or whatever. Um, but even to get, uh, just the, to, to work with just the featured image. Like I didn\'t have experience with the panels, uh, inside the block editor. And so I looked at it, I hacked at it for a [00:26:00] little while, and then I said, okay, you know what, I\'ve got a buddy who can help me out with this.
So, hired him for a couple of hours to get me over the hump. Um, you know, and so. There\'s that, there\'s again, the partnering, uh, you and I working together, um, which we haven\'t really flushed out, but we\'re kind of excited to do, um, launching something, putting something in, in the plugin directory is, is its own experience.
Um, and so yeah, I think there\'s, there\'s a lot of different things here that if nothing else, just getting that, you know, the tip of the iceberg. Um, or I\'m mixing metaphors here. But anyway, you know, just getting this thing out the door and, and starting, um, is, is where a lot of, uh, a lot of questions arise and there\'s, there\'s a lot of hurdles, you know, unto itself.
But, um, you know, I think the, one of the things that I really like about WordPress is that. It does require, or [00:27:00] WordPress plugins, WordPress products, it does require development, no question. Um, I don\'t think there\'s a big overlap yet enough of an overlap yet with like, no code products, um, services out there that, you know, people are building products against to then somehow get that into WordPress.
Um, but it doesn\'t have to be a huge lift. It doesn\'t have to be like, some of the best, um, plugins out. There are one single feature or, you know, single file, um, the, the plugin that we have so far that, that gets the featured image. Cropped and, and injected into a post is, is still basically just two files.
You know, it\'s not complicated. It\'s not this big convoluted thing. Um, I\'ve got, uh, from, you know, from a nerd perspective, like there\'s a couple of developer patterns that I\'m using, but there, there [00:28:00] aren\'t frameworks. We\'re using a library that, you know, does the cropping for us, cuz there\'s no way I\'m stepping into that quagmire.
Um, you know, but we\'ll grow from there. I mean, and I think that that\'s, that\'s the big difference. It\'s like, yes, we wanna launch something that is useful, um, and complete unto itself, but it can be, it can start as a feature and grow.
Cory Miller: How, how has this experience differed from your past product experiences?
Um, you know, you, you released, let\'s say the CommonBond different plugins on your own. I think, um, were, were similar problems and questions. That we\'ve talked about just in this, I don\'t know, month or so we\'ve actually gotten real serious about it. No, it\'s probably what, three, four weeks I think. . Yeah. Um, but like did you have similar things like that as a developer when you were doing like the combine?
Or did you just go, okay, this is what I want to build and you knew like the N V P V one V two kind of sorted [00:29:00] it out. How did those experience go in comparison to this one?
Corey Maass: Yeah, the con bond, I really, I wanted the name space. That\'s the thing that sticks in my mind. This was, you know, eight years ago now.
Um, so I don\'t, I don\'t remember everything, but we, same sort of experience. I was working at a startup and we needed a conbon solution. Um, Trello has. Rubbed me the wrong way. I don\'t know why. Um, and, and it was then that I was first starting to look at, so another, I\'ll give away another one of my secrets here is honestly, I often look for a, um, blue o, well, red Ocean SaaS solution or SaaS app that I can put into WordPress.
Um, and so with something like Trello, I was like, you know, we are, we are working in [00:30:00] WordPress, um, but we have to go over to Trello and, and do stuff. And for whatever reason, I didn\'t like Trello anyway. Um, and so that\'s part of what made me go, oh yes, if we had a CONBON board built into WordPress, so like posts were your cards or whatever, like, this makes sense anyway.
And so I cranked out a first version, very clunky and. Mostly just because I, I wanted to, I\'m trying to think if I had actually put a plugin in the repo before that. I don\'t, no. I had, I had, but years before. And so it was, it was really a new experience for me. Um, and I made all sorts of mistakes and I was listening to, like, one of the biggest ones was, um, I kept going back and forth.
Coming from, coming from a tra a, um, a, an a developer perspective outside of WordPress, [00:31:00] I wanted to do custom tables. And I was like, no. The word pressy way is you have to use the post tables. And I swear, the week after I released it, I heard an episode of, um, back when Pippin and Brad Ard had their podcast pippin\'s, like one of the greatest regrets of my life was using the post post table for e d D.
And that was like the beginning of when they were trying to release version three, which took them years to, to untangle, basically. I was like, crap. So right away I had to untangle my own thing, which thankfully only had 50 users or something, but I had to, you know, build a migration there and stuff like that.
Um, and then I think there\'s Go ahead. Go ahead, go ahead. Well just, you know, and, but there were, I I think maybe part of your question is like, There was, there were, I was solving bigger problems, you know? Um, whereas this, I think is like, I, I like, I mean, part of why [00:32:00] the, the light bulb went off when you were like, no, just featured image to start with.
So it just, it kept it focused, you know? And that\'s so much easier. Again, like I, I hacked away for a month or two months, you know, to get a working Now conbon board is a more complicated problem than, than what we\'re talking about. But, um, you know, but it, it, it was a much bigger lift to get it out the door, which I don\'t, I don\'t think is the right thing to do.
You know, you, you need, you need, especially talking about customers and clients and users, you need something. You need to get people using it as fast as possible.
Cory Miller: I, I think they\'re, I\'m seeing two paths that when you\'re launching a product, there\'s the technical path and the business path. Um, particularly if you want to monetize from it.
Um, but technical, I saw my teams for years. It was like, I, I always describe development as a, uh, an adventure and territory. You don\'t always know like, what\'s, what\'s gonna [00:33:00] come over the next hill. You could hit a swamp and end up drudging through a swamp or get sidetracked totally off on a minor bug. And so some of the things I started watching over the years is like, it, it\'s, it\'s a tough gig with the technical cuz you got a roadmap for potential.
You don\'t know where all the terrain\'s going cause you don\'t know where the business case is gonna come from, the use case. Um, and I just think it\'s like a blind expedition oftentimes. Like, so what we would do is, and we\'re doing this now too, is just kind of check in and see how we\'re going. And I valued having someone else external watching to at least kind of keep track.
And then I\'ll say this on the business side. Same thing. There\'s potential here. I see potential here from a business, business case. I don\'t know what it is. I\'m not even gonna be foolish enough to try to predict, but there\'s something here, I think. And um, because I don\'t predict anymore, by the way, Corey, because I\'m wrong most of the times when I try to predict, [00:34:00] oh, this is gonna be $20,000 a month, you know, MRR kind of product.
Yeah. I go, there\'s maybe a hope for those things, but I never predict or promise because if I get too mired in that, I start to get too f a little bit off of focus. Because some of the questions we\'ve talked about is, okay, free plugin, what do we do there? We felt it was, at least for our collaboration here, partnership, we want to do this.
We want this in the world, you know? Um, we think though putting it in the world has the potential for something that could grow into. Something We don\'t even, but I, I say this cuz we, we said, I love every time you say something like, Hey, I think we should do this. I\'m like, right on. We should be honest. We should be authentic and share the experience.
I think too, oftentimes in business and stuff, it\'s like, this is the way I felt when I left eye themes is like the pressure real or unreal. Hey, [00:35:00] Corey did this, oh, what\'s his next thing gonna be? And I was like, she, uh, let\'s see here. Um, I don\'t know. I followed the trail, um, and kept following that trail and trying to keep going on that trail for as long as we could.
Um, th this, I just like the fact that. One of the questions I try to ask myself before I begin any new venture or partnership is, what if it fails? What\'s the worst that can happen? You know? And what\'s great is we\'ve been talking about those things along where we manage it. I know when you hired the, the friend to help with some of that stuff, I was like, well, how much is that?
And, you know, do you need me to share it? And you\'re like, Hey, for now, let\'s just, I\'m gonna keep track of it. But, uh, to see where it goes and, um, I think that\'s healthy. That open dialogue and conversation where you respect each other, what each other knows. And know just because you\'re a developer doesn\'t mean you, you have a ton of insight and feedback [00:36:00] and perspectives to share on both business and marketing.
And, but it, it, it, I don\'t know. I see those two pasts. This is the one I\'ll tell you ahead of time, Corey is I\'ll struggle with, is when we get to the point we\'re like, okay, how much should we charge for? , it\'s oftentimes feels like this meandering thing of like, okay, and I\'ll need the same for you to go.
Sure. Hey, what if we do this? Um, because if everything\'s an option, everything sucks. .
Corey Maass: Yeah. I, so a couple of things that you touched on, like, it, this needs to exist in the world. I haven\'t found a better solution. So hiring somebody to get us over the hill immediately was worth it. And just like you said, if it, if it fails, if it never makes, uh, A dollar if you and I af after this call are like, yeah, I don\'t like you in the end it turns out, let\'s just call it, it\'s like, no, it was still money well spent.
You know, and I, I understand that I, I am in [00:37:00] the very fortunate position to have a, a little money that I can throw towards a project like this, but it\'s, it\'s very limited. And I, I think of this type of stuff as a hobby. Um, and there\'s been a lot of life choices that have gone into inclu, especially with, with my, my wife talking about like, okay, what is, if, if this is a hobby, what is an appropriate amount of money to spend on it?
Cuz there were times 20 years ago when I first started building SaaS apps that I was like, every spare dollar that I have is gonna go back into this without thinking about it. Um, because everything I ever think of is brilliant and every product I launch is undoubtedly gonna make me millions. Um, Spoiler alert.
None of it has yet, yet. Um, but uh, you know, yeah, we, we, we gotta start somewhere. Um, and, uh, I\'m with you. So I, I\'m also looking [00:38:00] forward to, like, I\'ve been, I met, it was, it was at a, it wasn\'t a WordCamp, it was like, um, what are they called? Free camp, or there\'s, there\'s conferences where it\'s like anybody can sign up to talk about anything.
Um, and it\'s sort of tech specific. But anyway, I met a young woman, uh, who was a developer and she had lucked onto a client who became a partner, um, who was an older guy who ran, I don\'t remember, an advertising agency, but he had access to an, a pool of customers, basically. And so he would tell her what to build.
and then he would sell it to his audience and they just kept cranking out products. And I was like, okay. Despite being an only child, and despite my first instinct being to do everything by myself, you know, there are things that I can\'t do. There [00:39:00] are things that I don\'t wanna do. and, and things that I shouldn\'t do.
So I\'m happy to weigh in on, you know, as, as your owning, marketing and your owning business, I, I want to weigh in, I want to have opinions, I want to make suggestions. And, you know, I think you and I have established that we, the expectation is that, you know, we, there\'s, there\'s going to be quite a bit of overlap in our concentric circles.
Um, but we, we each are gonna own a lane, which I think makes a huge difference. Um, and we\'re also able to sort of look over the cubicle wall to the other person and say, Hey, you know, like I, I touched on earlier, just cuz I can, doesn\'t mean I shouldn\'t, I\'m. Not going to want. There\'s going to be times where I, I\'m going, I\'m not going to want to build what I need to build.
Like there\'s a feature that every client is clamoring for. You are finally confident. You\'re like, they will all pay X number of dollars if you [00:40:00] just add this. And I\'m gonna be like, yeah, but we need a dark mode or some ridiculous thing that\'s just gonna be more fun to build. Um, and I think there\'s definitely going to be points where, you know, I, we\'re essentially going to need to be each other\'s bosses.
Um, and that\'s going to be interesting and going to be difficult at times. But I, but I think good, you know, you, you, you need other people. There are people out there that are, there are exceptions to this of course, but you know, I, I think we\'ve pretty well established that both you and I do better if nothing else.
Having a sounding board, having somebody else who\'s as invested, um, you know, and helps keeps us, keep us on the line we\'re supposed to be on.
Cory Miller: Yeah. On that note too, um, the partnership side of things where I, I\'ve been in circumstances where, okay, this is Mon Lane, that\'s your lane. [00:41:00] And sometimes, like you were really good to ask me what part of the development do you want to contribute to?
And I said, my strengths through trial and error. By the way, I think my contribution strengths are u UI experience, like how things flow. Um, I obsess over there cuz I want them to be as fast as possible. Mm-hmm. intuitive as possible. Knowing some of my, probably I\'m gonna have to freshen up on some things.
And the other is I said, you gotta be careful with me because I will share all of these things that I would love to see, but we\'ve like, But we gotta put \'em on a, a feature roadmap, A backlog somewhere. Because I said, and I told you this, I said, be careful cuz I\'ll come in and go, what about this, what about that?
And what I had to tell my team too, and I told you is like, please don\'t unless I go, can we get this in the next release? Please don\'t think that. Let\'s do this right now. And that\'s the [00:42:00] idea Fairy in me is mm-hmm. . Uh, but, and so an example of that was we have a square coop cropper. And I was like, okay, I\'m introducing the new customer story here, which is my own, every, the Posts newsletter has those little circles in them for all the, and I\'m like, that is a pain in the butt to do.
Now I flag that because I go, if I\'m the, uh, kind of a typical user, I don. Know how, how to crop that, you know, there\'s tools out there, right? But like I go, there\'s an experience if, if someone has that and I go, Hey, what about a circle cropper? And then I knew you were going to like chase it , and I was like, Hey, hey, hey.
Not for this one unless it\'s an easy thing. This was that back and forth I did with Right. All the developers I\'ve worked with too is just like, please don\'t say, please don\'t interpret that as, can we do this right now? Um, sometimes I\'ll be like, can we do this right now? Because I\'ll, I\'ll feel [00:43:00] like we got something here.
Um, but then you\'re like, okay. I was like, well,
Corey Maass: it\'s just cuz you can doesn\'t mean you should. Yeah. But there\'s also, you know, you and I, I, I also get the sense, we haven\'t talked about this, but I get the sense that we both trust our instincts pretty well, um, when it comes to product. You know, and I\'ve, I\'ve been, I.
Studying product, looking at product. Um, for years and years and years, I\'ve got, you know, books on architecture. And, uh, the, one of my favorite books about, about the Bowhouse School is sitting next to me. I mean, things like this and like, I nerd out about this stuff. And so, um, I\'m not saying I\'m an expert, I\'m not trained in any way, but like, I think I like a lot of people we know, you know, I, I, I love putting, I love loading an app and putting it in front of my mom.
You know, who\'s, who\'s not trained in any way. She has [00:44:00] a little bit of an artistic background. Um, but she is a power user. I mean, she, at this point, she doesn\'t even have a computer. She does everything on her iPad. Bless her heart, honestly, because. Trying to book tickets or, you know, I mean, things that she does on her iPad, I, I didn\'t think possible, um, even, which really is just in a browser and, and her fingertip, you know, but gets an unbelievable amount of stuff done.
But I love putting things in front of her and saying, you know, show me how you would muddle through this. Um, and, and anyway, so all of this to say that I, I trust my instinct a lot of the time, um, when, when somebody mentions a feature to me of like, oh, this is worth doing right now. Even if it, yes, it\'s not mission critical, you know, we haven\'t released yet, so technically any feature other than one feature is, is enough.
But I was like, not only [00:45:00] do, is there not a image cropper for WordPress the way that we want. Out there, but I really don\'t think any of \'em do circles. And again, my clients for most of their stories featured images are 16, nine or square. But for whatever reason, there\'s that, that now that browser pattern where avatars people are circles.
And so, you know, let me see if I can, I can crank this out and it\'s, and it\'s fun. Um, and sure enough, like, like you said, it, it wasn\'t a big lift, but yeah, I think, I think you and I will, we\'re just gonna have to figure that stuff out. Like everything, everything goes on a backlog. Everything gets discussed at least a little bit.
Um, but I also, you know, I don\'t, I don\'t think that there\'s harm in, you know, there\'s low hanging fruit, there\'s return on investment. There\'s lots of different ways to put it. [00:46:00] It\'s like, oh, well if we, you know, if we make all the buttons green, you know, is it, does the user benefit? No. You know, so just cuz it takes a minute isn\'t worth it.
But, you know, we\'re, we\'re just gonna have to, and, and I liked what you said too, of like, we, we are gonna have to, I guess this is the other, the other benefit of trying to get this thing out the door is like, get people using it, talk to people using it. Um, you know, being part of a, a community like Post Status, um, there\'s the great, um, advanced WordPress Facebook group.
Like there\'s, there\'s places that. You and I have been involved for a long time, kind of regardless of, of our actual position within those communities. But, you know, trying to add value or trying to Twitter to trying to just, you reply to tweets for months and then you hope that when you, you do something and you need somebody else to reply that, they will.
So it\'s like, let\'s get this thing out there. Let\'s see what people think. [00:47:00] Give it a try. Um, you know, and, and follow, follow our.
Cory Miller: This is where I struggle back and forth with product. But my typical mo, what I feel instinct is you, uh, there\'s product people that are just genius and gifted. They\'re like, here, you know?
And you\'re like, God, okay, cool. Uh, but for mere mortals, um, for me it\'s been put something enough out there, check some boxes. Okay, is this something you think we need? Like, does anybody even need it? Because I put those things out there, I\'m like, put \'em out there. Not necessarily products, but other things.
I\'m like, nobody\'s even asking for this. And a lot of the entrepreneurial books and stuff, it\'s like, okay, how you scientifically go down it? And I go, it\'s art and science. Yeah, it\'s a blend. It\'s this alchemy and magic of like, but I know the power of like putting something out there and that creates enough a ripple where you get a feedback loop and, um, [00:48:00] That was so helpful along the way when you get feedback like, I, I, we feel this is a good, this is a good V one, solve somebody\'s problem, that laser beam, you know, thing of what we\'re doing for it.
Um, but what I\'m most looking forward to the product is how people react when you hear those. Like, um, backup buddy was in development, uh, and then, I can\'t remember, 2009, 2010, and I, we were at, we had a little group thing where, and this, these two twin brothers ran an agency and I just, this wasn\'t something somebody told me.
I was just like, Hey. We\'re doing this thing and this plugin, and it helps you do, um, basically, uh, backup, restore, and migrate websites. By the way, those were not things that came from me. They came from Dustin Bolton and Christine and I themes, they\'re like, no, a backup needs to do these three things. Okay, okay, let\'s do it.
Sounds good to me. But I mentioned to them [00:49:00] the migrate, or what was it? The migrate side and just in passing, and they, their eyes lit up and they go, we pay somebody $300 to do, to do that now. Wow. Consider the time and everything. This is back in the day. And I was like, okay, I think we got something.
Because, you know, and then we just try to, okay, I think we\'re gonna keep going, keep doing, we obviously launch it, we\'re gonna launch it no matter what. But um, that\'s where I was like those moments where someone lights up and they\'re. Can I pay you now? The shut up pay, shut up. Let me pay you thing. Right? I was like, shut up.
You can take my money. Shut up and take my money. That\'s a magical moment. Um, I think times I\'ve tried to force it, um, and it\'s just, it\'s not, or create a category you hear that\'s not, and I\'m like, cool. Yeah. For those a hundred people out there that have that insane genius to create a category, most of us stumble into it.
Right. You know, um, the garage stories for startup [00:50:00] stories are always make me laugh. Cause I\'m like, what was the background? What was the context? I\'m like, that\'s a sexy headline. We started in a garage and here we are, apple. I\'m like, that\'s a sexy headline. Don\'t, and I like it. Don\'t get me wrong, but I\'m like, what Were all the actual moments, the places you got phenomenally lucky.
I know there\'s a big part of mine luck and every time I\'ve tried to time it and like, okay, I\'m gonna ride this thing, it just hasn\'t worked. And that\'s why I really like her direction with this. Um, Because we kind of had a fleeting thought of like, I think as I recall, like this could be a paid product.
Um, you know, I don\'t even know if we entertained much of starting with a paid, we\'re like, let\'s just do the free plugin. And I will say, remember actually, um, give you credit for this too, is I think I said, what about a Gutenberg block? Put it in editor. So upload image crop, boom, I\'m there. My workflow\'s fast, efficient.
And, [00:51:00] um, you, you looked into that, you chased a little bit of it and I said, Hey, there\'s some roadblocks here. And that\'s that collaboration of how we go, okay, featured image, what if we started right here? We want to grow potentially into that. You know, I think the idea in this, and we\'re, I think we\'re both verbal processors, but is the thesis is start here and it\'ll grow into.
Block, like the inline process where you\'re in the thing and you\'re having the same problem, I need to crop it, figure out right. Dimensions and all that. Um, so I don\'t know where I was going with that other than to say that was some of the background too of decisions and knowing like you could hit a dead end.
And I\'m waiting for that. I think we\'re putting ourselves out there with this to see if there\'s magic in this. Yeah. Journey.
Corey Maass: Yeah. A couple of things you said, um, stuck out to me. One is [00:52:00] like a lot, everybody builds products differently. Everybody b builds UI differently. WordPress has very soft wall, has a lot of walls, but they\'re very soft and there\'s a lot of discussion, often negative, often complaints about, um, The, the experience that a plugin provides.
And I think what\'s different about WordPress, right, is like often you\'ll, you\'ll go to Trello and you interact with Trello, and you go to Slack and you interact with Slack in WordPress, you\'re essentially interacting with numerous apps, really numerous UIs, side by side. Um, and the tolerance for terrible ui.
I mean, let\'s be honest, even WordPress is not great anymore. Um, the tolerance is high for what you can [00:53:00] get done. Uh, and so I think that that\'s, that\'s an, that\'s something that I hadn\'t really thought about, but it\'s like things you can get away with in WordPress as long as you can solve the problem. And so there\'s, there\'s a lot to be said for, bless you.
There\'s a lot to be said for. Solving the problem, um, and not getting caught up in the genius of a product. You know, cuz like you said, people, people wanna get it done and get out, you know, get on with their lives. Um, the other thing that I\'ve had a lot of luck with, so I think we should do this here, is talking about that feedback loop.
Um, with Conbon, I put myself on the homepage and had a, and, and had a nice. Response. Um, with, uh, there\'s an online game that I built during the pandemic that, that I\'ve told you about, um, called Mexican Train [00:54:00] in the web websites, Mexican train.online. So if anybody out there wants to play Mexican train, which is a Domino\'s game, but I built an online version, um, I put myself on the homepage and it\'s a game that is played by a lot of seniors and especially during the pandemic when everybody was really locked down.
And then even now a lot of seniors are still trying to stay inside, stay safe, stay more isolated than they were before. Um, and isolated being the word. They use the game to keep interacting with their friends, um, which is just amazing. Um, but they. Not only does every email that come in start with, Hey Corey, because I am on the homepage.
Um, but apparently when, like, there, there are groups of people that play every week and even every day and uh, they curse me when they get bad dominoes. They praise my name when they get good dominoes. Um, the picture is of me [00:55:00] eating cheezits cuz it\'s sort of as a joke, like, Cheezits are a guilty pleasure for me.
So a number of them actually like, go and buy Cheezits and eat Cheezits while they\'re playing because it\'s become a, you know, uh, a thing. Um, inside joke I guess is the, you know, uh, or whatever. Um, but there\'s the, that feedback loop is definitely there. Like, they talk to Corey, you know, and then even with.
Subsequent products that I\'ve built, me being on the homepage with a blurb about like why I started the Solve the Problem and stuff like that, has made a huge difference. And so I think as, at least early on, that\'s something that you and I should definitely replicate is, you know, as we\'re se I mean, we\'ll we\'ll send this to our friends and family.
Okay, that\'s easy, that\'s obvious. But, um, you know, maybe even building in a mechanism that\'s like, you know, Hey, it\'s your favorite. Corey and Corey, like, tell us what you think. What do you, you know, um, does this work for [00:56:00] you? Does this not work for you? That kind of thing. I usually don\'t think about explicitly collecting feedback until further down the road.
Um, usually wanting to focus on like paid customers and that kind of thing, but, you know, maybe it\'s something we start with sooner than later.
Cory Miller: I definitely think so, because, you know, so many times I\'ve put products out there and not really made that splash. Like, you know, they\'re like, okay, there\'s practical, they\'re doing this thing, um, that we set out to do, but I think you wanna have push, push it to have an opinion.
Mm-hmm. , you know, like the user to have a reaction to it, enough to say it sucks or it\'s awesome. Um, some, some way of that to see where you\'re at. I think both if you get it sucks and it\'s awesome. You\'ve got some validation there, you\'ve got something. Um, but putting things out there, that\'s [00:57:00] how I, my mo with products.
So 2006 or seven I think I, I launched, I did launch, I guess, uh, this is way back in Word Press was different, but I launched a theme and put my zip file. Uploaded it to.org. People downloaded it and I was like, this is crazy. I got a response from them, which I had a contact form up , you know, my website linked in the theme and stuff, and they\'re like, will you build blog for me?
And I was like, whoa. I\'m learning. I did this too because I wanted to do it and I\'m learning. But that\'s the magic that when you put something out there. Yeah. But I think there\'s this case for put something out there that kind of pushes a reaction. You know,
Corey Maass: and I think this will be an interesting point of conflict potentially, is uh, there\'s going to be a point where.
We\'re, we\'re going to see different paths and we\'re gonna want different features too. And so I think this is, that\'ll be an [00:58:00] interesting, you know, let\'s try to have that conversation on camera because it\'s there. There\'s points where I\'m dogmatic, like I\'ve got my, one of my other plugins is like, like I said, I, I often look at products that are out, out on, out in the wild and I repurposed them inside WordPress.
And so I\'ve, I\'ve got a plugin that\'s kind of like a link tree or a card or an About me where it builds very simple social focused landing pages. Like the link bio pages is kind of the, the phrase most people think of. And uh, and even like when I submitted it, the, the people reviewing the plugin were like, um, you\'ve kind of built WordPress inside WordPress.
And so I still get a lot of requests for features that are beyond. The point of the product, because it is within, like WordPress using the right theme or page builder, you can do literally anything. [00:59:00] So this is supposed to be very focused and people come in, come, come in and are like, well make it do this.
And I\'m like, that makes no sense. Like, go use WordPress. Um, and so I have found myself being more and more dogmatic about like, my own vision or, you know, certain vision for a product. Um, you know, and right now, like you and I have it easy, like we know it, it it\'s a one trick pony or one and a half if we do circles.
Um, you know, so what\'s, what\'s the next thing that I think that\'ll, and, and, you know, in a year down the road, I think that\'ll be interesting. Um, again, that, that backlog, you\'re probably gonna end up hearing more feedback than I am. Um, you know, uh, Product ownership might ha end up being a thing that we, we actually have to sort out.
So, and it\'ll be an interesting ride.
Cory Miller: Well, that\'s been a lot of the background, um, that we wanted to share and kind of catch you all up since we were, were launching [01:00:00] this live or in public. Um, but catching you up on some of the background, some of those key conversations. I hope people can use some of this to, uh, inform their own product journey.
Um, where we are today, where are we today, Corey, with the actual product? Sure. Um,
Corey Maass: yeah, and I just to add to what you just said, like as people watch this, there are a few people watching live. Um, my expectation, like most things recorded is, you know, more people are going to watch it on the playback. Um, but we are going to.
Looking at comments, and I think both of us are pretty easy to find. Um, you know, so, so as, as the, as the conversation gets started, you know, I encourage anybody listening, please ask us questions, you know, give including hard questions. You know, what do you want us to talk about? What do you want? What questions do you want our answers to?[01:01:00]
Um, not that we have the answers to all these problems, but you know, this is, we\'re doing this out loud, recorded on the internet, you know, so we\'re happy to talk about it. Um, and we\'re both pretty candid out, outspoken kind of people. So we\'re, we\'re happy to talk about prayer, pretty much anything. Um, but anyway, where are we at now?
Um, so I, with, again, with the, the help of a freelancer built, uh, a first version, I did the p h P. Um, he helped get the. JavaScript and React part of the, um, panel inside of the block editor integrated. Um, and then I took the, the cropping library that we\'re using, stuck that in. Um, and we\'ve, we\'ve gotten pretty far with that.
The, what, what we had been limited to for the last couple of weeks [01:02:00] is the selecting of an image. So, you know, nobody\'s, nobody\'s seen this yet. So talking through the flow real quick, you\'re opening up a, a new post in WordPress. There\'s, you know, the built-in featured image panel on the right. Um, we\'re essentially replacing.
It looks very similar to the built-in one intentionally, but when you click on it, instead of it opening the media library where you upload an image or select an image, it uploads a, uh, or excuse me, it opens a modal where it says What shape do you want a crop? Um, it does say, do you want a circle? Um, you select an image from your hard drive, it then opens the crop.
And one of the nice things about this kind of tech is that that image is not uploaded yet. And so it\'s all just in the browser until you say, okay, set this, you know, I\'ve moved the crop. I want it this part. Set that as the featured image and that\'s what gets uploaded. [01:03:00] Um, as of today, I got a poll request again from my freelancer who helped me get started with the media library, cuz this is the one thing.
I\'m, I\'m undermining you here, but you said, I really want circles. To me, I was like, that\'s a differentiator. We need circles. Um, to, from my perspective, I\'m saying also we need very basic media library integration. I think you originally suggested this as a nice to have, and I was like, no, you\'re right like this.
To launch with, you need to be able to select an image that has already been uploaded or select an image from your hard drive, crop it and set it. Um, and so we\'re, we\'re pretty much there. The media library is opening and you can select an image. Um, so I need to do a, a couple more hours of development, I think, to get it so that it\'ll save that essentially re cropped version of what is in your media library.
Um, [01:04:00] and then from a d a product standpoint, we\'re pretty much ready to go, um, on, on your list. Um, I know we have the readme.
Cory Miller: That\'s, it was like, Hey, Corey, you have 15 minutes of work to do. .
Corey Maass: That\'s not true. I mean, it, it is to get it in the repo because it\'s one of those, you know, no, nobody does it if a tree falls in the wo if a plugin gets committed to the repo and there\'s nobody there to hear it. Yeah. Um, you know, or, or security by obfuscation kind of thing.
But, you know, there\'s, it\'s the beginning of the marketing. How do we describe this thing? What do we even really, what do we call it? You know, is it, is it crop express? Is it crop express image cropper? Is it image, crop express, da da da da da. Like, just, we have the domain, but that\'s it. So there\'s,
Cory Miller: uh, it presents a lot of questions.
[01:05:00] Um, and I know we\'ve run outta time, um, but it presents a lot of questions because you go, there\'s wordpress.org plugin search that is, Pretty big, right? Um, the, these are some of the things coming outta my mind with the readme because it does turn into that plug-in repo section. Um, I\'ve seen a bunch throughout the years how people like, enough there to go.
Here it is. And then my balancing act is, let\'s get enough to show this is the value proposition, this is what it can do for you. Uh, and then just like everything iterate over time. Um, but I can\'t help but tell and admit to you. I think, oh, it\'s gotta be like side bki put a plugin on the repo. Like he knows he\'s a marketer, he\'s got all these talents, but he, he understands how to put a plugin, um, and showcase it, right?
And so I\'m battling that a little bit, but I go, okay, get enough to, so here\'s the value prop and that this is an active development and we want that [01:06:00] feedback loop back about what\'s next. But I think the read me is showing. Telling enough of what we\'re trying to do where someone goes, that is a problem. I have this plugin, will will solve it.
Now getting to that is gonna be, is gonna be fun, but I started on the Readme file from the Generate WP site you gave me. And um, that\'s where I\'ll honestly spin some wheels a little bit, cuz I\'ll try to be perfect. But I think the two outcomes there really are, you know, clearly understanding what this does.
So someone, mm-hmm can go, oh, I\'ve got this problem, or my client\'s got this problem. And then second is, we need a loop. We need to know these things. Even the things you go, we\'re never gonna do. I still want to have \'em up there. I still want to have \'em in our visibility because it just allows us to make better informed decisions as we over time hone in on, you know, A lot of the products we [01:07:00] released at I themes, it was years before we go, oh, that group right there, because you get enough of big sample size and you go, okay, convert Kit had a very similar, uh, fault, Nathan Berry.
He started out with one thought in mine, and then he saw it was this creators, you know, um, economy. And then he just, when he got that bead, he just, you know, doubled down on that. And I, I see, I see that similar here. I think we have pretty good profiles, like anyone that wants to make image cropping easier, um, and faster from a blogger to an agency doing work for clients, um, that\'s a big use case for me.
And I\'m like, there\'s, that\'s why I have some faith that there\'s something here that we can do in an advanced case, but it\'s just discovery to me, you know, so.
Corey Maass: Yeah. Well, and I think that\'s part of, I, I think you should take notes on your experience and then tell me about it. The next time we have a call, like [01:08:00] mm-hmm.
you are a, apparently you launched a pro a theme many years ago, , uh, but have it since. And so when I was like, okay, you go, go and do the read me. You were like, uh, I need some guidance. Like I, yes, I can write words, but tell me more about the Read me and what are the consequences of, you know, the, what I put in the read Me.
Um, and I think that that\'s, you know, you, here\'s a prime example of your experiencing something for the first time. You know, tell us about that experience and, and, and the thinking, some of the thinking that goes into it, like, it is, it is something that gets iterated on often, but there are consequences of, uh, you know, when we submit the plugin, the slug, the u r l is going to be locked.
You can. ask them to change it [01:09:00] once within, I don\'t know, the first couple of days or something. But then that\'s it. So, you know, cuz and you\'ll, and you\'ll see that with plugins on the repo that the U R L is W P S E O, but the product is Yost, you know? Right. Or things like that. Um, things that they\'ve had to change over time, but you can\'t change the slug.
Cory Miller: I know that firsthand too . Right. I sure think security was better WP security and, and it still is. I think. I don\'t think we That\'s right. Get there\'s, yeah. So that\'s right. Yeah. There are some foundational things that can\'t change over time, which is tough when you\'re doing new products as you don\'t.
Always know where it\'s gonna go or what the right, you know, do we need to say image cropping, you know, kind of thing. Whatever the, the kind of keywords are.
Corey Maass: Yep. So, yep. So, but I, I definitely think that\'s, that\'ll be a great experience for you to talk about and, and also a lot of the, the thinking that, that it makes you do will subsequently guide at least some of our early work [01:10:00] when we do put up a marketing site.
Cory Miller: Absolutely. Well, okay, so last question. We\'ll wrap this up since we, since we got over time. Um, but it\'s hard not to stop talking with you. I enjoyed this. Um, so by next Wednesday, um, what do you think is realistic for us to make progress on and we can start talking about that next. Because we\'re gonna be doing this, by the way, for the next five, six weeks, I think.
Um, there\'s a webinar, um, that was in the newsletter, the link to that. And then of course, if you\'re watching on YouTube, you can just come back to Post Status on YouTube. But Corey, what do you think, um, our next steps are, the progress we wanna make in this week interval?
Corey Maass: Yeah. I think the goal should be either we get this across the first finish line or past the first milestone or whatever of it.
Either we submit it to the [01:11:00] plug-in repo or it\'s, or it\'s ready to go and we can talk about that. But, you know, feature, feature complete as far as version one is concerned, um, and, and that, that read me, basically it\'s the whole zip file ready to go and be submitted and then we can either, Maybe we even, we could even submit it while we\'re on the, uh, you know, on the call and kind of talk about like that.
And then I think we\'ll end up talking about like, you know, whenever I\'ve submitted plugins, um, I\'ve, I\'ve never just had one like stamp done. Like there were questions asked or there were, um, code revisions that I needed to make based on, I know that they use a programmatic, um, I can\'t think of what it\'s called, but basically code sniffer, um, to, you [01:12:00] know, it basically some little AI that, that will flag variables that aren\'t escaped or things like that.
And, um, and then I\'ve also usually wound up having a conversation with a human being who\'s like, you know, what are your intents? What, what\'s your intention of this? Or, you know, why do you think we need this? Or whatever. And so if, you know, I think that\'ll be worth talking about too.
Cory Miller: Because the submission to the repo takes some time because it\'s gotta go in the review and all that stuff too.
So, um, I think about timing wise as well as like, once it\'s there, it\'s, we\'re gonna have just by nature of the review process, which is good. I, I, I get it. Um, it\'s gonna push us out some to actual, to actual launch. That\'s something to consider too.
Corey Maass: So, you know, so we can, I think let\'s, you know, let\'s regroup, um, today\'s Wednesday, you know, end of the week, beginning of the week kind of thing.
Um, and we can. basically just hit submit. Um, and [01:13:00] I th the last I heard the review process takes a couple of days and I, that, that fits with my experience. Mm-hmm. , um, you know, so maybe we\'ve heard if we submit Friday or Monday, we might have heard by Wednesday. Um, and then we\'ll have that to talk about, you know, or we can just submit on Wednesday and then the following week we definitely should have something to talk about.
We might not be live in the repo, but um, you know, we should have heard back. I know we\'ll hear back within a week. Yeah.
Cory Miller: Okay. Well, my intention is to carve out some time today. I think I\'ve got some buckets of time to finish, to read me at least get a draft that you can review and we can go back and forth, um, to have that, at least you not be waiting on that or me, so that sounds great.
Corey Maass: Yeah, I\'m.
Cory Miller: All right, Corey. Thanks, man. It\'s always fun talking through this stuff. Yeah, having a partner and a collaborator. And, uh, thanks everybody else for, uh, joining in as you can. Um, we\'re gonna be here Wednesdays 11:00 AM Central Standard time, um, [01:14:00] for the next five, six weeks throughout January and February.
As we talk, just share the progress we\'re making for this WordPress product called Crop Express. Thanks everybody. Thanks Corey. See ya. See ya.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 20 Jan 2023 20:00:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:6:\"Emilee\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:8;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"Do The Woo Community: AI Text, Art, and Code\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74344\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:41:\"https://dothewoo.io/ai-text-art-and-code/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:397:\"

I chat with Mark Westguard from WS Form about how we have both used AI with content, art and even WordPress. With some added thoughts of AI and WooCommerce.

\n

>> The post AI Text, Art, and Code appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 20 Jan 2023 12:12:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:9;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"WPTavern: WooCommerce Seeks to Improve Cart and Checkout Blocks Performance\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141242\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"https://wptavern.com/woocommerce-seeks-to-improve-cart-and-checkout-blocks-performance\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3517:\"

WooCommerce Blocks maintainers are asking the developer community to share feedback on any performance issues they are experiencing with the Cart and Checkout blocks.

\n\n\n\n

“We’re aware there is work to be done in this area and we want to improve,” WooCommerce developer Alex Florisca said.

\n\n\n\n

“We’re specifically interested in any performance related issues that may be stopping merchants or developers from adopting the Cart and Checkout blocks over the shortcode version.”

\n\n\n\n

The plugin’s repository has nine open issues categorized as related to performance. Most of them are not straight forward and require more research and testing. For example, an issue with running multiple blocks of product grids was reported as having increased response times of 4+ seconds. Contributors have proposed a few different ideas to address performance issues, such as experimenting with useSuspenseSelect to improve the perceived loading experience for various blocks and finding a way to track the performance of the Cart and Checkout blocks. Neither of these tickets have seen much movement yet.

\n\n\n\n

Store owners will not be eager to switch over to a checkout experience that is slower, so the WooCommerce team is seeking feedback that will help them make the cart and checkout blocks faster. So far, one user reported that due to a bug in a third-party plugin, he got a glimpse of what the block-based checkout adds to the JS asset payload.

\n\n\n\n

“I think this adds at least ~300 kB (compressed) JS payload (initial numbers, my measurement process is still ongoing),” Leho Kraav said.

\n\n\n\n

“We don’t plan to convert our classic theme to a block theme any time soon, but still, I feel uneasy about this direction.”

\n\n\n\n

Florisca followed up on this feedback with a few cursory benchmarks comparing the legacy shortcode checkout with blocks checkout and Shopify:

\n\n\n\n
Blocks CheckoutShortcode CheckoutShopify
Total Payload2.9MB935kb6.1MB
Total Transferred2.1MB1.3MB*3MB
Number of requests14477146
\n\n\n\n

“The number of requests has almost doubled for Blocks, which isn’t great so this is something that we can look into,” Florisca said. “I suspect the reason is because we rely on a few layers of abstraction on top – WooCommerce and WordPress, each with their packages and set ways of doing certain things. We can investigate if we can simply this.”

\n\n\n\n

The discussion on how to improve cart and checkout block performance is still open for more developers to give feedback, and investigations are ongoing. The good news is that WooCommerce maintainers are aware of how much weight the block-based checkout adds and are actively looking for ways to improve it for users.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 20 Jan 2023 03:53:58 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:10;a:6:{s:4:\"data\";s:13:\"\n\n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"WPTavern: WordCamp Europe 2023 Tickets Now on Sale\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141212\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:61:\"https://wptavern.com/wordcamp-europe-2023-tickets-now-on-sale\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2405:\"

WordCamp Europe announced the first batch of tickets on sale for the 2023 event that will be hosted in Athens, Greece, June 8-10. General tickets are € 50.00, a fraction of their true cost, which is heavily subsidized by sponsors. It includes admission to the two-day event, lunches, coffee, snacks, Contributor Day, a commemorative t-shirt, and an invitation to the After Party.

\n\n\n\n

WCEU is also offering micro-sponsorship tickets at € 150.00, which organizers say is closer to the real cost of attendance.

\n\n\n\n

Speaker applications are still open but will close soon in the first week of February. Applicants will be notified by the second week of March and organizers will announce the lineup in mid-April.

\n\n\n\n

WCEU is also seeking a host city for 2024. The minimum requirements are considerably less stringent than in previous years. Hosting the event is open to any team that has organized at least one successful in-person WordCamp in a European city in the last four years with a community that has been active during 2022. Organizers have also published an update to the selection process:

\n\n\n\n
\n

For this year, we have tweaked the selection process to concentrate more on the local community and the city instead of deep knowledge about how to organise a successful WordCamp Europe.

\n\n\n\n

The selection of the WordCamp Europe 2024 host city will be based on the overall evaluation of the application, instead of ranking different parts of it. We don’t ask your team to prepare a budget for the whole event, but estimated costs for the proposed venue(s) should be available.

\n
\n\n\n\n

Contributor Day registration for this year’s event is not yet open but will be free with the purchase of a conference ticket.

\n\n\n\n

At the time of publishing, only 257 tickets remain in this first round, but more batches will be released in the future. Register now to lock in your spot or sign up for email updates on the registration page to be notified of future ticket releases.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 19:37:51 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:11;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"Post Status: Interview With Product Lead Tiffany Bridge Of Nexcess — Post Status Draft 137\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146391\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"https://poststatus.com/interview-with-product-lead-tiffany-bridge-of-nexcess-post-status-draft-137/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:57251:\"

In this episode, Tiffany Bridge joins Cory Miller to talk about the latest innovations she and her team at Nexcess have created for beginner online store owners, simplifying WordPress for users, and the ongoing battles between centralization and decentralization.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

Tiffany Bridge has been working in WordPress almost since the beginning of WordPress. She is the Product Manager for WordPress eCommerce at Nexcess and talks with Cory Miller about their hosting services and products, specifically highlighting the benefits and capabilities of Store Builder. They dive into optimizing UX in WordPress, the benefits of open source, and more.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • WooCommerce Simplified with Store Builder. As you know, WordPress and WooCommerce love to hide settings in layers of menus. Nexcess saw the struggles people had in trying to set up eCommerce sites and created StoreBuilder as an easy tool to go from zero to having an online store. This removes the initial learning curve required to get started in Woo and sets up a DIY tool for merchants.
  • \n\n\n\n
  • A Platform to Grow with You: One of the great things about setting people up on WordPress and Woo as they start businesses is the flexibility available for future growth. If their model totally shifts, they can just uninstall a plugin and add another to obtain the functionality they need to sustain their business growth without the hassle of migration or the increased fees of a platform.
  • \n\n\n\n
  • Solving for What Users Shouldn’t Have to Know. Kadence and so many WordPress and WooCommerce plugins are designed for WordPress professionals. We are working to leverage the power of Kadence by creating a top-notch user experience for people who don’t know what things like a border radius or gutter are. These tools enhance and expand the power of WordPress, so creating solutions that lower the knowledge barrier to entry is the kind of work that moves WordPress forward.
  • \n\n\n\n
  • You Can Own Your Own Platform. Often people aren’t aware that this is an option. From Etsy to Twitter, controversies tend to increase demand for alternatives. Bringing more awareness to individual ownership on the web-for blogs, stores, or anything else-empowers people to show up online and conduct business on their terms.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n

\"🙏\" Sponsor: GoDaddy Pro

\n\n\n\n

Manage your clients, websites, and tasks from a single dashboard with GoDaddy Pro. Perform security scans, backups, and remote updates to many sites on any host. Check up on site performance, monitor uptime and analytics, and then send reports to your clients. GoDaddy Pro is free — and designed to make your life better.

\n
\n\n\n\n
\n\"GoDaddy\n
\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

Cory Miller: [00:00:00] Hey everybody. Welcome to back to Post Status Draft. This is an interview in the series of product people that we\'re doing with some of the great product companies in WordPress. And today I have my new friend Tiffany. Um, we get to talk a couple weeks back and I love her energy, her experience, her approach to WordPress overall. She\'s very distinguished, uh, experienced person in WordPress having done some cool stuff that I\'m gonna let her talk about. But we\'re gonna be talking about Nexus and Store builder today I think So, um, Tiffany, welcome to Draft podcast. Thanks Corey. You tell us what you do, what, what you do in WordPress now, and where, where you got to this.

\n\n\n\n

Tiffany Bridge: Okay. Well, so right now I am the product manager for WordPress e-commerce at Nexus, which is, uh, basically I kind of, uh, I have my hands in the entire experience [00:01:00] of using WordPress on our platform as a, as an e-commerce focused host. Um, that\'s a pretty wide swim lane, so I do a lot, a lot of different things.

\n\n\n\n

Um, the thing that I\'ve been focusing on is our store builder. Um, before Nexus I was, uh, I was at Automatic for a while doing, uh, I was on their special projects team, um, which works with, um, you know, interesting people and organizations to try and make sure they have a great experience on WordPress. So I did a lot of, sort of very bespoke projects there.

\n\n\n\n

Um, before that I freelanced. You know, was kind of doing what a lot of, uh, my colleagues are doing is just trying to, you know, help my clients have, um, you know, with by setting up like WordPress sites for them and things like that. And before that I was doing a lot of WordPress just kind of in personal projects.

\n\n\n\n

I started teaching myself WordPress in 2004. So, um, I\'ve been with WordPress almost as long as WordPress has been WordPress, which is, um, which is fun, like to see how far we\'ve. As a, as a community and as a, and as a piece of software. Right?

\n\n\n\n

Cory Miller: We\'re gonna have to [00:02:00] talk about that later. I\'m gonna come back to that cuz you, you predate me. I was just a blogger in 2006 on, on this cool thing called WordPress . Um, but you said this, uh, as part of you, I know you\'re so, you\'re so humble, but I want to act accentuate a part of this, like that special projects team you did at Automatic is known for doing. Big, glamorous, cool sites with potential big problems attached to them.

\n\n\n\n

And I can\'t remember what the code name for the team has called, but I knew about it for years. And then when we met a couple weeks ago, months ago, um, and you told me your background, I was like, you were on that team. Cuz it\'s very, um, I, I would say like, You know, a celebrity status in my sense, because I know I\'d go, I\'d go to this blog site of this cool site and realize it was on WordPress, or somebody would say, now this is on WordPress, and you kind of dig into the details and you go, it\'s that team at Automatic that was doing it, that you were a part of for such a long time.

\n\n\n\n

Tiffany Bridge: Yeah, I was there for, uh, well, it was just like, [00:03:00] it was a couple of years and, um, yeah, I mean I worked on some very, very cool projects and it\'s kind of like WordPress bootcamp, right? Like if you don\'t, whatever you think you know about WordPress, you will know more after, after like six months on that team.

\n\n\n\n

Um, because we solved like, Like every WordPress problem there is, right? Like you\'re, sometimes you\'re rescuing a site from a developer that maybe didn\'t do a great job. Sometimes you\'re converting a site that isn\'t on WordPress to WordPress. Um, like a, a project that I worked on that is very close to my heart that I can talk about is, um, I worked on the conversion of a list part from Expression Engine to WordPress, which was just an incredible experience.

\n\n\n\n

Um, I learned so much, and the a list part team was super great. So, um, yeah, like that was a, that was an intense couple of years. Like there\'s a lot, there\'s a lot that goes into those projects and our job was to kind of make it, it was like, you know, like the metaphor of the duck, right? Like you\'re, you\'re swimming seren except underneath, you\'re like furiously paddling

\n\n\n\n

And like that\'s, uh, [00:04:00] that\'s the special projects team.

\n\n\n\n

Cory Miller: Can you say this special code name for it? I wanna say stiff.

\n\n\n\n

Tiffany Bridge: Um, the, so, I mean, every team at Automatic has like an internal nickname, right? Like the, the, the name. Because the names of teams at Automatic have historically not been, um, they have, there, there isn\'t just like, oh, that\'s accounts payable.

\n\n\n\n

Like there\'s, that\'s not what any of the teams are called, right? They all have like clever names, , um, special projects team is, uh, the overarching team is called Team 51. There are a lot of, there are a lot of rumors about why that was chosen. Um, none of them are, all of them are more glamorous and interesting than the real reason it was chosen

\n\n\n\n

Um, but now team 51 is actually, like, when I was there it was like 13 people, but it\'s now like 40 some people and so there\'s lots of subteams and those subteams all have names and things like that as well. So, but the overarching team internally is called Team 51.

\n\n\n\n

Cory Miller: This is why I wanted to do these set of interviews cuz there\'s people behind, oftentimes behind the scenes with these vast experie.[00:05:00]

\n\n\n\n

Building the cool products that so many people use and why? I wanted to highlight your background. When we got to talk, I was like, oh, I\'ve gotta share this, because I think it\'s so compelling to see one, you\'ve been doing WordPress for a very long time. Two, you did it for with this like, very, uh, interesting team doing some cool projects that really put a great face on WordPress.

\n\n\n\n

Um, like a list apart. You know, so many people in our community know that like the back of their hands. Um, I wanna share that. Cause I think that that all formulates these compelling stories into today in your role at Nexus and what you\'re doing and formulates all this background. Like I remember at I themes, there\'s so many times we\'re building cool stuff, but people don\'t see inside the workshop, they don\'t see all this stuff.

\n\n\n\n

They don\'t know all the history and background, the care and passion that goes into this. And so that\'s one of the reasons I was doing this and why I wanted to like point it out, you know, , um, So, um, okay, so that brings us to [00:06:00] today, and now you\'re at Nexus doing store builder of many things. But I really wanna talk about store builder because I think it\'s really interesting.

\n\n\n\n

I know you\'ve been focusing on it, um, at Nexus and it, there\'s a big problem that I think it solves for my own work. , I shouldn\'t even say work, trying to use w this thing called WooCommerce, which is incredible. one I, I think I, I\'ve said at least, and you correct me, kept, but I\'m like WooCommerce is the default e-commerce software on the planet because it\'s used so broadly.

\n\n\n\n

I think it\'s growing faster still than WordPress and for good reason, but you can do anything and everything with it. And that presents a lot of complexity. Absolutely. Absolutely. What is the problem you\'re trying to solve with store builder?

\n\n\n\n

Tiffany Bridge: Sure. Oh, well. So as you say, like the more flexible and powerful something is, the more complicated it is.

\n\n\n\n

And you know, something that I learned, and this I think, especially I learned at, um, on special projects is that, [00:07:00] you know, setting up WordPress and WooCommerce, that\'s a different set of skills than just using them day-to-day. And the problem is that people who, like once you, once the, the site is set up right, people can learn to use it.

\n\n\n\n

It\'s not, it\'s not that hard to use, but getting to that point where you can just use it and run your business on it requires a ton of knowledge. And you know how WordPress. Is like, it likes to hide all of the settings, like in all of these different menus. And you have to, you have to kind of know what you\'re looking for in order to find it.

\n\n\n\n

Um, and that\'s a real, that\'s a real challenge for people. So the problem that we\'re trying to solve with store builder is this idea of like, okay, there\'s like five or six things you have to do in order to go from zero to a store. And we wanna like gather those all up in one place and just walk you through them in a very logical way.

\n\n\n\n

So, okay, first we\'re doing like what we call first time. Consider. You\'re setting like the name and address of the store and the name of the site. And then we wanna do look and feel. Um, so let\'s just get some pages into your site. Let\'s get some content into your site that you can edit and make your own.[00:08:00]

\n\n\n\n

Then we wanna, like, let\'s add a domain. We\'ve got this very cool, like we call it the Go Live wizard, where you just, um, where it like walks you through the process of, of connecting a domain right there from inside WP admin. And then we\'ve got, okay, great. Now it\'s time to add your products. Products we don\'t have a wizard for.

\n\n\n\n

We\'re just sort of surfacing a lot of help content to just help people make good choices as they\'re configuring their product, their products. And then it\'s like, great. Now let\'s connect your payment. Now let\'s set up your shipping. Hey, congratulations, you have a store. Is there more work to do on the site?

\n\n\n\n

Of course there is. There\'s always more work to do. But now we have gotten to a point where you have products and you can take payment and you can ship them, and your site has a domain name and therefore an SSL certificate. So here you are, now you\'re in business on the. And that\'s the problem that we\'re really trying to solve is just like, let\'s just get p get all of these, like things that you have to configure in front of people so they don\'t have to go hunting for.

\n\n\n\n

Cory Miller: And that\'s a huge problem I see that firsthand, um, is, you know, WordPress enabled me [00:09:00] to start a business, start a blog first, and then it evolved into a business. And that\'s the beauty of it. And I see that with, with commerce. Nearly any, uh, nuance thing you want to do, you can probably do it with WooCommerce.

\n\n\n\n

There\'s so many extensions, plug ons and addons and stuff. It from my experience, it seems like, you know, you get in and, and e-commerce just set aside from e-commerce is just complex because, okay, well you\'re selling in Europe and you need that and you need invoices or something like that. You\'re selling, you know, a digital good with a physical product and you want a free trial.

\n\n\n\n

I was just talking to somebody about that yesterday. The whole thing on e-commerce. And then you get to WooCommerce, great tool, awesome ecosystem and stuff. And I see this problem that you\'re trying to tackle over and over, uh, and I think it provides a huge need for those trying to build stores on the web.

\n\n\n\n

Um, tell me about who the product is really for. [00:10:00]

\n\n\n\n

Tiffany Bridge: So you know, this product is really for that sort of like merchant who is either setting up the site themselves or maybe they\'re working with somebody to set up, but they\'re not like hiring an agency to build them a site, right? Like they might have, they might have a buddy who\'s good with computers, or they might even have paid a freelancer, but it\'s really meant to be kind of, Right at that like level of the person who is actually gonna be running the business should be able to set up the store.

\n\n\n\n

That\'s always the goal that we\'re after, right? Is if you decide, if you\'re like knitting hats and selling them on Etsy and you decide you wanna get off of Etsy, like you should be able to do this. So it\'s, it\'s meant for people whose skill is whatever it is that their business is. Not building websites, and that\'s who we\'re really targeting with this.

\n\n\n\n

Now, that is a very complicated problem and there\'s a lot of layers to it. And so we are always in the process of trying to solve for that use case. I think, um, I don\'t know if you can ever be, you can never say. We\'ve solved it, right? Like there\'s always gonna be more to do. [00:11:00] Um, and that\'s what we\'re doing with Store Builder right now, but that\'s who, that\'s for.

\n\n\n\n

Like a lot of our other products, like we host, we have Manageable commerce hosting, manage WordPress hosting. What we like to say about those products is that we\'re the hosts that you graduate to, right? If you\'re coming to us, you\'ve probably already been somewhere else. Um, but with Store Builder, we\'re really focusing on people who probably don\'t already have a website, and that\'s, uh, that\'s who the product\'s for.

\n\n\n\n

Cory Miller: That\'s unique with Nexus, but I know Nexus is a brand company, has extensive experience with e-commerce too. And this offering is really interesting because one, you\'re tackling a big problem. Um, but two, you\'ve got a lot of experience on your team and the company that has really dealt with this, um, the e-commerce question for a long time.

\n\n\n\n

So.

\n\n\n\n

Tiffany Bridge: Yeah well, and it\'s such a privilege to be able to work with people who like really think about e-commerce, right? Like Nexus got its start doing Magento. And so like we have a lot of like all of our, you know, engineering and our operations, like, they understand like what an e-commerce site [00:12:00] needs. And so it\'s, it\'s been great to watch them kind of apply that knowledge to WordPress and w as well.

\n\n\n\n

Cory Miller: Excuse me. And I think this is. It\'s one thing to have a blog, you don\'t wanna have blog. Mm-hmm. , I didn\'t worry too much about downtime. Sure. When you have downtime or something happens and you can\'t get things done with your story, you\'re probably likely losing money. So Absolutely. I think that experience is, is key to highlight Mato Gun back to the, the days, you know, this big, big behemoth of an e-commerce platform that switched hands and

\n\n\n\n

.

\n\n\n\n

hear that background. Next is, So you, you said this, uh, just a second ago, but you talked about some of the things, like what you\'re trying to do, and you mentioned some, some key things in the last year or so, as you\'ve b led this project. Um, what are some of the things that, that stand out that you\'re, um, excited about, proud about that uh, you can share.

\n\n\n\n

Tiffany Bridge: You know, I think in terms of like actual product features, you know, I\'m so proud of that Go Live Wizard. Um, because like, [00:13:00] you know, what\'s this saying? Like it\'s always d n s, right? D n s is hard and that\'s. and that\'s such, and there\'s no way to talk about it in a way that isn\'t like technical, right? How do you connect a, a domain name to your site?

\n\n\n\n

Well, you\'ve gotta go change your name servers. Well, what\'s a name server? What\'s a cname? What\'s an a record? Um, people shouldn\'t have to know that, right? Like people shouldn\'t have to know that in order to get online, I think. Um, so it\'s been really fun to kind of build this cool tool that just walks people kind of through a decision tree.

\n\n\n\n

The first thing it asks you is, , do you have a domain name or do you need one? If you need one, it\'ll send you out to the Nexus checkout, or we\'re working on this feature where it\'ll send you out to the, the Nexus checkout. We\'re working on the feature where it brings you back, back into your store. Like right now, we can, we can send you out to our domain registration, but we, we have to rely on you to come back.

\n\n\n\n

We\'re working on a feature where we can move you out and then just bring you right back to where you left off. But you know, so that\'s the first question. And then like once you have it, it like it will actually validate whether your domain is ready to connect, right? It\'ll do all the queries to see like, [00:14:00] are your name servers set or do you have the C name set up?

\n\n\n\n

And it\'ll tell you. If not, it\'ll tell you what it is that you need to do. Um, And then, you know, you, as you proceed with it, it\'ll like set up the DNS zone in your portal and it will like do the, um, the find and replace on your database to make sure that like WordPress knows what domain it\'s supposed to be using and that all of your internal links are now referring to the correct domain.

\n\n\n\n

So like it does all of those like little things that, like on special projects, we have a whole checklist for, to make sure that a human does them well. Now we\'ve got like a. Um, so that, that does that, and that\'s, I actually tease my former coworkers sometimes and I\'m like, Hey, I\'m over here trying to replace special projects with a series of onboarding wizards.

\n\n\n\n

And they\'re like, yeah, good luck with that . I\'m like, Hey, look, I never said I like small problems. Right? . So, um, but so that, like, that feature is something that I\'m really, really proud of and, um, and excited about. And I\'m always telling people it\'s like the best single piece of store builder

\n\n\n\n

Cory Miller: is, is this different [00:15:00] from the wizard?

\n\n\n\n

You mentioned a bit ago.

\n\n\n\n

Tiffany Bridge: It\'s the same one. Okay. I mean, it\'s like the, like that\'s the, that\'s the one that I\'m most excited about. And, and I think it\'s the reason that I may, that we\'re able to do that one so beautifully is because you don\'t have to, like, there isn\'t like a third party that we\'re having to connect with.

\n\n\n\n

Um, you know, when you start getting into like payments and shipping, like suddenly you\'re dealing with other people\'s APIs and so there\'s a limit to what you can do. Um, but like where we\'re able to kind of control the experience, we\'re able to make it like really beautiful and functional.

\n\n\n\n

Cory Miller: I know I\'ve, I\'ve helped people.

\n\n\n\n

You know how it is, I\'m sure you get this too. It\'s like if they know you do WordPress or websites, you know, everybody has some kind of idea. And, um, there\'s platforms out there, but again, the power of WooCommerce and, and WordPress particularly to, to grow your business. But there\'s complexity that happens that, that I know you\'re wiring in as you think about and build, continue to build the.

\n\n\n\n

For that experience. Um, it\'s kind of [00:16:00] going back for a second. I know Nexus does. Okay. You graduate to us. Uh, store builder specifically, I think is for a different kind of, um, problem. And you might have said this, but I want to come back to it cause I, I think I might have missed sharing this part of it. So, store builder, if you, you know, want to start a store and here are, you know, 15 options.

\n\n\n\n

This is the option if you want to, um, start a store and grow it.

\n\n\n\n

Tiffany Bridge: Is that right? Yeah, I think so. I mean, I think there\'s a no better platform than WordPress and Woo for something that\'s gonna grow with your business and be flexible to your business. Like maybe you get farther down the road and you decide, you know what?

\n\n\n\n

I don\'t actually want to sell merchandise anymore. What I would rather do is do courses or events. I mean, all right, well just install another plugin. You can uninstall WooCommerce. , off you go. Um, and so, you know, having that option always available to people as well is really important. Like you can, [00:17:00] because as you know, it\'s so flexible and you can just swap in the pieces you need and take out the pieces you don\'t.

\n\n\n\n

Um, I think it\'s, it\'s really great to just get people, like, just, just get on the platform that\'s going to grow with you at the beginning instead of having. Migrate later, right? Like, nobody likes migrations, nobody likes, you know, having to convert their data and carry their, carry their orders from like their Shopify store and their commerce.

\n\n\n\n

Just start with WooCommerce. It\'s fine.

\n\n\n\n

Cory Miller: I know. Um, so we talked about in that experience, like really making that initial experience where you\'re like, I\'ve got something I want to sell. Um, you mentioned when we were talking before this too, like particularly you\'re on another platform, like an Etsy or some other platform.

\n\n\n\n

This is when, um, you\'re ready to go and there\'s this, there\'s this learning curve with WordPress WooCommerce that you\'re trying to sort out. Um, I think you said it when we were, um, prepping for this like idea to selling [00:18:00] is, is kind of that key, which I think is so awesome because I know from experience.

\n\n\n\n

People, you know, non-word, pressure related. Go, I\'m ready to do this. Lindsay and I, my wife have a, a partner, great founder who does physical products. And, and that was the question I was like, okay, well you have a couple of options. , they all have pros and cons, they have some things. Um, but having an experience like this, I think is so key because of that initial learning curve going live online.

\n\n\n\n

But there, I know there\'s other things too. Nexus happens to be in the family of LiquidWeb, which is Own, has a number of WordPress specific company outside of the Nexus brand of families that you all, um, leverage within the platform too.

\n\n\n\n

Tiffany Bridge: Yes, absolutely. Um, the biggest, uh, so you know, the liquid web family of brands is large and growing, right?

\n\n\n\n

And, and, and as our post status friends know, there are quite a lot of like WordPress plug-in businesses that are now part of the family of brands. And the one that we are leveraging most right now in store builder is [00:19:00] cadence. And cadence. For those who don\'t know, is this really great? I don\'t wanna call.

\n\n\n\n

I mean, it\'s a theme, but it\'s like so much more than a theme, right? Um, it, it is a theme. It is blocks, it is starter templates. It\'s this whole package and it\'s really geared around people who are like web designers, but just need a great, um, like way to build and customize a site that doesn\'t necessarily rely on like a third party page builder.

\n\n\n\n

Right? Something I appreciate about Cadence is the way it sort of embraces. Extends the WordPress Block editor rather than trying to replace it. Um, cadence is there, there\'s so much great stuff, right? Like right now, store Builder really leverages this Cadence starter template. So you pick one of the starter templates around, uh, around e-commerce, and we import a site for you, basically.

\n\n\n\n

Um, and then you just have to edit it and make it your own. Replace the images, replace the text. But, you know, the, the feedback that we\'re getting from our customers is that that\'s still a lot of work and it. Their feedback is that because it is, they are correct. [00:20:00] That is still a lot of work to do. And so something that we\'re kind of, the next problem we\'re trying to tackle in store builder is this idea of editing all the not store parts of your site, making sure that you have a homepage and an about page and you know, all of your policy pages and things like that.

\n\n\n\n

And making it as easy as possible for people. Because you know, cadence was kind of designed around people who are already web designers and that isn\'t who our audience is. So we\'ve been working very closely with the ca cadence team on, you know, what\'s a, how can we leverage cadence and the power and the, the, the experience that they have, but create like a really great experience for, um, people who aren\'t.

\n\n\n\n

Who aren\'t already savvy with web design, right? Who don\'t know, like, what is a gutter, what\'s a border radius like, you know, no one should have to know that. Um, so we\'re, that\'s the next problem that we\'re trying to solve and um, and it\'s been a real privilege to work with my colleagues over on that side of the house on that.

\n\n\n\n

Cory Miller: I, That\'s you just kind of like [00:21:00] highlighted one of, one of the benefits why we, our partner and, and the founder of that physical products company. Like why not just to use, let\'s say a Shopify site or something is like mm-hmm. , the stuff you said that the non-store stuff is so awesome and attractive.

\n\n\n\n

Mm-hmm. and helpful for store owners where you can blog and. NCO and different things like that. And I happen to have some inside knowledge as far as . Um, having been at Lake Web a couple years ago, sold, sold our themes to, uh, lake Web, that there\'s a suite of tools That\'s awesome. And to see, you know, post status by the way, also runs cadence and such a powerful framework, whatever we call it, you know, word critical.

\n\n\n\n

Tiffany Bridge: Yeah. It\'s a, it\'s a sweet a package. I don\'t know, it\'s like, it\'s a theme. It\'s a lot. It\'s a lot of stuff. Um, and it\'s, it\'s just great. And, um, I\'ve been really, it\'s been really nice to be able to, to work with, um, something that both kind of embraces kind of the WordPress way of doing things, but also really [00:22:00] enhances and expands it.

\n\n\n\n

Cory Miller: Okay. So help me complete this sentence. As for product lead for this, this particular. Um, there\'s probably all these things that your, your team knows in sudden and out cuz you built them and you built them based on these customer, this journey of these problems with obstacles people ran into. I wish people knew or did about what?

\n\n\n\n

As part of store builder. Is there things from like, you know, your team just goes, gosh, they\'re not taking advantage of the school teacher. They\'re not doing this one thing that would make their life easier, the business would grow better. What are, what are some of those things, part of the platform that\'s come to mind?

\n\n\n\n

Tiffany Bridge: Oh, that\'s a hard one. I mean, I think the thing that I find is that the thing that I always want customers to know is usually it\'s bec, usually they don\'t know it cuz I haven\'t adequately conveyed it to them. So it seems a little bit almost self-serving. Right. To be like, oh, I wish [00:23:00] they knew. Like, one thing that I always find myself wishing that people knew is that e-commerce is really complicated.

\n\n\n\n

Right. Um, cuz I think sometimes we get people who come to. To store builder and expect us to solve all of the complexity of the e-commerce when what we\'re really able to solve is like the complexity of the website part. Like I read our, um, One of the things I do as a product manager is I read all of our cancellation reasons.

\n\n\n\n

Um, so like anytime somebody has left the product and they wanna tell me why it\'s hard reading, sometimes , it\'s very bad for the ego, but it\'s very good for the product. And somebody once said, well, I, I can\'t believe how many things I have to log into to use this. Like, okay. Well if you\'re talking about like our Nexus portal, like I agree with you.

\n\n\n\n

I would love to reduce the need for people to have to log into a web hosting portal. Right? But if you\'re talking about payments shipping, like was there ever a future where you weren\'t gonna need a Stripe account? I know some people are [00:24:00] tackling that by like building their own payments, but then I feel like that\'s another form of lock-in that I don\'t love.

\n\n\n\n

Right. Um, so, you know, so a thing that I, I want people to know is that, um, the system ha the, this, we\'re trying to, we\'re trying to balance like that like. Opinionated versus like freedom thing, right? Like, can we be very opinionated? Like, look, you\'re just gonna use, this is the payment system you\'re gonna use.

\n\n\n\n

Just, just, you know, while also still giving people that freedom of w of, of WooCommerce, um, I think that\'s always like when I\'m reading stuff, that\'s always what I\'m wishing people knew. And so now it\'s just a question of like, well, how do I then, like how do I teach \'em that it\'s not their fault? They don\'t know that I know that they don\'t know that.

\n\n\n\n

I think about e-commerce all day. You don\'t, you, all you wanna do is just get online and like sell this thing you made,

\n\n\n\n

Cory Miller: sell your stuff. Absolutely. Well, and, and there\'s platforms out there like Shopify for instance, and it, it\'s super fast gets [00:25:00] something going, but the complexity exists of some of these things.

\n\n\n\n

Like, you gotta think through, are you selling to Europe? What do you, you know, that\'s just one that comes to mind for me. Exactly. Um, but I totally get it. Um, the space that you all are in, what the product you\'re trying to provide, um, that, that is kind of like a pro and con of the beauty of the. , you can with store builder, with WordPress, with WooCommerce, get a store up and going mm-hmm.

\n\n\n\n

Um, so you can do it. And that\'s a great freedom that we have and enjoy for sure. But that, uh, I know from having done had, obviously businesses that run e-commerce rely on e-commerce or website was our front door to our store, but it was down. We didn\'t make money. Um, and then trying to help navigate some of those complexities is, is a pretty tough job.

\n\n\n\n

Anything else that kind of comes out to. About what I wish people knew. Yeah.

\n\n\n\n

Tiffany Bridge: Oh gosh. So many things. All the [00:26:00] things. Um, , they need anything, I guess they wouldn\'t need store builder

\n\n\n\n

Cory Miller: anything about the product that we haven\'t. Mentioned that, that you want to share too? I

\n\n\n\n

Tiffany Bridge: mean, I think, like, I think we\'ve covered all the things that I\'m like most passionate about.

\n\n\n\n

Like I just, yeah. You know, well, we were, you remember that controversy several months ago about Etsy and like Etsy\'s increase in fees and people were sh closing down their Etsy stores. And, um, like I just, like, I want people to know that it doesn\'t have to be that. . Like, it doesn\'t have to be that way.

\n\n\n\n

Like you can own the plat, like you can own your platform. We\'re seeing this now with Twitter, right? The implosion of Twitter. People are like, what are we gonna do? Where are we gonna go? And I\'m like, you should have a blog is what you should do. Um, you know, I think I, it just, I want people to know that it doesn\'t have to be that way.

\n\n\n\n

We don\'t have. Like our presences on the web, which is an increasingly important way of way, way that we conduct business, the way we conduct our relationships, the way we meet new people. Like we don\'t have to, it doesn\'t have to be that way, right? You [00:27:00] can own your home on the web, whether that home is a store or just a blog.

\n\n\n\n

Or just a blog or, um, or anything else. Like it. Just like, it doesn\'t have to be this way. It can be. There are many of us who would love to help you with it. And like, I\'m not saying that just as a person who wants to sell store builders, I wanna sell store builders, but I want to sell, like the reason that I care about store builder is because what it allows people to do.

\n\n\n\n

Cory Miller: Mm-hmm. Absolutely. You backed into my question I was gonna ask you next was to, you\'ve been a workforce a long time and you know when we prop. Uh, examples, like, I don\'t want to just poo poo Shopify, but use Shopify software is a service. There\'s benefits to having a SaaS Absolutely. Solution for what you\'re doing, but there\'s also,

\n\n\n\n

Tiffany Bridge: there\'s a reason they\'re successful.

\n\n\n\n

Cory Miller: Absolutely. There\'s also downside, and you mentioned earlier it\'s like WooCommerce, WordPress, and even store builder and Nexus grows with you. Um, but I want you to share a little bit more about that. You know, Shopify, what I was telling our partner, I said, you know, [00:28:00] Shopify\'s the glamorous thing people look at.

\n\n\n\n

And I see, I see why. But I said, you\'re gonna trade some problems for a new set of problems. And one of those you\'ve mentioned a couple times is lock in. And the beauty of, I want you to share a little bit about the, what your thoughts are around WordPress, WooCommerce, and open.

\n\n\n\n

Tiffany Bridge: Yeah, I mean, I think, I mean, the number one, biggest one is that you can own it and you can go, you know, wherever you want, and you can decide the experience that you wanna have.

\n\n\n\n

Um, I think that\'s something that a lot of us are spending a lot of time thinking about right now as like various social media platforms or like the, the downsides of like, for example, kind of lock in, uh, in social media pro. Platforms is becoming apparent, right? So that\'s like one thing that I think is really important.

\n\n\n\n

Um, another thing that\'s important is that, you know, the thing about, like, there are lots of companies in WordPress and Yes, here we all are trying to sell you our solution, right? We\'re all trying to make money. We\'re all trying to, you know, everybody, we, we live in capitalism. We\'re all trying to make money here.

\n\n\n\n

[00:29:00] But at the same time, like there is no reason. That you have to have any of that, right? Like the only thing that, that you have to pay for to use WordPress is someplace to. Right. You can download it, you can use it, it\'s all free, and that you can decide what you need and then you know what\'s worth paying for versus what\'s worth not paying.

\n\n\n\n

Like you can, it\'s such a like a choose your own adventure kind of platform. And I feel like, you know, we\'ve had so much centralization and so much, um, You know, like it\'s just so much centralization, so, so much like merging and like this company buys this company that we kind of forget that like we don\'t have to be that way.

\n\n\n\n

And I think it\'s, it\'s really important. Uh, I think open source is really important to like individual autonomy in that way. Like we\'re starting to get a little of like philosophical here, but I think, you know, just knowing that. If nothing else, you can just go download WordPress and learn to use it. Like I started downloading WordPress and learning to use it because, um, [00:30:00] movable type was going to a pay a for pay model and it was more money than I could pay at that time to indulge my like personal blog habit.

\n\n\n\n

And everybody was talking about this new system, WordPress that was open source and free. And I was like, free is good cuz I am broke. And I downloaded it and I started teaching myself to use it and it completely changed my. And I know I\'m not the only one. Right. I have talked to other people who are like, great.

\n\n\n\n

WordPress was free for me to learn to use, so I learned to use it. Word camp was $20 for me to go, so I slept on somebody\'s couch and went to a Word camp. Something that I think is, is so important is, is that kind of low financial barrier to entry. I would love to see us have a lower like knowledge barrier to.

\n\n\n\n

and I think we\'re all working on that every day. Um, but um, that, that\'s just like, that barrier to entry I think is always really close to my heart because I really believe that, you know, these are things that can change people\'s lives if they just have what they need in order to take advantage of them.

\n\n\n\n

Um, and I think that the community really [00:31:00] does care about that. And that\'s something that\'s like, makes me very proud to be involved in WordPress.

\n\n\n\n

Cory Miller: Well, you, you just, there\'s a practical side to this too, and I love the philosophical because it has practical implications as well. It\'s like Absolutely. You get locked into a platform, like you\'re talking about, whether it\'s an Etsy or a Twitter or a Shopify.

\n\n\n\n

Mm-hmm. , you\'re at kind of the whims of. What they\'re doing. That\'s a little bit different in word control,

\n\n\n\n

Tiffany Bridge: like company gets bought by somebody who then does all kinds of questionable things with it, and then here you are, like, I\'ve been on Twitter for 15 years, right? Like I\'ve been on Twitter since, yeah, 2007.

\n\n\n\n

So I\'ve been on Twitter like 15 years and here I am. Like with my like 15 year old, like at Tiffany Twitter handle, because that\'s how long I\'ve been on it. I got my first name and now somebody\'s over here like running it into the ground, making all kinds of questionable decisions, messing up the experience I have.

\n\n\n\n

And then I\'m like, well, now what? Like half the people I know I met here, like now what do I do? And like here I am like. I got locked in. I said I wasn\'t gonna get [00:32:00] locked in, but here I am, locked in. Um, so yeah, I mean that has like very practical considerations. There\'s people that I\'m struggling to stay in touch with because I only knew them on Twitter and like, how do I find them now?

\n\n\n\n

Cory Miller: Well, and you know, just a real direct one-to-one is, um, Shopify and Etsy platform versus this. And you, you look at a lot of entrepreneurs, e-commerce merchants start something, it blows. It. It starts to really grow and that lock in down the stream really comes into play For sure. Like you start getting taxed on your success in a sense where you, like you said, to that own and locked in feature where you go now.

\n\n\n\n

Exactly. With WordPress, we built a tool to, I themes that stellar brand that you can move websites very easily with. Exactly. Including at Nexus Brands.

\n\n\n\n

Tiffany Bridge: Exactly. And you know, like you, you build something, you go viral, you\'re like, suddenly your Etsy store\'s [00:33:00] going crazy. Now you have like, you know, transaction fees at Etsy.

\n\n\n\n

So the bigger you are, like the more your fees grow at ets, you know, at Etsy. And um, so you have that problem, but also like maybe you never bought a domain name. So now everybody only knows where to find you on Etsy instead of getting a domain name. So now you\'ve gotta like figure out how to teach people to go somewhere else.

\n\n\n\n

Like if you wanna move, like it\'s, yeah, it\'s a real. . I see this a lot of times too with like content creators and like Instagram. They\'re like, oh my gosh. If, I mean, Instagram\'s how I reach my audience, how are people gonna find me? If inst, if Instagram goes down, y\'all, that is a problem. Like you need a website and, and it just like, it makes me nuts, like a thing that is, it just makes me like pound the table cuz I get so annoyed about it.

\n\n\n\n

Is so you don\'t have like, People, you can only have like one link on Instagram, right? It\'s in your bio link in bio. And so people will like pay money for a link in bio service and then like link to their website and a link in bio. And I\'m like, what if I told you that you could just put a page on your website with the list of all your [00:34:00] links and then put that link in your bio.

\n\n\n\n

Um, and then you wouldn\'t be locked into yet another service, right? You don\'t have to get locked into the, like, there\'s the lock into Instagram and then there\'s the lock into the, the thing that you did to like work around the limitations of Instagram. Just have websites. Y\'all just have websites.

\n\n\n\n

Cory Miller: It\'s well in this, this partner of our same thing, built a great, huge audience on Instagram.

\n\n\n\n

Mm-hmm. that you gotta have an gotta have a website, gotta have an email list that you\'re trying, you know, things have, things have evolved. There\'s other marketing opportunities. But I go for me, website, email list that you can contact them that you quote own. So if something shifts, but you know, Tiffany, I\'m interested too.

\n\n\n\n

You see all this, you know, looking, looking around Instagram for instance. Some of the people that have got huge audiences, and I click those links and I think, okay, well maybe they\'re what, you know, at some point, how do they monetize that? And I go and I wanna get your thoughts on this and this whole creator [00:35:00] economy and what, I think probably 10 years ago we thought it\'s like bloggers and , you know, we have a new name for it now, but the creator economy, where they used the platform to get some initial buzz, but then, Okay.

\n\n\n\n

What\'s the path to Monet monetization. I mean, we\'re all passionate about what we do, but at some point you also need to, you know, keep the lights on and pay the pay the bills kind of thing. Absolutely. But I\'m curious too, like seeing that you\'ve been at WordPress a long time, seen in the web, a long time, been a technologist, but like, you know, what\'s your thoughts on that creator economy?

\n\n\n\n

Just like you said, okay, hey, here\'s a good point. Build your audience here. Hey, maybe not just a link tree or whatever it\'s called, but like, here\'s your website and all that. But what kind of trends and, and themes are you seeing in, in the foreseeable future, uh, that you know, you have thoughts on and ideas for as the creator economy builds?

\n\n\n\n

Tiffany Bridge: I mean, I\'m seeing, I\'m seeing a lot of people kind of fall back to newsletters, which is very cool in like retro, right? Like this idea of [00:36:00] like email, like we\'ve all got email. We neglected our email boxes for a while, but now it\'s back email\'s back, baby. Um, I think that\'s really interesting. And, and you know, and we\'re still seeing like some consolidation there, right?

\n\n\n\n

Because then now it\'s like, oh, let\'s, let\'s have a CK and like, okay, but now you\'re like locked into ck, right? Yeah. Um, which, which is a little bit of a concern, but you can at least like export. Subscribers out from ck, like if nothing else, like you can take your list with you, which I think is really great.

\n\n\n\n

CK has put together like a really easy to use stack of things that you need to run a four page newsletter. And, um, and so they\'re, they\'re popular for a reason, even if I still think people should have websites mm-hmm. , um, you know, but, but we are seeing that and even within sub, I\'m starting to see people like branch out into.

\n\n\n\n

Having websites like ghosts, which is another open source project. I\'m seeing people do that instead. Um, I think it\'s, it\'s really interesting right now because we ha we\'re in this moment where like the, the platform, the [00:37:00] social media platforms are really starting to show the seams and, and it\'s starting to feel like maybe we\'re on the edge of something.

\n\n\n\n

And I was just talking about this with a friend of mine the other day, and cuz he was saying like, Man, like Google Reader died and it kind of killed R Ss, right? Like, and nobody\'s figured that problem out since then. I\'m like, well, no, because everybody just started aggregating through Twitter. Twitter\'s the new, your new Google reader, except now like Twitter is twittering.

\n\n\n\n

And, um, because then we all, you know, we, and, and that, and again, that\'s like that problem of consolidation. Like even Google Reader, which was aggregating sources, it was like the dominant r s s reader. And I don\'t know, I don\'t know how to solve that problem. decent, uh, of centralization. Right. But I think it\'s very interesting that we\'re seeing people kind of move to newsletters because then they at least know that they can contact you.

\n\n\n\n

Mm-hmm. , and, and you can, um, and you, and you can have more control of your audience that way. Well, and then I\'m watching people like try out, like mastered on and that\'s interesting. [00:38:00] I don\'t, I don\'t know how that\'s gonna go cuz I feel like Mastodon is still. It\'s too difficult from like an administrative perspective.

\n\n\n\n

Like it\'s too difficult to start an instance right. Still. Um, I was talking about this actually in post status Slack the other day. I feel like a big reason that I ever got as far as I did with WordPress is cuz they had that five minute install so early on. Yeah. Like even in 2004, it was easy enough to install that I could figure it out myself and that like, I tried to set up ma on like ju like just like on a Nexus test account and like, , we don\'t have a way to run that particular form of like, of SQL that it uses of S SQL L and so like, like I would immediately stop and like, well, I.

\n\n\n\n

Like this, this thing doesn\'t even, like, it has dependencies that aren\'t necessarily available everywhere. And um, and then you have to, like, there\'s all this stuff that you have to do to set it up. And I\'m like, and you all have to, and it all has to be done from the command line. Um, so I feel like, you [00:39:00] know, these kind of like federated platforms where you run under an instance are gonna have to put a lot of attention into installation and onboarding if they want to, if they really wanna take off.

\n\n\n\n

I think that\'s gonna be a big thing.

\n\n\n\n

Cory Miller: What I take from this too is really going back to if you\'re thinking about building a business, even if you\'re dancing for passion, all of a sudden you\'re back in. You go, oh my gosh, I\'m a business owner. The thought process here to me is make sure you understand. What you own and what you\'re renting or borrowing for a time.

\n\n\n\n

Yeah, and just like you said, like I think so much from the we, I think we so much, by the way, benefit from de decentralization, AK WordPress, . You can, yes, you can copy it, you can for it and do whatever you want with WordPress. And there\'s power in that. And that shift of power where another platform has the rules.

\n\n\n\n

and regulations and policies that they change like Instagram, changing from more focus on [00:40:00] video to compete what\'s, let\'s say a TikTok and you go mm-hmm. Well, and, and I\'m not looking at my analytics all the time, but I look at likes, right? And I go, well, my likes went down quite a bit. Well, because I don\'t do video, I don\'t want to do video.

\n\n\n\n

Right. And right. Then you go, there\'s a way to build, it seems like build some initial audience, but make sure you have these off-ramps into something, even like an email list, you said, much less complex to export your subscriber list and go to another platform than e-commerce, but be really choosy and picky about what you\'re doing because.

\n\n\n\n

When your business does continue to grow, you want to be able to grow with it in the right platform to do that.

\n\n\n\n

Tiffany Bridge: Absolutely. Absolutely. And also, you know, as like the thing about decentralization is that there are a lot of problems that we are accustomed to having platforms solved for us. That now we have to solve on our own a decentralized situation.

\n\n\n\n

And so those of us who\'ve been working in open source a long time and and who work in tech, kind of like we already understand that like moderation is a problem and you have to think [00:41:00] about it. But you\'ve got all these, like for example, new MA on instance, admins who\'ve never really thought about moderation is like a problem.

\n\n\n\n

They have to solve , , and, and, and you\'d better. Right? And so, and that\'s like a, I think that\'s gonna be a real adjustment for people to make as we kind of like, if we\'re, if we\'re really gonna see like the beginning of a decentralization here, like there\'s gonna be a lot of like lessons that have to get relearned.

\n\n\n\n

Cory Miller: Yes. And when you said that about the five minute install, raise my hand because I go, that\'s why I loved WordPress. I didn\'t have to, what\'s a command line? What\'s the, you know, how do I. Upload, install, extract, set up my databases. Like that kind of simple. I\'ve seen so many tools over the years that promise some decentralization.

\n\n\n\n

But it\'s great for the developers that know all those things. But for the everyday person, once that gets figured out, that five minute or click, click install, I, I think we\'re gonna see some shifts in power.

\n\n\n\n

Tiffany Bridge: Yeah, I think so too. I think, um, I think if they pay a lot of, at pay more attention to that, I think you\'ll start to see a lot more.

\n\n\n\n

Cory Miller: [00:42:00] Tiffany, thanks so much for being on, um, post draft today and sharing some of your background and obviously your vision values, and then, um, what you\'re doing over at Nexus with store Builder and the other products. Um, tell, tell people where they can find you.

\n\n\n\n

Tiffany Bridge: Well, um, my slightly less neglected these days.

\n\n\n\n

Personal blog is tiff.is so, https://tiff.is/, you can find me there as long as there\'s still a Twitter. You can find me on Twitter at Tiffany. And, uh, you can find me on Mastodon at, uh, Tiffany@theinternet.social social.

\n\n\n\n

Cory Miller: Awesome. Thanks so much, Tiffany.

\n\n\n\n

Tiffany Bridge: All right. Thank you.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 18:45:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Olivia Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:12;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:60:\"WordPress.org blog: The Month in WordPress – December 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14191\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"https://wordpress.org/news/2023/01/the-month-in-wordpress-december-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:12476:\"

Last month at State of the Word, WordPress Executive Director Josepha Haden Chomphosy shared some opening thoughts on “Why WordPress” and the Four Freedoms of open source. In this recent letter, she expands on her vision for the WordPress open source project as it prepares for the third phase of Gutenberg:

\n\n\n\n
\n

“We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.”

\nJosepha Haden Chomphosy
\n\n\n\n

December brought with it a time for reflection—a time to look back, celebrate, and start planning new projects. Read on to find out what 2023 holds for WordPress so far.

\n\n\n\n
\n\n\n\n

WordPress is turning 20!

\n\n\n\n

2023 marks the 20th anniversary of WordPress’ launch. The project has come a long way since the first release as it continues to advance its mission to democratize publishing. From its beginnings as a blogging platform to a world-leading open source CMS powering over 40% of websites.

\n\n\n\n

Join the WordPress community in celebrating this important milestone. As the anniversary date approaches, there will be events, commemorative swag, and more.

\n\n\n\n
\n

Stay tuned for updates.

\n
\n\n\n\n
\n\n\n\n

WordPress 6.2 is scheduled for March 28, 2023

\n\n\n\n

Work on WordPress 6.2, the first major release of 2023, is already underway. It is expected to launch on March 28, 2023, and will include up to Gutenberg 15.1 for a total of 10 Gutenberg releases.

\n\n\n\n

The proposed schedule includes four Beta releases to accommodate the first WordCamp Asia and avoid having major release milestones very close to this event.

\n\n\n\n
\n

Read more about the 6.2 schedule and release team.

\n
\n\n\n\n
\n\n\n\n

What’s new in Gutenberg

\n\n\n\n

Two new versions of Gutenberg have shipped in the last month:

\n\n\n\n
    \n
  • Gutenberg 14.8 was released on December 21, 2022. This version features a reorganized Site Editor interface with a Browse Mode that facilitates navigation through templates and template parts. In addition, it includes the ability to add custom CSS via the Style panel and a Style Book that provides an overview of all block styles in a centralized location.
  • \n\n\n\n
  • Gutenberg 14.9 became available for download on January 4, 2023. It introduces a new “Push changes to Global Styles” button in the Site Editor, which allows users to apply individual block style changes to all blocks of that type across their site. Other features include typography support for the Page List block, and the ability to import sidebar widgets into a template part when transitioning from a classic theme.
  • \n
\n\n\n\n
\n

Learn how Gutenberg’s latest releases are advancing the Site Editor experience to be more intuitive and scalable.

\n
\n\n\n\n
\n\n\n\n

Team updates: WordPress big picture goals, new Incident Response Team, and more

\n\n\n\n\n\n\n\n
\n

Check out the 2022 State of the Word Q&A post, which answers submitted questions that Matt could not address at the live event.

\n
\n\n\n\n
\n\n\n\n

Feedback & testing requests

\n\n\n\n\n\n\n\n
\n

Have thoughts for improving the Five for the Future contributor experience? This post calls for ideas on how this initiative can better support the project and the people behind it.

\n
\n\n\n\n
\n\n\n\n

WordPress events updates

\n\n\n\n\n\n\n\n
\n

Would you like to be a speaker at WordCamp Europe 2023? Submit your application by the first week of February.

\n
\n\n\n\n
\n\n\n\n
\n\n\n\n

Have a story we should include in the next issue of The Month in WordPress? Fill out this quick form to let us know.

\n\n\n\n

The following folks contributed to this edition of The Month in WordPress: @cbringmann, @laurlittle, @rmartinezduque.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"rmartinezduque\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:13;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:112:\"Do The Woo Community: Bringing WordPress Certification to the Community with Talisha Lewallen and Sophia DeRosia\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74322\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:44:\"https://dothewoo.io/wordpress-certification/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:427:\"

Talisha Lewallen & Sophia DeRosia from CertifyWP chat with us about the importance of WordPress certification.

\n

>> The post Bringing WordPress Certification to the Community with Talisha Lewallen and Sophia DeRosia appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 10:57:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:14;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: WooCommerce Blocks 9.4.0 Adds Support for Local Pickup\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141197\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"https://wptavern.com/woocommerce-blocks-9-4-0-adds-support-for-local-pickup\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1740:\"

WooCommerce Blocks version 9.4.0 was released with support for a new block-powered Local Pickup option under shipping settings. The feature plugin offers users early access to new blocks and improvements to existing blocks before they become available in WooCommerce core.

\n\n\n\n

Local Pickups introduces two new blocks: a shipping method toggle block that allows shoppers to select between regular shipping or pickup from a specified location, and a pickup location block that displays local pickup rates.

\n\n\n\nimage source: WooCommerce Blocks 9.4.0 release post\n\n\n\n

These blocks can both be enabled and configured via a new local pickup settings page. Store owners can even rename Local pickup to something else, and optionally add a price for this option.

\n\n\n\n\n\n\n\n

It’s important to note that the new Local pickup blocks can only be used with the Checkout block. WooCommerce Blocks also introduces a change with this new Local Pickup experience that will support location-based taxes based on the pickup address, improving tax reporting. Previously, WooCommerce based local pickup taxes on the store address.

\n\n\n\n

WooCommerce Blocks 9.4.0 includes a handful of other small enhancements and bug fixes. Check out the release post for a more detailed look at everything that’s new in the latest version of the plugin.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 02:59:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:15;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:21:\"Matt: Polls on Tumblr\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:22:\"https://ma.tt/?p=75803\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:38:\"https://ma.tt/2023/01/polls-on-tumblr/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:301:\"

We just launched polls on Tumblr, and it’s been pretty fun. Cool to bring together the Crowdsignal (née Polldaddy) technology into a new world.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 19 Jan 2023 01:38:22 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Matt\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:16;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"WPTavern: WordPress Project Aims to Complete Customization Phase and Begin Exploring Collaboration in 2023\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141181\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:117:\"https://wptavern.com/wordpress-project-aims-to-complete-customization-phase-and-begin-exploring-collaboration-in-2023\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3124:\"

WordPress Executive Director Josepha Haden Chomphosy published a summary of the project’s “big picture” goals for 2023. The goals fall into three major categories: CMS, Community, and Ecosystem.

\n\n\n\n

WordPress development will focus on completing the remaining tasks for Phase 2 (Customization), and will move on to begin exploring Collaboration in Phase 3.

\n\n\n\n

“As we prepare for the third phase of the Gutenberg project, we are putting on our backend developer hats and working on the APIs that power our workflows,” Haden Chomphosy said in her recent Letter to WordPress.

\n\n\n\n

“Releases during Phase 3 will focus on the main elements of collaborative user workflows. If that doesn’t make sense, think of built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and programmatic editorial and pre-launch checklists.”

\n\n\n\n

The vision for the first two phases was “blocks everywhere” and Haden Chomposy said this will be updated for Phase 3 to be centered on the idea of “works with the way you work.”

\n\n\n\n

In addition to the Phase 3 APIs, Haden Chomphosy identified the following items as part of the CMS goals for 2023:

\n\n\n\n
    \n
  • Openverse search in Core
  • \n\n\n\n
  • Navigation block
  • \n\n\n\n
  • Media management
  • \n\n\n\n
  • Simplify the release process
  • \n\n\n\n
  • PHP 8.2 compatibility (Core and Gutenberg)
  • \n\n\n\n
  • Block theme development tools
  • \n
\n\n\n\n

Under the Community category, WordPress will be focusing on planning the Community Summit, which will be held at WordCamp US in 2023, contributor onboarding, improving Polyglot tools, establishing mentor programs, revamping WordPress.org designs, and keeping pace with learning content. The project is also aiming to develop a canonical plugin program, which should be helpful as some Performance team contributors recently expressed that they don’t fully understand what the process is for canonical plugins.

\n\n\n\n

The Ecosystem category will focus on the WordPress Playground, an experimental project that uses WebAssembly (WASM) to run WordPress in the browser without a PHP server with many useful applications for contributors.

\n\n\n\n

WordPress contributors also prevailed upon Matt Mullenweg to consider having the project devote some time to working through old tickets and fixing bugs. Mullenweg said he is amenable to tackling one long-standing ticket (the kind that are stuck because of missing decisions or multiple possible solutions) each month in 2023.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 22:57:07 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:17;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"Post Status: Big Picture Goals 2023 • WP 6.2 Planning • LearnWP Needs Analysis • Wrong Plugins\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146539\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"https://poststatus.com/big-picture-goals-2023-wp-6-2-planning-learnwp-needs-analysis-wrong-plugins/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:16840:\"

This Week at WordPress.org (January 16, 2023)

\n\n\n

Where is WordPress going in 2023? Read Josepha\'s Big Picture Goals for the year. WordPress certifications are in the planning phases, and the foundation will include LearnWP. The Training Team is conducting a Needs Analysis. Help gather the community\'s input. Plugins Team is seeking intentionally wrong plugins, and Core has the 6.2 Planning Roundup.

\n\n\n
\n\n\n\n\n\n\n\n

News

\n\n\n\n\n\n\n\n

\n\n\n\n
\n
\n

Central

\n\n\n\n\n\n\n\n

CLI

\n\n\n\n\n\n\n\n

Community

\n\n\n\n\n\n\n\n

Core

\n\n\n\n\n\n\n\n

WordPress 6.2

\n\n\n\n\n\n\n\n

Meetings

\n\n\n\n\n\n\n\n

Developer Blog

\n\n\n\n\n\n\n\n

Design

\n\n\n\n\n\n\n\n

Docs

\n\n\n\n\n\n\n\n

Hosting

\n\n\n\n\n\n\n\n

Marketing

\n\n\n\n\n\n\n\n

Meta

\n\n\n\n\n\n\n\n

Openverse

\n\n\n\n\n
\n\n\n\n
\n

Performance

\n\n\n\n\n\n\n\n

Polyglots

\n\n\n\n\n\n\n\n

Plugins

\n\n\n\n\n\n\n\n

Project

\n\n\n\n\n\n\n\n

Test

\n\n\n\n\n\n\n\n

Themes

\n\n\n\n\n\n\n\n

Training

\n\n\n\n\n\n\n\n

Online Workshops

\n\n\n\n\n\n\n\n

Tutorials

\n\n\n\n\n\n\n\n

WPTV

\n\n\n\n\n
\n
\n\n\n\n
\n\n\n\n\n\n\n\n\n\n\n\n

Thanks for reading our WP dot .org roundup! Each week we are highlighting the news and discussions coming from the good folks making WordPress possible. If you or your company create products or services that use WordPress, you need to be engaged with them and their work. Be sure to share this resource with your product and project managers.

Are you interested in giving back and contributing your time and skills to WordPress.org? \"🙏\" Start Here ›

Get our weekly WordPress community news digest — Post Status\' Week in Review — covering the WP/Woo news plus significant writing and podcasts. It\'s also available in our newsletter. \"💌\"

\n\n\n\n
\n\n\n\n
\"Post
\n

You — and your whole team can Join Post Status too!

\n\n\n\n

Build your network. Learn with others. Find your next job — or your next hire. Read the Post Status newsletter. \"✉\" Listen to podcasts. \"🎙\" Follow @Post_Status \"🐦\" and LinkedIn. \"💼\"

\n
\n\n\n\n
\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 20:57:41 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Courtney Robertson\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:18;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:80:\"WPTavern: #59 – Corey Maass on How To Use WordPress To Kickstart Your SaaS App\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"https://wptavern.com/?post_type=podcast&p=141113\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:94:\"https://wptavern.com/podcast/59-corey-maass-on-how-to-use-wordpress-to-kickstart-your-saas-app\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:46303:\"Transcript
\n

[00:00:00] Nathan Wrigley: Welcome to the Jukebox podcast from WP Tavern. My name is Nathan Wrigley.

\n\n\n\n

Jukebox is a podcast which is dedicated to all things WordPress. The people, the events, the plugins, the blocks, the themes, and in this case, how WordPress can be used to get your SaaS app off the ground.

\n\n\n\n

If you’d like to subscribe to the podcast, you can do that by searching for WP Tavern in your podcast player of choice, or by going to WPTavern.com forward slash feed forward slash podcast. And you can copy that URL to most podcast players.

\n\n\n\n

If you have a topic that you’d like us to feature on the podcast, I’m keen to hear from you and hopefully get you or your idea featured on the show. Head to WPTavern.com forward slash contact forward slash jukebox. And use the form there.

\n\n\n\n

So on the podcast today, we have Corey Maass.

\n\n\n\n

Corey is a full stack developer who works with agencies and businesses, large and small. He specializes in advanced WordPress functionality and building products for, and using, WordPress.

\n\n\n\n

Over the last decade or so SaaS, or software as a service, apps have become more and more popular. Not only are we using our computers more, but with the rise of smartphones, we’re connected to our services all the time. There does not appear to be any corner of life where online platforms don’t have some presence. From email to taxis, fitness to food planning and delivery. You can find it all in a SaaS app somewhere.

\n\n\n\n

Now that many people are comfortable using SaaS apps, there’s been a deluge of new players coming into the market, but it won’t surprise you to learn that most of them fail to make an impact and shut up shop.

\n\n\n\n

Corey is on the podcast today to talk about why he thinks that building an MVP, or minimum viable product, app on top of WordPress is a good way to start your product journey.

\n\n\n\n

We talk about how WordPress comes bundled with many of the features that apps require. User login, roles, permissions, and the REST API. This means that you don’t have to reinvent the wheel for the things that WordPress already does.

\n\n\n\n

On top of that, the plugin ecosystem which surrounds WordPress, might enable you to short circuit the need to build all the features that your service needs. It may be that there’s an existing plugin, which does most of what you require, and is ready to go right away.

\n\n\n\n

Corey talks about how using WordPress in this way might enable you to see if there’s really a market for your app. And if there’s not, you’ve used less resources finding that out. And if there is, then you might have some revenue to develop the app in other ways.

\n\n\n\n

If you’ve toyed with the idea of creating a SaaS app in the past, but never quite got there, this episode is for you.

\n\n\n\n

If you’re interested in finding out more, you can find all of the links in the show notes by heading to WPTavern.com forward slash podcast. Where you’ll find all the other episodes as well.

\n\n\n\n

And so without further delay, I bring you Corey Maass.

\n\n\n\n

I am joined on the podcast today by Corey Maass. Hello, Corey.

\n\n\n\n

[00:03:58] Corey Maass: Hey there.

\n\n\n\n

[00:03:58] Nathan Wrigley: Very nice to have you on. Corey, we’re going to talk today all about the capabilities of WordPress as a SaaS platform. But as we typically do on this podcast, it would be very nice if we could orientate the listeners, allow them to figure out what your credentials are, what your WordPress chops are, if you like. So would you spend a few moments just giving us a brief potted history of your relationship with tech and WordPress more specifically?

\n\n\n\n

[00:04:24] Corey Maass: Absolutely. Back in the late nineties in college, a roommate of mine introduced me to this internet thing and the first websites I saw were some of my favorite bands. And I was a aspiring musician at the time, and I said, well, I want to appear as famous as they are. How do I make one of these website things, and the rest is history.

\n\n\n\n

I taught myself basic web design, web development. That led to learning some programming, JavaScript and then ASP classic way back in the day. But around that time there was the new trend of SaaS apps. 37 Signals was popular talking about this. Forums like Joel Spolsky’s, Joel on Software. And I caught the bug because I’ve always had an entrepreneurial streak.

\n\n\n\n

So I said, oh, this internet thing, building software, but not selling a download, but selling access to a website. So, I started going down that path, building websites for clients, but also building SaaS apps to try to sell on the side. And then WordPress took off and for a number of years, WordPress was pretty much my day job. Doing development or website setup or what have you, and then building Sass apps. Not using WordPress for a number of years.

\n\n\n\n

And then suddenly the light bulb went off. One, the WordPress market was getting bigger and bigger, and I realized that there actually was money in it. So that led me to start building plugins, which I think is what had you and I talking last time. But also at some point it occurred to me that WordPress had matured enough and solved enough of the problems that I was encountering over and over building SaaS apps that I said, let me look at WordPress as a SaaS platform, and I’ve been doing it ever since. So now it’s been probably five years or something, and WordPress only continues to mature, and this conversation continues to evolve.

\n\n\n\n

[00:06:27] Nathan Wrigley: So you, in the last few years, you’ve joined together the idea of a SaaS platform, but with WordPress handling some of the basic things in the background, if you like. I say basic, I just mean some of the things that we are more familiar with in WordPress. So user management, obviously if you throw some other things like WooCommerce at it, you may be able to handle billing or subscription or whatever it might be, and getting people to the right page depending on whether they’re logged in or not. Is it basically the promise of that? You can cut out a whole body of work, which you would need to build, well potentially from scratch, each time you create your own new SaaS app?

\n\n\n\n

[00:07:04] Corey Maass: Yeah, I think that’s the way to think about it. So, when you’re solving problems for people online, these days it’s definitely more broad than it was five years ago and 10 or 15 years ago, of course. So if you’re building something that’s B2B, technically speaking. So if you’re trying to build an API or some sort of true service that other systems are going to talk to. WordPress is probably not the answer you want.

\n\n\n\n

The REST API is, has come a long way, but it’s not really what it’s meant for, right? But if you think of most B2C apps, business to consumer, most of these apps are websites that you’re signing into. Well, WordPress accommodates that. You’re clicking through from page to page. WordPress accommodates that. You’re taking billing, you’re handling subscriptions. WordPress with WooCommerce or Easy Digital Downloads, or Restricted Content Pro or any number.

\n\n\n\n

I’ve been paying more attention to the membership plugins lately, which are in some ways are specifically designed to handle exactly this problem. Users signing in and doing something, interacting. Interacting with the website. Interacting with each other, that kind of thing. One of the things that, an example that I pick up on a lot is, years ago when I was building apps regularly for clients, for friends, for myself. Over and over and over again, I had to implement some sort of user password reset. And it’s so mundane. Once you’ve solved it once, it’s boring to solve as a developer. But it’s crucial to every app.

\n\n\n\n

And I got to the point where I was like, I just don’t want to ever think about this stupid problem again. But I had to integrate the code, again every time over and over again. It’s like with WordPress, I never have to think about that. And there’s a plugin called Theme My Login, that’s one of my favorites that you drop in and users can register for your website and immediately get access to a slash dashboard, which you can change. But arguably that’s the first huge leap, you set up a basic website.

\n\n\n\n

You want users to be able to register and have exclusive access to a page that they don’t have if they haven’t signed in or haven’t paid or what have you. So, these kinds of plugins just solve all of these basic problems. The bottom of the pyramid, so to speak. So that you can get onto whatever problem, your unique problem, that your SaaS is going to solve. As opposed to spending days, weeks, months, tackling the not unique problems like user registration.

\n\n\n\n

[00:09:36] Nathan Wrigley: So what you are suggesting here, let’s just lay this out. The audience that you are suggesting this to, is people who want to get something shipped quickly. And really, if you are at the beginning of your SaaS app journey, you’re not quite sure yet whether the market even exists. You’re just trying to float a solution to something that you believe might be viable in the marketplace, but you’re not sure.

\n\n\n\n

So we’re creating a shortcut. We’re offsetting the billing, the user management and so on to WordPress, just as a, as a quick way of getting an MVP or a minimum viable product out there. Is that the idea? Just to sort of test the water? WordPress is a good bet for that, and then presumably at some point you would advise that if it turns out to be an out and out success, then maybe, at that point you might need to look at different tooling.

\n\n\n\n

[00:10:28] Corey Maass: Not necessarily. There was a time when I would’ve said that definitively, but WordPress has come a long way. Hosting has come a long way. Optimization has come a long way. So it’s definitely the scenario that I’m using WordPress the most. I’ve got a new idea, or I’m working with somebody and they’ve got a new idea and this is how I want to get it off the ground.

\n\n\n\n

But there are a number of companies, big companies, in the WordPress space that continue to work, use WordPress as the core of their SaaS app, and they’ve got plenty of customers. I think it really, when you get to that level of, if you see a, a good amount of success, then there’s going to be technical problems to overcome.

\n\n\n\n

And so it’s either ramping up hosting, server power or optimizing queries or rewriting certain aspects of your app. We can talk about that. I had to do that for one of mine, about a year ago. Or again, depending on the amount of user inactivity or user, user interactivity, how much and how often your users are using your app, you may find that it handles it just fine.

\n\n\n\n

[00:11:43] Nathan Wrigley: So right at the beginning you started talking about why you use WordPress. You mentioned a few plugins, which might assist you on this journey. So I think some of the ones that you mentioned were things like Easy Digital Downloads, WooCommerce, and so on. Whilst I don’t want to necessarily promote certain plugins, I’m just wondering if, given the experience that you’ve had, if you could give us some tips as to plugins that you have found to be helpful for particular problems that you’ve faced while you’ve been trying to build it. And then in a few moments we’ll get onto the subject of how you’ve had to amend WordPress to do things, let’s say more efficient.

\n\n\n\n

[00:12:20] Corey Maass: Sure. So these days, I actually use Beaver Builder for building pages out. Beaver Builder’s a page builder. Elementor is another good one. But I find that doubling down and knowing these tools well, helps greatly with being able to solve a variety of problems because they’re not a theme, so they’re not locked into a certain layout or that kind of thing.

\n\n\n\n

But most SaaS apps have a pattern called CRUD, create, retrieve, update, and delete. So if it’s Twitter, then you are creating tweets. You are retrieving tweets, meaning you’re viewing all of them. You can’t really update tweets, but you can update your profile, that kind of thing. And again, you can’t really delete tweets, but you could delete your account, and that kind of thing. Facebook, you can create posts, you can delete posts, your viewing posts, so your retrieving posts, that kind of thing.

\n\n\n\n

So, a lot, a lot, a lot of software comes down to that pattern, and so using something like, Advanced Custom Fields and there’s a great plugin called ACF Front End, I think it’s called, that essentially puts an ACF form on the front end. So that’s how users can create and update. You could also use Gravity Forms. Or there are a couple of other plugins, form plugins, that you can then put on the front end, for again, collecting data from users or letting users post data. Essentially insert data into the database. And then using something like Beaver Builder or Elementor that have post modules.

\n\n\n\n

So it’s like if I was recreating Twitter, I would create a form, and this obviously once I’m logged in, but I would create a form that said, what do you want to tweet? And that would insert it into the database as a post record. And then I would use Beaver Builder, me personally, but you could use Elementor or again, any number of page builders, with a posts module that says, okay, show all posts, meaning tweets, with the author of Corey. So then you’ve just created a way to create tweets and then for somebody else to go look at all of Corey’s tweets, that kind of thing.

\n\n\n\n

So thinking, breaking it down to these kinds of patterns and then looking at these different plugins on how to solve them. A lot of the time I’m able to find ways to quickly implement. And it, again, it doesn’t have to be quick, and this doesn’t have to be forever, but a lot of the time it can be where WordPress and these plugins can solve these problems so that my SaaS offers the, again, the unique problem or solves the unique problem that I’m, the whole reason I’m building it in the first place.

\n\n\n\n

To get back to your question about those other plugins in particular. If you only want users to sign in, I love the plugin called Theme My Login. Again, look at membership plugins. And then, if you want to charge, again, break down the problem. What are you actually, what do you want? Usually you want subscriptions, like that’s a SaaS pattern that most people are used to now. And what are users paying for? Usually they’re paying for access to a page or pages or content or some feature to interact with other users or something like that. And there are plenty of plugins that restrict content. Which is the way to think about that.

\n\n\n\n

And so there’s literally Restricted Content Pro as a plugin. Easy Digital Downloads, which is e-commerce, but they have an add-on for restricting content. WooCommerce is really more e-commerce, but can handle this kind of stuff. And then again, membership plugins that are, as people are setting up communities, as at least some people are trying to get away from social media and get back to more private communities without relying on Facebook groups or Twitter or what have you.

\n\n\n\n

Membership plug-ins have been mature for a while, but are, I’m seeing them become even more and more popular. And are designed exactly for this. So a user pays for access to features, pages, what have you. And that’s again, kind of the core of most SaaS apps.

\n\n\n\n

[00:16:24] Nathan Wrigley: I suppose that if you are thinking of building a SaaS app, you must have some kind of kernel of an idea of whatever it is that you are trying to solve. So, you’ve got this fabulous idea, and the most important thing at that point is to judge whether or not this idea A, can be built, and let’s assume that after sitting down and thinking it through and mapping it out, you’ve decided, yep, yeah, this has got legs. This can be built with the technology that’s currently available on the web.

\n\n\n\n

And then thinking, okay, is there an audience for this? Are there going to be enough people out there who are willing to open their wallet to make it worthwhile? And if you go down the SaaS route, you may very well be an incredibly adept developer, in which case this may be in your purview.

\n\n\n\n

But if you are not and you are just trying to figure out whether the market is there and you want to do that affordably, then WordPress seems like a fairly decent bet, just because of what you said. The fact that with 60,000 plus plugins in the WordPress repository and countless more that you can purchase, in many cases for a very small amount of money.

\n\n\n\n

It may be that you can get 90%, 80%, 70% of the features that you are trying to build, but without having to do much in the way of custom coding. It may be that you can’t get a hundred percent of the way there, and that would require some tweaking, which we’ll get into. But is that essentially it? You know, you might have to cut some corners or, on your roadmap, cut out some of the things that you really thought would be nice to have in and just go for the things which can be enabled quickly and affordably.

\n\n\n\n

[00:17:58] Corey Maass: Yeah, I think it just depends on what you’re trying to accomplish. I have a buddy who is non-technical, knows enough CSS to be dangerous, which he’s learned over times, specifically for this scenario. He wanted to create a mentor program, and so he needed scheduling for matching mentorees to mentors.

\n\n\n\n

So we found a plugin that did that, or did that well enough. And then put I think a membership plug in. I don’t remember how he handled subscriptions. But basically put WordPresses stylized user management in front of it. Limited access to features based on a user being logged in or a user paying. And then a little bit of CSS to make it look a little more integrated or little more branded or what have you.

\n\n\n\n

And that was kind of all he needed. It solved the problem. He was able to charge for it. He got some customers. And then at some point he did end up hiring a developer to add a few bells and whistles or whatever features he found that were missing. But yeah, it got him 70, 80% of the way. Arguably it got him a hundred percent of the way of solving the problem enough that at least users could start using it.

\n\n\n\n

[00:19:10] Nathan Wrigley: Yeah, I suppose that’s it, isn’t it? If he’s got a core body of users, and he’s determined that, in this case he can use a calendar plugin or whatever it may be, and it will get him the user base that he needs. Then he can start to use the revenue that’s generated from the, let’s call it the SaaS app, to invest in having something done bespoke.

\n\n\n\n

That’s really interesting. That’s kind of nice to know. I guess one concern, which I may have, and I’m sure you’ve come across this before. Is just the notion that if you did build this and you fully had the intention of it staying on WordPress for all time. Then you are of course very much dependent upon the plugins that you are using. The spaghetti of plugins being updated regularly.

\n\n\n\n

In many cases that would very much be the case. It’s updated frequently. It’s made secure, and any vulnerabilities and things like that are taken care of. But there is always that chance that the developer of a key part of your SaaS app may just decide to call it quits, and then you might be left hanging a little bit.

\n\n\n\n

[00:20:14] Corey Maass: And the scenario I’ve seen more often is a mature product. Meaning your own SaaS app evolves away from what the plugin that you purchased does. So I saw this with a very big company in the WordPress space, who long ago had built their platform on top of EDD, Easy Digital Downloads. But over time had hacked and slashed at it, so that they couldn’t update it anymore.

\n\n\n\n

And that’s just a decision they had to make at some point of whether they were going to keep going with EDD and just lean into the features that EDD had and forego the other features. Or most good, big WordPress plugins are well documented and have hooks so you can add function extra functionality, or figure out how to sort of hack around them, to a point.

\n\n\n\n

And then, yeah. They had to make the decision to just stop updating it, and there was discussion. Last I heard that they were going to maybe move to something custom altogether. But the idea being, one of my favorite phrases, we made the best decision we could with the information we had at the time, right?

\n\n\n\n

So starting out early. It solves all your problems. Go for it. And then down the road you can migrate away from it. You can code around it. You could build something custom, what have you. But yes, that is certainly a risk. I mean, it’s also a problem that a lot of apps have broadly speaking. So it’s, you know, if you’ve built an app that uses the Twitter or Facebook API, you’re putting yourself in their, their hands.

\n\n\n\n

Or if you are operating system dependent or even, something I’m seeing right now is, microchip dependent, right? If you build software for MacOS and it only works on Intel and, and they move to M1 or M2. So these are just risks that I think you assess over time.

\n\n\n\n

But what I like is, the point you keep emphasizing, that this is a, a way to solve the technical problem. What I think that a lot of SaaS founders, small and large, real and imaginary, don’t take into account and, I struggle with, and most of us struggle with, is that these days the technical lift of building an app often pales in comparison to the marketing.

\n\n\n\n

We hear about these wonderful, amazing stories, like Instagram selling for whatever it was, 8 billion after two months, and yada, yada, yada. Most SaaS apps fail. And so you, you want to build quickly with a low lift and then spend most of your time, like you said, trying to get it in front of customers, validating the idea, getting feedback from customers about what features they actually want, or now that you’ve built the features they want, does it actually solve the problem for them?

\n\n\n\n

All of that is arguably way more important than the actual platform you use. But that’s what brings me back to WordPress as a platform, is in fact often a great way to get something out the door. Even if it’s just a form to collect data and then a page builder or a theme of some kind to then show the data back to the user, if that’s what solves the problem.

\n\n\n\n

[00:23:36] Nathan Wrigley: It’s interesting because if there’s a body of people listening to this who are not building SaaS apps on WordPress, and they’re just building client websites, you’ve probably encountered that scenario where the client comes and they have incredibly grandiose expectations of what they want the website to do.

\n\n\n\n

And because you’ve been building websites for so long, you just know, you have an instinct which says, well, we could build all of that. But how about we just start here? Because I would imagine it’s quite unlikely that your staff are actually going to start using some kind of intranet solution that we build as WordPress. Or some messaging system that we build in the app. It’s much more likely that they’ll continue to use things like Facebook Messenger or WhatsApp or Slack or whatever it may be.

\n\n\n\n

And so over the years you’ve become accustomed to figuring out what is plausible, what is likely to work, and I think I feel it’s the same with SaaS apps. It’s very easy to come to the table. You’ve got your blank canvas and you throw everything at it, every idea, every permutation, every possible thing that the app could do, and then decide that’s what must be built.

\n\n\n\n

That’s it. Until that is all done, we’re not going to launch it. And I think history shows that you have to be much more agile than that. You have to be able to drill it down and say, okay, what’s the 10, 20, 30% of all of that, that we’ve decided upon, which is going to get us off the ground? And so that feels like where this goes. If you try to build everything, it’s probable that you’ll A run out of money, B run out of time, and nothing will be shipped.

\n\n\n\n

Whereas in your scenario, offset the uninteresting jobs that probably don’t need to be tackled because they’ve already been tackled by plugins or WordPress Core. And just concentrate on the things which are going to benefit your users. And frankly, you don’t know what is going to benefit your users.

\n\n\n\n

It’s always amazing to me when I open up a new SaaS app that I’ve never use before. And you think, oh, this will be perfect what I need. And you end up on support saying, does it do this? No, I wish it did that. And those companies that succeed tend to be, well in my experience, the ones who listen to their early adopters and quickly pivot their solution to satisfy them.

\n\n\n\n

[00:25:45] Corey Maass: Exactly. There’s obviously no harm in thinking through what your dream app does, all the features. You make a long, long list. But one of the things that drew me to WordPress plugins, and selling WordPress plugins early on, was a rather cynical observation that I made.

\n\n\n\n

I was building blogs for customers. I was building e-commerce websites for customers. And instead of writing another article, which is hard and work. Or instead of inserting more products, which is hard and feels like work. A lot of my clients would get in the WordPress plugin repo where all the plugins are free and go, oh, I could use a to-do list plugin and they’d install it.

\n\n\n\n

Or, it’s winter. I should install a plugin that adds snowflakes falling over my theme. And they would waste an unbelievable amount of time on what felt productive and felt free. And I was like, well, if people are people, we are all human, we are all valuable and we are all, don’t want to do the things that are hard.

\n\n\n\n

But I see all these people that are spending time just digging through the plugin repo, I’m going to start building and selling plug-ins, because the discoverability is amazing. And so I think you’ve touched on that for SaaS as well, which is, we generally shy away from the things that are hard.

\n\n\n\n

We also tend to skew towards our own genius. What we think is the best idea. Because we thought of it isn’t necessarily the features, or it isn’t ecessarily solving the problem that your actual paying customers have. The real strength, and the real challenge, comes more in that side of things. Marketing, sales, talking to customers, getting over your own ego, optimizing your own time, all that kind of stuff.

\n\n\n\n

[00:27:48] Nathan Wrigley: Yeah. It’s interesting the marketing piece you mentioned. Never ceases to amaze me how much of the overall budget needs not to be sunk into the development of the actual software, but in alerting people to its existence. A significant amount. And it’s not to be underestimated.

\n\n\n\n

And obviously if at the beginning you sink a hundred percent of your finances into the code, that’s great, but I guess you better be a really good word of mouth, somebody that can spread by word of mouth incredibly successfully. Because experience at least tells me that it’s very hard to gather an audience from a standing start.

\n\n\n\n

So we’re a WordPress podcast. We’re obviously very keen on WordPress, we think it’s amazing. But I’m guessing that there must be downsides to this. Let’s just talk about that for a moment. Any drawbacks to this system that you’ve encountered over time? Just some quick examples may be that, well, does it scale very well? Does WordPress tend to be doing a lot of things in the background that a leaner, more specifically custom-built solution may get you out the hole of? Just questions around that. Any drawbacks that you would alert people to if they do decide to go down this approach?

\n\n\n\n

[00:28:59] Corey Maass: A few years ago, I was tasked with building a food subscription website. So think Blue Apron or Freshly kind of website, if you’re familiar with those. And for better or worse was told that I had to use WooCommerce. And so I spun up a WordPress website, installed WooCommerce, got subscriptions going, customized the choose the meals that you want, and then check out. And that all was okay.

\n\n\n\n

But it turned out that, I think some of this has been changed, because this was a number of years ago but, WooCommerce was storing all of the data in a very WordPressy way, which was fine because it was a known pattern. But was not very optimal. And then for the business, because all of those meals were cooked every morning and then shipped out, all of the charges had to go through at the same time, at like two in the morning. And it turned out that WooCommerce subscriptions was built so that if you signed up for a subscription at 10:30 in the morning, it would renew at 10:30 in the morning. While we needed it to renew at two in the morning so that all of the orders went through, so then the chef knew how many dishes to make, and how many chicken dishes to make or whatever.

\n\n\n\n

And that’s the kind of risk that you run into, right? So if you are using a third party piece of software, WordPress, and then with plugins. And you are essentially building it to your, or bending it to your will, so that it’s doing things that it’s not necessarily meant to do. You’re going to run into issues.

\n\n\n\n

We found that our server didn’t have enough power to process all of these orders at the same time, because it’s essentially multiple threads need to be run at the same time. We wound up in that instance sticking with WooCommerce and WordPress for at least a little while longer.

\n\n\n\n

But switching off of a hosting company that really was most popular for blogs and delivering content and not necessarily running process, CPU power. And moving to a custom AWS set up. And we watched the CPU go from 80% all the time, to 3% all the time. So in that instance, we just needed to throw more metal at it.

\n\n\n\n

But again, we were definitely using a tool, at least slightly, in ways that it wasn’t meant to do. I also, during the pandemic, or at the beginning of the pandemic, my wife made the mistake of turning to me and saying, you know, my family plays this game called Mexican Train, in person all the time. Boy, I wish there was an online version. And she should just know better than to put that kind of idea in my head.

\n\n\n\n

So within a couple of months I had spun up the only interactive online version of Mexican Train, which was great for our family, but it’s a very popular game in retirement communities. And naturally during the pandemic a lot of people in retirement communities were isolating a lot more. The game became quite popular, because it spread word of mouth. And the first Christmas, I think I built it early in the year, and, and the first Christmas it peaked at like 2,600 concurrent games or something. Which, for me, I had never built anything that needed quite that much power.

\n\n\n\n

And it did eventually fall over. But initially I’d built it so that every time somebody played, all the other games, so four people are playing, basically all four games are sitting there pinging the server, looking for updates. That’s very inefficient because most of those pings don’t return anything, but the CPU still has to accommodate them. So I wound up switching to a pushing system. So I had to integrate with that. And originally I had built it so that the game itself, so when you’re signing into mexicantrain.online, that’s the website, the login screen you’re seeing is Theme My Login.

\n\n\n\n

All of the delivery of content, so like when you go to the My Games page and you see all of your games, that’s just Beaver Builder. And then the actual game I had to build, so it was quite a lift as far as development goes. But that was what that SaaS needed. But I built an app in a JavaScript framework called React that then talks to the server.

\n\n\n\n

Well, I built the initial version using the WordPress API. So my game talked to WordPress, functionality that was built into WordPress. And the API worked, until it didn’t. So, in that instance, again, too many people hitting the server too much. Aw, shucks, it was too successful.

\n\n\n\n

I had to revisit it after a year or two and build a custom API. Now I’m a developer. I have that luxury, right? But these are things that, I got enough of a version out the door. So, thinking about it from the perspective of a non-developer. I could have set up most of it except for the game itself.

\n\n\n\n

And the game is sponsored by donations. So I installed GiveWP, which is one of the bigger WordPress donation plugins. And I still used the free version. And so I got most of those sort of basic stuff using third party plugins out of the box. And then if I wasn’t a developer, I might have had to hire a developer.

\n\n\n\n

And so yes, I would’ve had to put some money into it. But they wouldn’t have had to build everything. And I also could conceivably hire different developers, or I could by using WordPress. So one of the things we haven’t talked about is because of the popularity of WordPress, you also have a lot more developers to choose from if you’re going to hire somebody.

\n\n\n\n

But anyway, if I wasn’t a developer, I would’ve had to hire somebody to build the game. And then down the road, presumably I would’ve proven that the platform was popular, hopefully in the form of donations, which would’ve been enough money to then hire somebody to rebuild the API, if I couldn’t have done it myself.

\n\n\n\n

You know? So there’s sort of this evolution of, as you’ve said. Try things, see if it’s popular, and then maybe hire somebody if you have to, you know, if you’re going to grow parts of the platform, parts of the app beyond WordPress.

\n\n\n\n

[00:35:40] Nathan Wrigley: It’s really interesting you mentioning about all of the very large number of WordPress developers. The developers I guess, go into different niches, don’t they? They might be experts in one field or another. Do you detect that there’s a lot of people doing this kind of thing? Building SaaS on top of WordPress. Or is it just you shouting into an empty room? What I’m basically saying is, is there a community, a subset of the WorldPress developer community who, like you, are interested in building SaaS apps on top of WordPress.

\n\n\n\n

[00:36:10] Corey Maass: There is a book called Building Web Apps with WordPress that came out from O’Reilly. So it’s popular enough that people are writing books about it. I’ve given talks on it at a few different WordCamps as far back as I think four or five years ago or more. And I’ve come across a number of people who are doing it, or are thinking about it or have done it. But it’s definitely not, and even Mullenweg has talked about it, but it’s not the most common use case.

\n\n\n\n

I think in part because people just don’t necessarily think about SaaS apps separately as much anymore. More and more websites do something. And so if they have functionality, maybe that people are paying for, and users are signing in to use the web app to do something.

\n\n\n\n

It’s a SaaS app. But that’s, again, I think more and more commonly just how people view websites. So it’s not necessarily something that people are thinking about or searching for. Except for, I think, as you’ve mentioned a few times, if you’re looking for no code now means something different. But if you’re looking for a non-developery way to spin something up quickly using third party software, then it still gets some attention. But to answer your question, no, I’ve never found a community. I’ve thought about starting one, but never have. Because I just haven’t gotten a sense that enough people are talking about it.

\n\n\n\n

Which is okay. Maybe at some point they will, or, you know, maybe some other better solution will come along and consistently solve the problems. But, right here, right now, I still find WordPress a great option.

\n\n\n\n

[00:37:57] Nathan Wrigley: It’s really interesting because curiously, there’s a great deal of overlap with something that’s going on in my world at the moment in that I have been working with a developer on a SaaS app. I won’t go into the details, but reached a point where a couple of years ago, the interest in it, from my point of view, I think probably, is best to describe it. It waned a little bit and so it went on the back burner and it’s never been revived.

\n\n\n\n

And as a couple of years have gone by, I’ve decided that, actually wouldn’t it be nice to revive this? And so with a couple of friends decided that, yeah, let’s give this another go. But actually, let’s just begin again, because I’ve noticed there’s significant things in what’s already been built that I would change.

\n\n\n\n

And guess what we’ve decided to do? We’ve decided to do the MVP inside of WordPress. Basically for pretty much all the reasons that you’ve suggested. We’re familiar with it. There are sometimes free, sometimes commercially available plugins, which will do a significant amount of the lifting. Will it be exactly what we would like from our roadmap? No. Will it be close enough to get us to measure whether there’s an audience for this? Yes, I think it will. And so, curious that this is actually playing itself out in my life at this moment.

\n\n\n\n

[00:39:19] Corey Maass: Nice, yeah. Depending on the problems you’re trying to solve, but I think that’s like most things, a bit of planning, sit down, design. I encourage everybody to do this. What is the all the bells and whistles version. We nerds are a big fan of what’s called the 80 20 rule.

\n\n\n\n

So what’s the 20% that needs to be solved now, today to prove the idea? And then see what plugins align with that. How they can get you there. Will it solve the problem? Do you need custom development? Are there features that just don’t have solutions or aren’t solved by any of the plugins you might want to use.

\n\n\n\n

And then go from there. See what you can do. The nice thing too about WordPress is you can start locally, which is free. Locally meaning on your computer, not locally in your town, although you can do that too. Most computers using software like Local WP, I’m a big fan of, and there’s a few others. Also InstaWP, which lets you spin up instances of WordPress online for free, for, you know, seven days or something, and then pay to keep them, or you can download them, I think, I don’t know.

\n\n\n\n

I definitely have been guilty of getting an idea and I needed to illustrate the idea rather than just write the idea down. So I spun up an instance of WordPress real quick. Installed a couple of plugins real quick, and then said, what do I need next? Or what would the next step be? Or, if I was a user, what would I expect to see next? All that cost me was a little bit of time. There’s kind of that advantage too, where it’s, you can use it for wire framing means something specific, but conceptually you can use it for wire framing ideas, which I think is crucial. Without it costing you anything.

\n\n\n\n

[00:41:04] Nathan Wrigley: Corey, if people listening to this, if they’re resonating with it and they’re thinking actually, do you know what, this is something that I’ve been doing for a while, or, I’m curious to get into the community that you said might need to exist. Where would be the best place to get in touch with you?

\n\n\n\n

[00:41:20] Corey Maass: Honestly, the place that I talk about this the most is Twitter. twitter.com/coreymaass, c o r e y m a a s s. Just start a conversation with me. I’d love to hear people who are interested in this. If this resonated with them, if they’ve tried it at all. Because again, I’ve run into people who have done it. I’ve heard about people doing it. A book exists. So there must be people talking about it somewhere.

\n\n\n\n

But I think it would be neat to have a community of people, or even just a network of people, helping each other out, solving some of these problems. Hey, does anybody have a good recommendation for a plugin that solves such and such a functional, or a problem that I have. Where should I start? Suggestions for hosting companies. I mean, there’s, there’s always information to be shared. And honestly, that’s one of my favorite things about the WordPress community is that it’s so open. So many people are talking to each other and willing to help each other. I definitely think there could be more conversation around using WordPress as a SaaS platform.

\n\n\n\n

[00:42:21] Nathan Wrigley: Corey Maass. Thank you for chatting to us on the podcast today.

\n\n\n\n

[00:42:25] Corey Maass: My pleasure.

\n
\n\n\n\n

On the podcast today we have Corey Maass.

\n\n\n\n

Corey is a full-stack web developer who works with agencies and businesses, large and small. He specialises in advanced WordPress functionality and building products for, and using, WordPress.

\n\n\n\n

Over the last decade or so, SaaS, or software as a service, apps have become more and more popular. Not only are we using our computers more, but with the rise of smartphones, we’re connected to our services all the time.

\n\n\n\n

There does not appear to be any corner of life where online platforms don’t have some presence. From email to taxis, fitness to food planning and delivery. You can find it all in a SaaS app somewhere.

\n\n\n\n

Now that many people are comfortable using SaaS apps, there’s been a deluge of new players coming into the market, but it won’t surprise you to learn that most of them fail to make an impact, and shut up shop.

\n\n\n\n

Corey is on the podcast today to talk about why he thinks that building a MVP, or minimum viable product, app on top of WordPress is a good way to start your product journey.

\n\n\n\n

We talk about how WordPress comes bundled with many of the features that apps require, user login, roles, permissions and the REST API. This means that you don’t have to reinvent the wheel for the things that WordPress already does.

\n\n\n\n

On top of that, the plugin ecosystem which surrounds WordPress might enable you to short circuit the need to build all the features that your service needs. It may be that there’s an existing plugin which does most of what you require, and is ready to go right away.

\n\n\n\n

Corey talks about how using WordPress in this way might enable you to see if there’s really a market for your app. If there’s not, you’ve used less resources finding that out. If there is, then you might have some revenue to develop the app in other ways.

\n\n\n\n

If you’ve toyed with the idea of creating a SaaS app in the past, but never quite got there, this episode is for you.

\n\n\n\n

Useful links.

\n\n\n\n

37 Signals

\n\n\n\n

Joel Spolsky’s, Joel on Software

\n\n\n\n

Easy Digital Downloads

\n\n\n\n

WooCommerce

\n\n\n\n

Advanced Custom Fields

\n\n\n\n

ACF Frontend

\n\n\n\n

Gravity Forms

\n\n\n\n

Beaver Builder

\n\n\n\n

Elementor

\n\n\n\n

Theme My Login

\n\n\n\n

Restrict Content Pro

\n\n\n\n

Corey’s Mexican Train website

\n\n\n\n

GiveWP

\n\n\n\n

Building Web Apps with WordPress book

\n\n\n\n

Local WP

\n\n\n\n

InstaWP

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 15:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Nathan Wrigley\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:19;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:46:\"Do The Woo Community: Looking at Code as Words\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74188\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:45:\"https://dothewoo.io/looking-at-code-as-words/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:425:\"

The hurdle is getting past looking at code and saying, \"Oh, this is code. I can\'t understand it.\" You\'re not looking at zeros and ones, you\'re looking at words you can understand.

\n

>> The post Looking at Code as Words appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 11:07:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:20;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:125:\"WPTavern: Jetpack Revamps Mobile App, WordPress.com Users Must Migrate to Keep Using Stats, Reader, and Notification Features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141116\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:133:\"https://wptavern.com/jetpack-revamps-mobile-app-wordpress-com-users-must-migrate-to-keep-using-stats-reader-and-notification-features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:6594:\"

When Automattic launched a mobile app for Jetpack in June 2021, it was targeted mainly at users who were on a paid Jetpack plan, as it enables access to features like backups, restores, and security scanning. Most importantly, the app gave Automattic a more direct path for monetizing Jetpack, without adding more commercial interests into the official WordPress apps.

\n\n\n\n

This week Jetpack announced that it has revamped the app and is offering a more compelling reason for those using the free plan to migrate. As part of a longterm effort to refocus the official WordPress apps, features that require Automattic’s products (the Jetpack plugin or a WordPress.com account) in order to use them, will soon be removed. This includes the Stats, Reader, and Notifications features, which have been relocated to the Jetpack app.

\n\n\n\nWordPress.com announcement for the revamped Jetpack app\n\n\n\n

WordPress.com users and Jetpack users on the free plan who previously relied on these features will need to switch to the free Jetpack mobile app. All the features that are moving over from the core WordPress app will still be free in the Jetpack app.

\n\n\n\n

While most self-hosted Jetpack users may easily understand the need for the switch, this transition may be rougher for WordPress.com users who do not understand the history of the mobile apps and see it all as “WordPress.” They may not be aware that Automattic’s integrated products have been controversial features in the official WordPress apps for nearly a decade.

\n\n\n\n

The announcement on WordPress.com is confusing, as it presents Jetpack as just a new optional app and doesn’t convey the urgency of migrating if users still want access to stats, notifications, the reader, and any additional paid features.

\n\n\n\n

The post’s FAQ section describes the Jetpack app as “the premium mobile publishing experience for our super-connected world” and states that “the Jetpack app is free to download.” WordPress.com users who commented on the post found the words “premium” and “free to download” to be suspicious and confusing. They don’t understand the reason for two apps:

\n\n\n\n
\n

“Do we have to change over? I only want to blog, I’m not technical and I don’t understand why you have done this or how to use it?”

\n
\n\n\n\n
\n

“So is WordPress now called Jetpack?”

\n
\n\n\n\n
\n

“If it ain’t broke, don’t fix it. This move is not in your users’ best interests so why is it being done? This smacks of the recent pricing debacle.”

\n
\n\n\n\n
\n

“I’m really disappointed by this decision. Why are you forcing us to use two apps? Your explanation of the differences makes no sense, and sounds like you made a decision for some reason you won’t tell us and you’re just trying to justify it. This is not user-focused at all.”

\n
\n\n\n\n

Users are also concerned about data loss, as those who are migrating to the newly revamped app are advised to delete the WordPress app after installing the Jetpack app. The announcement states that “Managing your site across both apps is currently unsupported and may lead to issues like data conflicts.”

\n\n\n\n

One user asked if there are premium features in the Jetpack app that will carry additional cost, and if there is any advertising included within the app.

\n\n\n\n

“For clarity, the Jetpack app is free to use and doesn’t include in-app advertisements,” Automattic representative Siobhan Bamber said.

\n\n\n\n

“We’re still planning our 2023 roadmap, and it’s possible in-app purchases will be a part of our plans. The driving goal would be to offer features that bring most value to users, and we’re keen to hear any ideas or feedback. Any in-app purchases would be optional, with the currently free features remaining free to use.”

\n\n\n\n

In response to those asking about the differences between the two apps, Bamber said there will be a couple more posts on the WordPress.com news blog in the following weeks.

\n\n\n\n

Users will need to have the latest version of the WordPress app installed in order to automatically migrate their data and settings to the Jetpack app. This includes locally stored content, saved posts, and in-app preferences. The FAQ states that after users download the Jetpack app, they will be “auto-magically” logged in with all their content in place.

\n\n\n\n

“One good way to confirm whether your version of the WordPress app supports ‘auto migration’ is to tap one of the in-app ‘Jetpack powered’ banners,” Bamber advised users in the comments. “You’ll find these banners at the bottom of sections including Stats and Reader. If you tap the banner, you’ll only see the ‘Switch to the new Jetpack app’ prompt in versions that support migration.”

\n\n\n\n

The revamped Jetpack app has been presented to WordPress.com users as a more feature-rich way to publish to their websites, but it also lays the burden of choice on users to try to understand the difference between the two apps and select one for all the sites they manage. Many don’t want the inconvenience of switching to a new app. Based on the users’ responses, it might have been easier for them to understand that the official WordPress apps are removing all features require the Jetpack plugin or a WordPress.com account – instead of selling it as a new, shiny publishing experience.

\n\n\n\n

Migrating to the Jetpack app is the best option if you want to continue using the Stats, Reader, and Notifications features. In order to make it easy for users to choose the best path forward, future posts on WordPress.com should make it crystal clear what features users can expect in each app and when they will need to take action.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 18 Jan 2023 04:57:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:21;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"HeroPress: Reflecting on My 3 Foundational Pillars\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=5037\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:152:\"https://heropress.com/essays/reflecting-on-my-3-foundational-pillars/#utm_source=rss&utm_medium=rss&utm_campaign=reflecting-on-my-3-foundational-pillars\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:9650:\"\"Pull\n

I strongly believe that every experience we have up to our most current place in time shapes our identity. With that being said, as we go about living our lives it is not always obvious to see just how those compounding experiences shape us into who we are today. This is what makes all our journeys unique and worth reflecting on, because in our past often lies the tools and budding potential that influences the possibilities in our future.

\n\n\n\n

With that said, I’d like to share three pillars of my journey that have shaped me as a person and become the foundations of my current work.

\n\n\n\n

Technology

\n\n\n\n

I’ve found in one way or another that I have always lived technology-adjacent. When I was a kid my family had a Super Nintendo in the house which I always loved playing Super Mario World on– this device was essentially my first step into computers before we got our first home computer in the house when I was around 6 years old. By age 10, our computer was connected to AOL dial-up, which then allowed me to explore the early internet more deeply– MIRC, Livejournal, AOL Games, Neopets, MySpace… you name it. For the first time my world expanded beyond my immediate home of Rancho Cucamonga, CA, and El Paso, TX and into the interconnected world of the web.

\n\n\n\n
\n

Due to this opportunity of early access to computers, I became proficient in typing and navigating the internet at a very young age.

\n
\n\n\n\n

From what I’ve described so far, one would think that I was on track for a technology-related degree; however, there weren’t any people in my family or immediate network of friends who held a position in tech– so the idea that the computer could become a tool to propel my career didn’t really click until after graduating college.

\n\n\n\n

A slight detour– I’m a Social-Anthropologist by trade, having graduated from Lewis and Clark College with a Bachelor’s in East Asian Studies and a minor in Japanese. Following my passion for Japan and inter-cultural studies, I moved back to Tokyo after college and it was about one year later when I landed my first job as a Product Manager for a mobile gaming company called Cocone. This was my reintroduction to the idea that I could have a technology-driven career.

\n\n\n\n

In between working at Cocone and my return to the United States in 2016 I held a couple jobs that were not necessarily reliant on strong technical knowledge such as English Teacher, Executive Assistant, and even working at a karaoke bar. What my time as a Product Manager taught me, however, is that I do have a large thirst for working in the technology space so when I moved back to the States I applied to a Digital Agency called ASA Digital to get me back into that space.

\n\n\n\n

After a year at ASA Digital being a sort of Jack of all trades on projects that included mobile apps, web apps, websites, MR/ER/VR/AR, I knew that this was the right choice for me. Sometimes when you know you know, and when I moved on from ASA Digital to Automattic I was fully embracing my love for and need to have technology in my life.

\n\n\n\n

Diversity, Equity, Inclusive, and Belonging

\n\n\n\n

I haven’t always been aware of what the world now collectively calls DEIB, but since I was little I disliked the idea of injustice and lies. I have also faced adversity in the past due to who I am and what I look like, and it never sits right with me when others are in this kind of predicament as well. Due to this, DEIB practices deeply impacted my values and how I show up to work and with other people.

\n\n\n\n

It wasn’t until around 2019 that I became more involved in the world of DEIB in an official capacity at Automattic or at the incluu, LLC (a woman-owned and operated consultancy specializing in DEIB-thoughtful product strategy and advisement), and this is when I further developed this lens by participating in webinars on various DEIB topics, taking on leadership roles in the space, and keeping my eyes open to not only injustices that are happening but how they are being responded to.

\n\n\n\n
\n

The principles behind DEIB affect everyone and every aspect of our daily lives in some capacity, and embracing this space more fully not only allowed me to better understand the many systemic practices at play that keep us all from moving forward positively, but it also opened my mind to the real needs of people all over the world. 

\n
\n\n\n\n

Everyone deserves to live in a world or operate in a space with dignity and mutual respect.

\n\n\n\n

Community Building

\n\n\n\n

While I can understand the intent around the phrase “don’t mix friend groups”, I was never the type to follow this social role wholeheartedly. There are many times in our lives when we are put in situations where we interact with people we wouldn’t necessarily have engaged with such as school projects, clubs, sports, work, etc., and while it’s not all the time, sometimes a positive reaction can occur and we can meet someone new and interesting through these random groupings.

\n\n\n\n

I’m not quick to make friends, but when I do create a strong friendship it is because we share values and experiences which serve as the foundation for our relationship despite any other differences. Maybe it’s because of my (still ongoing) gaming days, but I tend to visualize people in the world as a character with a rich background story and something only they can bring to the table.

\n\n\n\n
\n

It has always brought me joy to bring people together and see how these chain reactions occur.

\n
\n\n\n\n

It could be that by some happenstance one of the friends is recruiting, they share a similar hobby, or come from a similar background. Facilitating safe spaces where folks can develop a sense of community has always been a passion of mine.

\n\n\n\n

I have had the pleasure of building community in the WordPress community through various outlets like BlackPress, with the Training Team, and even in Automattic’s Black employee resource group Cocoamattic.

\n\n\n\n

The Outcome

\n\n\n\n

Early last year I applied for the Community Education Manager with a basic idea based off of the job description of what I would be doing– fast forwarding to today I have found that the three pillars shared above gave me the tools I need to perform in this role successfully.

\n\n\n\n

As a Community Education Manager I work to break down perceived barriers for folks who want to contribute to the Make WordPress Training Team’s goals, and work as a close partner with the Training Team Representatives and members to empower them to excel in their leadership, goals, and strategy. I also help shepherd the Faculty Program, and therefore work to enable these folks to fully own and participate in their roles.

\n\n\n\n
\n

When working with our contributors, I focus on building relationships, encouraging engagement, and enabling contributions.

\n
\n\n\n\n

We have contributors from all over the world, so I also take care to be mindful of any language or cultural differences that may be at play and lean in with curiosity to better understand each community’s unique needs.

\n\n\n\n

When working with our Team Reps, I similarly focus on building relationships, and work with them (not for them) to create an environment where the goals of the team can be realized. 

\n\n\n\n

Lastly, I work with our Faculty Program Members by building relationships and connecting them with work related to their role, and with contributors who can benefit from their expertise and mentorship.

\n\n\n\n

Can you see how my pillars are directly impacting and influencing the work I currently do?

\n\n\n\n

Exploring Your Own Foundational Pillars

\n\n\n\n

There are probably many articles with thought-provoking exercises that can lead you in your own self-reflection, so I’ll leave you all with just a some questions from me that have worked to get me started:

\n\n\n\n
    \n
  • What have you been given positive feedback on lately?
  • \n\n\n\n
  • What actions/things bring you the most joy in life?
  • \n\n\n\n
  • What actions/things make you feel motivated?
  • \n\n\n\n
  • When was the last time you found yourself “in the zone”?
  • \n
\n\n\n\n

As you go through the questions for yourself don’t discredit or try to change your initial thoughts.

\n\n\n\n

Using these as a starting point, even if what comes up doesn’t immediately surface something that could be a pillar, you’ll surely learn or get to acknowledge something about yourself that shapes your character and how you present in the world.

\n\n\n\n

Take your time with it– the way we walk through life is a long-term journey which is constantly being updated by new experiences along the way.

\n

The post Reflecting on My 3 Foundational Pillars appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 17 Jan 2023 23:00:08 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Destiny Kanno\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:22;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:89:\"Do The Woo Community: Accepting Cryptocurrency in a WooCommerce Store with Lauren Dowling\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74258\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://dothewoo.io/cryptocurrency-woocommerce-store/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:521:\"

Lauren Dowling, lead product for Coinbase commerce joins returning guest Dave Lockie from Automattic as the conversation covers accepting cryptocurrency on WooCommerce shops, whether it be for your clients sites or your own.

\n

>> The post Accepting Cryptocurrency in a WooCommerce Store with Lauren Dowling appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 17 Jan 2023 11:06:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:23;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:79:\"WordPress.org blog: WP Briefing: Episode 47: Letter from the Executive Director\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://wordpress.org/news/?post_type=podcast&p=14175\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/episode-47-letter-from-the-executive-director/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:8660:\"

On episode forty-seven of the WordPress Briefing podcast, Executive Director Josepha Haden Chomphosy shares her vision and current thinking for the WordPress open source project in 2023. Rather read it? The full letter is also available.

\n\n\n\n

Have a question you’d like answered? You can submit them to wpbriefing@wordpress.org, either written or as a voice recording.

\n\n\n\n

Credits

\n\n\n\n

Editor: Dustin Hartzler
Logo: Javier Arce
Production: Santana Inniss
Song: Fearless First by Kevin MacLeod

\n\n\n\n

Show Notes

\n\n\n\n

make.WordPress.org/core
Join the 6.2 Release!
Submit Topics for the Community Summit!

\n\n\n\n

Transcript

\n\n\n\n\n\n\n\n

[Josepha Haden Chomphosy 00:00:00] 

\n\n\n\n

Hello everyone, and welcome to the WordPress Briefing, the podcast where you can catch quick explanations of the ideas behind the WordPress open source project, some insight into the community that supports it, and get a small list of big things coming up in the next two weeks. I’m your host, Josepha Haden Chomphosy. Here we go.

\n\n\n\n

[Josepha Haden Chomphosy 00:00:40] 

\n\n\n\n

Last month at State of the Word, I shared some opening thoughts about why WordPress. For me, this is an easy question, and the hardest part is always knowing which lens to answer through. Though I always focus on the philosophical parts of the answer, I know that I often speak as an advocate for many types of WordPressers.

\n\n\n\n

[Josepha Haden Chomphosy 00:01:00] 

\n\n\n\n

So as we prepare ourselves for the start of a new year, I have a few additional thoughts that I’d like to share with you, my WordPress community, to take into the year with you. 

\n\n\n\n

Firstly, the Four Freedoms. If you have already listened to State of the Word, you have heard my take on the philosophical side of open source and the freedoms it provides.

\n\n\n\n

But if you didn’t, then the TL;DR on that is that open source provides protections and freedoms to creators on the web that I really think should just be a given. But there are a couple of other things about the Four Freedoms, and especially the way that WordPress does this kind of open source-y thing that I think are worth noting as well.

\n\n\n\n

One of those things is that WordPress entrepreneurs, those who are providing services or designing sites, building applications, they have proven that open source provides an ethical framework for conducting business. No one ever said that you aren’t allowed to build a business using free and open source software, and I am regularly heartened by the way that successful companies and freelancers make the effort to pay forward what they can.

\n\n\n\n

[Josepha Haden Chomphosy 00:02:02]

\n\n\n\n

Not always for the sole benefit of WordPress, of course, but often for the general benefit of folks who are also learning how to be entrepreneurs or how to kind of navigate our ecosystem. And the other thing that I love about the Four Freedoms and the way that WordPress does it is that leaders in the WordPress community, no matter where they are leading from, have shown that open source ideals can be applied to the way we work with one another and show up for one another.

\n\n\n\n

As a community, we tend to approach solution gathering as an us-versus-the-problem exercise, which not only makes our solutions better, it also makes our community stronger. 

\n\n\n\n

As I have witnessed all of these things work together over the years, one thing that is clear to me is this: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, but open source methodologies represent a process that can change the way we approach our work and our businesses.

\n\n\n\n

[Josepha Haden Chomphosy 00:03:01] 

\n\n\n\n

The second big thing that I want to make sure you all take into the year with you is that we are preparing for the third phase of the Gutenberg project. We are putting our backend developer hats on and working on the APIs that power our workflows. That workflows phase will be complex. A little bit because APIs are dark magic that binds us together, but also because we’re going to get deep into the core of WordPress with that phase.

\n\n\n\n

If you want to have impactful work for future users of WordPress, though, this is the phase to get invested in. This phase will focus on the main elements of collaborative user workflows. If that doesn’t really make sense to you, I totally get it. Think of it this way, this phase will work on built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and things like programmable editorial, pre-launch checklists.

\n\n\n\n

[Josepha Haden Chomphosy 00:04:00] 

\n\n\n\n

So phases one and two of the Gutenberg project had a very ‘blocks everywhere’ sort of vision. And phase three and, arguably, phase four will have more of a ‘works with the way you work’ vision.

\n\n\n\n

And my final thought for you all as we head into the year is this, there are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was State of the Word 2013, where Matt dreamed on stage of a true WYSIWYG editor for WordPress. Some say it was State of the Word 2016, where we were all encouraged to learn JavaScript deeply. For a lot of us though, it was at WordCamp Europe in 2018 when the Gutenberg feature plugin first made its way to the repo.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like it’s been a long time because it has been a long time. But I can also confirm that it takes many pushes to knock over a refrigerator. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:00] 

\n\n\n\n

For early adopters, both to the creation of Gutenberg as well as its use, hyperfocus on daily tasks makes it really hard to get a concept of scale.

\n\n\n\n

And so I encourage everyone this year to look out toward the horizon a bit more and up toward our guiding stars a bit more as well. Because we are now, as we ever were, securing opportunity for those who come after us because of the opportunity that was secured for us by those who came before us. 

\n\n\n\n

[Josepha Haden Chomphosy 00:05:33] 

\n\n\n\n

That brings us now to our small list of big things. It’s a very small list, but two pretty big things. The first thing on the list is that the WordPress 6.2 release is on its way. If you would like to get started contributing there, you can wander over to make.WordPress.org/core. You can volunteer to be part of the release squad. You can volunteer your time just as a regular contributor, someone who can test things — any of that. 

\n\n\n\n

[Josepha Haden Chomphosy 00:06:00] 

\n\n\n\n

We’ll put a link in the show notes. And the second thing that I wanted to remind you of is that today is the deadline to submit topics for the Community Summit that’s coming up in August. That comes up in the middle of August, like the 22nd and 23rd or something like that. 

\n\n\n\n

We’ll put a link to that in the show notes as well. If you already have chatted with a team rep about some things that you really want to make sure get discussed at the community summit, I think that we can all assume that your team rep has put that in. But if not, it never hurts to give it a second vote by putting a new submission into the form.

\n\n\n\n

And that, my friends, is your small list of big things. Thank you for tuning in today for the WordPress Briefing. I’m your host, Josepha Haden Chomphosy, and I’ll see you again in a couple of weeks.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Santana Inniss\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:24;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:69:\"WordPress.org blog: Letter from WordPress’ Executive Director, 2022\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14180\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:81:\"https://wordpress.org/news/2023/01/letter-from-wordpress-executive-director-2022/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5901:\"

Last month at State of the Word, I shared some opening thoughts about “Why WordPress.” For me, this is an easy question, and the hardest part is knowing which lens to answer through. The reasons that a solopreneur will choose WordPress are different than the reasons a corporation would. And while artists and activists may have a similar vision for the world, their motivations change their reasons, too. That’s why I always focus on the philosophical parts of the answer because I know that I am speaking as an advocate for many types of WordPressers. I have a few other reasons, too, which you may not be aware of as you use our software every day.

\n\n\n\n

Why WordPress?

\n\n\n\n

Most importantly, the Four Freedoms of Open Source. If you have already listened to State of the Word, you have heard my thoughts on the philosophical side of open source and the freedoms it provides. If you didn’t, then the tl;dr on that is that open source provides protections and freedoms to creators on the web that should be a given. There’s an extent to which the idea of owning your content and data online is a radical idea. So radical, even, that it is hard for folks to grasp what we mean when we say “free as in speech, not free as in beer.” Securing an open web for the future is, I believe, a net win for the world especially when contrasted to the walled gardens and proprietary systems that pit us all against one another with the purpose of gaining more data to sell.

\n\n\n\n

A second reason is that WordPress entrepreneurs (those providing services, designing sites, and building applications) have proven that open source offers an ethical framework for conducting business. No one ever said that you cannot build a business using free and open source software. And I am regularly heartened by the way successful companies and freelancers make an effort to pay forward what they can. Not always for the sole benefit of WordPress, but often for the general benefit of folks learning how to be an entrepreneur in our ecosystem. Because despite our competitive streaks, at the end of the day, we know that ultimately we are the temporary caretakers of an ecosystem that has unlocked wealth and opportunity for people we may never meet but whose lives are made infinitely better because of us.

\n\n\n\n

And the final reason is that leaders in the WordPress community (team reps, component maintainers, and community builders) have shown that open source ideals can be applied to how we work with one another. As a community, we tend to approach solution gathering as an “us vs. the problem” exercise, which not only makes our solutions better and our community stronger. And our leaders—working as they are in a cross-cultural, globally-distributed project that guides or supports tens of thousands of people a year—have unparalleled generosity of spirit. Whether they are welcoming newcomers or putting out calls for last-minute volunteers, seeing the way that they collaborate every day gives me hope for our future.

\n\n\n\n

As I have witnessed these three things work together over the years, one thing is clear to me: not only is open source an idea that can change our generation by being an antidote to proprietary systems and the data economy, open source methodologies represent a process that can change the way we approach our work and our businesses. 

\n\n\n\n

WordPress in 2023

\n\n\n\n

As we prepare for the third phase of the Gutenberg project, we are putting on our backend developer hats and working on the APIs that power our workflows. Releases during Phase 3 will focus on the main elements of collaborative user workflows. If that doesn’t make sense, think of built-in real-time collaboration, commenting options in drafts, easier browsing of post revisions, and programmatic editorial and pre-launch checklists.

\n\n\n\n

If Phases 1 and 2 had a “blocks everywhere” vision, think of Phase 3 with more of a “works with the way you work” vision. 

\n\n\n\n

In addition to this halfway milestone of starting work on Phase 3, WordPress also hits the milestone of turning 20 years old. I keep thinking back to various milestones we’ve had (which you can read about in the second version of the Milestones book) and realized that almost my entire experience of full-time contributions to WordPress has been in the Gutenberg era.

\n\n\n\n

I hear some of you already thinking incredulous thoughts so, come with me briefly.

\n\n\n\n

There are a couple of different moments that folks point to as the beginning of the Gutenberg project. Some say it was at State of the Word 2013 when Matt dreamed of “a true WYSIWYG” editor for WordPress. Some say it was at State of the Word 2016 where we were encouraged to “learn Javascript deeply.” For many of us, it was at WordCamp Europe in 2017 when the Gutenberg demo first made its way on stage.

\n\n\n\n

No matter when you first became aware of Gutenberg, I can confirm that it feels like a long time because it has been a long time. I can also confirm that it takes many pushes to knock over a refrigerator. For early adopters (both to the creation of Gutenberg and its use), hyper-focus on daily tasks makes it hard to get a concept of scale.

\n\n\n\n

So I encourage you this year to look out toward the horizon and up toward our guiding stars. We are now, as we ever were, securing the opportunity for those who come after us, because of the opportunity secured by those who came before us.

\n\n\n\n

Rather listen? The abbreviated spoken letter is also available.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 12:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:7:\"Josepha\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:25;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:72:\"Do The Woo Community: Finding Team Members to Fit Your Companies Culture\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74204\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"https://dothewoo.io/finding-team-members-to-fit-your-companies-culture/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:442:\"

Marius Vetrici has built a process to bring in new employees that are drawn to fit his companies values, and to grow with them as a team member.

\n

>> The post Finding Team Members to Fit Your Companies Culture appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 16 Jan 2023 10:09:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:26;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:95:\"Gutenberg Times: Box Shadow, Newsletter Theme, Testing Call 20 and more – Weekend Edition 241\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://gutenbergtimes.com/?p=23190\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:100:\"https://gutenbergtimes.com/box-shadow-newsletter-theme-testing-call-20-and-more-weekend-edition-241/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:15190:\"

Howdy,

\n\n\n\n\n\n

Last week’s Live Q & A on Layout features went really well, with numerous participants. The post and the show notes are still in the works. The recording is available on YouTube, should you want to revisit parts of it or missed it entirely.

\n\n\n\n

Now that feature freeze for the major WordPress release is only three weeks away, the contributors would appreciate it if you could heed the 20th call for testing from the FSE Outreach program. You can help find quirks, bugs and annoyances, so they can be fixed before February 7th and during the round of beta version of the release.

\n\n\n\n

Have a lovely weekend!

\n\n\n\n

Yours, 💕
Birgit

\n\n\n\n\n\n\n\n\n\n\n\n

Developing Gutenberg and WordPress

\n\n\n\n

Gutenberg 15.0 release candidate is available for testing. Sticky positioning, resizable Site editor, updated to the Page List block, modify block style variations from global styles, and a lot more refinements are coming to the Gutenberg plugin

\n\n\n\n
\n

🎙️ New episode: Gutenberg Changelog #78 -State of the Word, WordPress 6.2, Gutenberg 14.8 and 14.9 with Birgit Pauli-Haack and special guest Hector Prieto

\n
\n\n\n\n

Last April, a group of contributors started working on research on how to best implement an API for to make blocks more interactive. This week, JuanMa Garrido shared a progress report: Update on the work to make building interactive blocks easier.

\n\n\n\n

The resources linked in the post are mostly code internals, so they are definitely very technical at this point. With that said, understanding how the new API works, will not be necessary for developers to use this new standard. A standard proposal will be published the next few months. So for now, this is all bit technical and architectural. The work on the underlying framework is shared on this GitHub Repository

\n\n\n\n
\n\n\n\n

Plugins, Themes, and Tools for #nocode site builders and owners

\n\n\n\n

Munir Kamal, GutenbergHub, shows you in his latest post How to Find and Use Block Patterns in WordPress. You learn, how to find patterns in the post and site editor, how to navigate the WordPress Pattern Directory and how to install the patterns via the plugin Extendify Patterns and Templates

\n\n\n\n

If you want to create your own patterns, but don’t know how to code them, you can use the plugin Blockmeister – Block Pattern Builder.

\n\n\n\n
\n\n\n\n

Sarah Gooding reports on the Lettre Newsletter Theme Now Available on WordPress.org, It can be used with the newly release newsletter feature in Jetpack plugin or as a stand-along theme. “The theme puts the focus on the subscription form, which is the most important thing a newsletter landing page can do – make it easy for people to sign up. Beneath the form there is a link to read all the posts, followed by another subscription form. All of these elements in the home page design are blocks, making it easy for them to be removed or rearranged.” Gooding wrote.

\n\n\n\n
\n\n\n\n

Will Morris explained the three ways add a Table of Contents in WordPress in is post for the Torque Magazine. The three ways are:

\n\n\n\n
    \n
  • Install a plugin
  • \n\n\n\n
  • Use on the Custom Table of Contents blocks
  • \n\n\n\n
  • Create you Table manually in the Block Editor.
  • \n
\n\n\n\n

Soon you will be able to use the core Table of Content’s block once it comes out of the experimental stage. It’s already available via the Gutenberg plugin.

\n\n\n\n

Theme Development for Full Site Editing and Blocks

\n\n\n\n

In his post, Justin Tadlock, walks you through the layout classes in WordPress 6.1. With the latest release of WordPress, the software has now centralized its layout definitions, created semantic class names, and reduced code duplication on container blocks. “Originally, this post was intended to be a quick look at the changes to the system for theme authors. However, given the heftiness of the topic, it has evolved into a full overview of the layout framework.” Tadlock wrote.

\n\n\n\n\n\n\n\n

In his second post published on the Developer Blog, Using the box shadow feature for themes, Justin Tadlock took a look at the box shadow support, that what just released in Gutenberg 14.9. As it happens with similar features, the first iteration of box shadow support is only available via code. The interface for the site editor screens are still in the works.

\n\n\n\n\n

 “Keeping up with Gutenberg – Index 2022” 
A chronological list of the WordPress Make Blog posts from various teams involved in Gutenberg development: Design, Theme Review Team, Core Editor, Core JS, Core CSS, Test and Meta team from Jan. 2021 on. Updated by yours truly. The index 2020 is here

\n\n\n\n\n

Daisy Olsen held her inaugural live programming session on Twitch this week. The recording is now available on YouTube. In this stream, she talked about:

\n\n\n\n
    \n
  • using LocalWP for local WordPress development,
  • \n\n\n\n
  • the Create Block Theme Plugin, and
  • \n\n\n\n
  • took a look at the code from a couple of existing block themes.
  • \n
\n\n\n\n

You need a Twitch account and follow DaisyonWP to get notified when she goes live.

\n\n\n\n\n\n\n\n

In his latest post for CSS-Tricks: Styling Buttons in WordPress Block Themes, Fränk Klein, takes a detailed look markup of various buttons and how to style them via the theme.json properties.

\n\n\n\n

Building Blocks and Tools for the Block editor.

\n\n\n\n

Tom McFarlin continued his series A Backend Engineer Learns to Build Block Editor Blocks with Part 5 in which he covers adding color controls to a custom block for the use case, when you want to give the user the option to select the colors for the block themselves.

\n\n\n\n

McFarlin, recommend the previous articles first as they build on top of each other. So far, he published:

\n\n\n\n
    \n
  1. Required Tools, Plugin Structure, Dependencies, Block Metadata
  2. \n\n\n\n
  3. The Backend, The Frontend, Functionality, Styles, a Working Demo
  4. \n\n\n\n
  5. Block Attributes, Editable Content, Components, Editor Styles
  6. \n\n\n\n
  7. Saving Data, Styling the Frontend
  8. \n
\n\n\n\n
\n\n\n\n

Phil Sola create a Custom Color Picker for WordPress. Sola added some improvements to the existing color picker. It’s more an experiment rather than a full-fledged solution. His exploration might also be an inspiration for others to start experimenting with WordPress component library.

\n\n\n\n
\n\n\n\n\n

Need a plugin .zip from Gutenberg’s master branch?
Gutenberg Times provides daily build for testing and review.
Have you been using it? Hit reply and let me know.

\n\n\n\n

\"GitHub

\n\n\n\n\n

Upcoming WordPress events

\n\n\n\n

February 4 + 5, 2023
WordCamp Birmingham, AL

\n\n\n\n

February 17 – 19, 2023
WordCamp Asia 2023 

\n\n\n\n

Check the schedule of WordCamp Central of upcoming WordCamps near you.

\n\n\n\n

Learn WordPress Online Meetups

\n\n\n\n

January 17, 2023 – 3pm / 20:00 UTC
Patterns, reusable blocks and block locking

\n\n\n\n

January 19th, 2023 – 10:30 ET / 15:30 UTC
Live stream: Building an Advanced Query Loop block variation plugin w/ Ryan Welcher @ryanwelchercodes

\n\n\n\n

January 19, 2023 – 7 pm ET / 24:00 UTC
Let’s make custom templates in the Site Editor!

\n\n\n\n

January 20, 2023 – 3 am ET / 8:00 UTC
Let’s make custom templates in the Site Editor!

\n\n\n\n

January 20, 2023 – 10:30 am 15:30 UTC
Block Themes and WordPress: Live Stream w/ Daisy Olsen @daisyonwp

\n\n\n\n

January 23, 2023 – 10 pm ET / 1 am UTC
Patterns, reusable blocks and block locking (APAC time zone)

\n\n\n\n

January 26, 2023 – 10:30 am ET / 15:30 UTC
Live stream: Reviewing developer-focused features in Gutenberg 15.0 w/ Ryan Welcher @ryanwelchercodes

\n\n\n\n

January 31, 2023 – 3pm ET / 20:00 UTC
Creating a photography website with the block editor

\n\n\n\n
\n\n\n\n\n

Featured Image: Amit Patel: Mango Shake Orange Sweet found in WordPress.org/photos

\n\n\n\n
\n\n\n\n

Don’t want to miss the next Weekend Edition?

\n\n\n\n

We hate spam, too and won’t give your email address to anyone except Mailchimp to send out our Weekend Edition

Thanks for subscribing.
\n\n\n\n
\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 14 Jan 2023 22:30:28 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Birgit Pauli-Haack\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:27;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: WooCommerce 7.3 Introduces New Products Block in Beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141097\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"https://wptavern.com/woocommerce-7-3-introduces-new-products-block-in-beta\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2242:\"

WooCommerce 7.3 was released this week with the new Products block now in beta. In December 2022, the Products block went into testing in WooCommerce Blocks version 9.1.0. It’s based on the Query Loop block and is intended to replace all of WooCommerce’s current product-displaying blocks.

\n\n\n\n

This first beta version of the Products block allows users to list products based on specific criteria and their layout in the list or grid.

\n\n\n\n\n\n\n\n

Version 7.3 also introduces three “commerce-adjacent” patterns for building WooCommerce store pages. These are patterns that do not tap into WooCommerce store data but allow store owners to customize the images and the links. These patterns were also tested in WooCommerce Blocks 9.1.0. They include an alternating image and text block pattern, a product hero with two columns and two rows, and a “Just Arrived” full hero pattern.

\n\n\n\nimage source: WooCommerce 7.3 release post\n\n\n\n

This release also brings store owners a new multichannel marketing experience in beta. Under the Marketing menu in the admin, users can now view a list of recommended marketing extensions without leaving the dashboard. These can be installed directly from the Marketing page.

\n\n\n\n\n\n\n\n

Other notable features in WooCommerce 7.3 include Pinterest and Codisto extensions added to the onboarding wizard, a new warning banner when the tax settings have a conflict, and an improved UI for creating product attributes and uploading product images.

\n\n\n\n

Check out the release post to see the template changes and all the new actions and filters available for developers. The full 7.3 changelog is available on GitHub.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 14 Jan 2023 04:25:34 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:28;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:64:\"WPTavern: Lettre Newsletter Theme Now Available on WordPress.org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141076\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:75:\"https://wptavern.com/lettre-newsletter-theme-now-available-on-wordpress-org\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:2595:\"

Automattic has published its Lettre theme to WordPress.org. The company launched its newsletter product at the end of December 2022 using Lettre as the default theme. The self-hosted version of this block theme is for those who want to publish a newsletter using Jetpack.

\n\n\n\n\n\n\n\n

The theme puts the focus on the subscription form, which is the most important thing a newsletter landing page can do – make it easy for people to sign up. Beneath the form there is a link to read all the posts, followed by another subscription form. All of these elements in the home page design are blocks, making it easy for them to be removed or rearranged.

\n\n\n\n

Lettre comes with 15 block patterns for building different pages and designs, including about the author(s), a bold color signup, a two-column signup, various designs for the newsletter intro with light and dark background images, newsletter signup with media on the left, newsletter signup with logos for the background, a list of posts, an in-post article promo, three columns of text, and more.

\n\n\n\n\n\n\n\n

A live demo of the theme is available on WordPress.com. The menu items on the demo give a few examples of the different signup patterns in action.

\n\n\n\n

Lettre is designed to be used with Jetpack’s Subscription block, which uses WordPress.com’s infrastructure to manage emails and subscribers. If you like the design but are already using another newsletter service, the Jetpack Subscribe block can be replaced with any other block, including the shortcode block for newsletter services that haven’t yet made their subscription forms available via a block. Be advised, you may need to write some custom CSS to ensure that the subscribe form matches the original design.

\n\n\n\n

Lettre is one of the only themes in the WordPress Themes Directory that was made to be a newsletter landing page and certainly the only block theme dedicated to this purpose. Combined with Jetpack’s subscription feature, this is one of the most seamless ways to distribute a newsletter without all the extra steps of copying the content into a newsletter service’s editor. Lettre is available for free download from WordPress.org. I wouldn’t be surprised to see more themes like this pop up now that WordPress.com has launched its newsletter service.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 14 Jan 2023 02:50:40 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:29;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"Do The Woo Community: Taking a Deep Dive Into the Current State of Social Media with David Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74300\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:50:\"https://dothewoo.io/current-state-of-social-media/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:422:\"

David Bisset and I share our current experiences with Twitter, Mastodon, Linked, Tumblr, the Fediverse and open source.

\n

>> The post Taking a Deep Dive Into the Current State of Social Media with David Bisset appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 13 Jan 2023 10:58:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:30;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:77:\"WPTavern: New Video Explores Site Building Progress from WordPress 5.9 to 6.2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=141039\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:88:\"https://wptavern.com/new-video-explores-site-building-progress-from-wordpress-5-9-to-6-2\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3346:\"

WordPress 5.9 “Josephine” was released in January 2022, but that seems like ages ago when you compare the advances made in site building over the past year. Anne McCarthy, an Automattic-sponsored contributor who heads up the Full Site Editing Outreach Program, has published a short video that tours the important changes in WordPress over the past few major releases. The video also doubles as a preview of some of the features coming in 6.2.

\n\n\n\n
\n\n
\n\n\n\n

If you are using the Gutenberg plugin and have been tracking the relentless progress of the Site Editor, you will notice how limited the design options are in 5.9 and how much more consistent and expansive they are today. In 5.9 users users can only add a Front page template, and the site building interface is disjointed and less polished.

\n\n\n\n

McCarthy demonstrates how WordPress 6.2 will introduce smoother interactions with browse mode. It will also greatly expand the template options available for users to add and includes a new colorized list view.

\n\n\n\n

The Navigation block has had a long, rocky journey but seems to be reborn in 6.2. McCarthy showed how much more intuitive it has become with the new experience of editing navigation in the sidebar, and repositioning via drag and drop with live previews.

\n\n\n\n\n\n\n\n

The instant that Style Variations were introduced in WordPress 6.0, it seemed they were always with us. Looking back at 5.9 in the video, the Site Editor appears so bare without them. WordPress 6.2 will extend this even further with improved block style previews, a style book, and a new zoomed out view that makes it easy to see changes at a glance.

\n\n\n\n

Everything coming in 6.2 is converging towards better usability and more design options for site editors. The challenge here is to continue introducing new features without the interface becoming cluttered and chaotic. Many of these features are still being ironed out. For example, McCarthy mentioned that the Edit button is still a work in progress and may soon be relocated to be more prominent in the Site Editor.

\n\n\n\n

This video gives a quick visual summary of what is being done to wrap up the full-site editing phase of the Gutenberg project before contributors move on to Collaboration. It is worth a watch to see the site building progress that contributors have made in just one year.

\n\n\n\n

If you want to get involved in making sure all these features in 6.2 are ready for prime time, check out McCarthy’s latest FSE Testing Call: Find Your Style. It will plunge you into the new features of the Site Editor to perform a few tasks. It’s essentially a guided opportunity to explore the new interface while contributing back to WordPress, and you will earn a fancy testing contributor badge that will display on your WordPress.org profile.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Fri, 13 Jan 2023 03:56:21 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:31;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:96:\"Post Status: On OpenAI And WordPress With Jannis Thuemmig Of WP-Webooks— Post Status Draft 136\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146297\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"https://poststatus.com/on-openai-and-wordpress-with-jannis-thuemmig-of-wp-webooks-post-status-draft-136/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:43263:\"

Jannis Thuemmig, founder of WP Webhooks, joins Cory Miller to discuss Open AI and WordPress. Jannis is passionate about utilizing the power of technology to increase efficiency. WP Webhooks is exploring the ways Open AI can be used to revolutionize website processes and management. It seems we are only at the tip of the iceberg for what is possible when working with WordPress and Open AI.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

In this episode, Jannis Thuemmig, serial entrepreneur and founder of WP Webhooks, dives into the world of automation and Open AI with Cory Miller. Together they look at what is currently possible within the world of integration and automation within WordPress. Then they lean into what is unfolding as Open AI finds its way into the mainstream and discuss what this might mean for the WordPress community.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • Integrations & Automations to Save Time: Everyone is in need of some kind of automation. Our main goal is to save time by creating automations wherever there are pain points. Rather than doing things manually, WP Webhooks enables you to automate them within your dashboard.
  • \n\n\n\n
  • Avoiding Automation through Software: Using software as a service partner means hosting your data on their platforms. Using Webhooks for integration and automation allows you to keep things on your server and within your complete control.
  • \n\n\n\n
  • Possibilities with Open AI Integration: Webhooks is focused on using Open AI as an advantage to speed up processes by creating integrations between services and generating original content. They are working on finding cool use cases and understanding the actual power of what it makes possible.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n
\n

\"🙏\" Sponsor: WordPress VIP

\n\n\n\n

Founded in 2006, WordPress VIP is the agile content platform that empowers marketers to build content both faster and smarter so they can drive more growth. We empower content and development teams with the flexibility and ubiquity of WordPress—the agile CMS that powers more than 40% of the web—while ensuring the security and reliability organizations need to operate at scale

\n
\n\n\n\n
\n
\"WordPressWordPress VIP
\n
\n
\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

GMT20230105-161248_Recording

\n\n\n\n

GMT20230105-161248_Recording

\n\n\n\n

Cory Miller: [00:00:00] Hey everybody. Welcome back to Post Status Draft. This is another interview and discussion in our Product People series, and I\'ve got someone I\'ve met, let\'s see, last year or the year before Giannis and doing good work, but we were talking about AI and that led to OpenAI and something they\'re doing with WP Webhooks.

\n\n\n\n

So that\'s what the conversation is gonna be about today. But Giannis, welcome to the draft podcast. Would you tell us a little bit about yourself and your work and WordPress?

\n\n\n\n

Jannis Thuemmig: Sure, totally. Thanks for having me here. Uh, it\'s always an honor. Uh, my name is Giannis. I\'m from Germany originally, but started traveling a long time ago and since then, I basically work as a digital dumper from anywhere.

\n\n\n\n

And I would say with a, with a very, very deep focus on web. And specifically in automation. This is where W P Airport comes from. So we are basically focused on connecting different services and WebPress plugging to let them talk to each other and kind of just automate the [00:01:00] system and get rid of the human error and save everyone a little bit of time and money, which is really interesting nowadays.

\n\n\n\n

Cory Miller: Yeah, I, and I love it. Uh, one part I\'ll just sidebar real quick is I know when you say digital notepad, uh, the several times we\'ve had zooms, I\'m like, where are you in the world today, y\'all? It\'s like, . Um, so I, I love that. I love to see the nude, like landscapes you\'re in every time we talk. Um, okay. So WP Webhooks, um, I know you\'ve been, so automation is key.

\n\n\n\n

It\'s about efficiency, um, like really saving that time. In the processes you\'re doing, um, what, tell me what all does WP Webhooks do?

\n\n\n\n

Jannis Thuemmig: So basically it allows you to use a set of redefine integrations to let other services and WebPress plugin specifically talk to each other. So let\'s say there\'s, um, a woo commerce shop, for example, and you have a, a custom programmed plugin that has no integrations [00:02:00] whatsoever.

\n\n\n\n

You can use our plugin as a middleman to allow sending data in between, and that works with mostly any kind of WebPress plugins as well as external data like, uh, external services, something like Zapier or make or integrated. So the, the basic main goal is to just make them compatible, which they, in a lot of cases, aren\'t from the beginning. Or if they are, they\'re often very limited, which is something we realized as well. So we just want to kind of get that interoperability to WordPress, which is something that was just lacking over the last couple of years.

\n\n\n\n

Cory Miller: Yeah, I, I love that. I know Zapier has used quite a bit uh, obviously we\'ve used it in the past at, at Post Status because of all the external services, and you\'re trying to link these and do some things that certain pieces of software doesn\'t do out of the bat.

\n\n\n\n

So I, I love the premise of web hooks for sure. Uh, WP Web Hooks, what are you seeing or finding? Customers are gravitating to webhooks [00:03:00] for, like is there specific tasks that stand out that people are using these over and over and over and going, this is what I need. I don\'t want to pay for Zapier or some other alternative, I wanna do something here with my WordPress site.

\n\n\n\n

What are you seeing from your customers?

\n\n\n\n

Jannis Thuemmig: So I\'d say that\'s not a specific use case. There\'s, uh, quite a lot. So everyone, literally, everyone who\'s in the, in the, has a web presence or has an online shop or something related and does something with website. Everyone is in in need of doing some kind of automations.

\n\n\n\n

Let it be to automatically book orders into your accounting system or synchronize your properties from a property management website with your WebPress website. Or let\'s say you have a Teachable account and you sell online courses and you want to synchronize your students with a WebPress website to give them extra features.

\n\n\n\n

This is stuff that they are using it for. So basically wherever there\'s a pain point and there\'s some time that just can be avoided by automating it through software. This is something where we are, um, jumping in [00:04:00] and it\'s specifically interesting right now for people that are very critical about privacy because especially in Europe, a lot of people don\'t want to use software as a service partners like Zapier or Integra.

\n\n\n\n

Because they are hosting their data on other platforms, right? So they have no full control over it, which comes very handy with our plugin because you have your own server, so everything runs on your own server. You are in full control where your data is, what your data does. And this is a very, very critical point that is, uh, always, always well seen at the moment.

\n\n\n\n

Yeah.

\n\n\n\n

Cory Miller: Yeah, yeah, for sure. I, you went to a subject I didn\'t even think about, which is if you don\'t want your information out there on another service, having it in in WordPress, something you control. I think that\'s a key facet. Before we start talking about, uh, AI and specifically OpenAI, what are you most excited about with webhooks this year?

\n\n\n\n

Jannis Thuemmig: Ooh, for sure. Bringing that AI space model [00:05:00] web. Because we had so much fun over the last couple of months trying these things out, seeing in which direction it goes. And it\'s just incredibly fun to, to play around with it because the possibilities are really endless. And we are, we are fully about saving time.

\n\n\n\n

Right? And this is something we can even use to leverage more time out of our daily task, which is really, really good. Okay.

\n\n\n\n

Cory Miller: Well let\'s roll into that because I think that\'s one of the most, uh, uh, Interesting themes in our community is ai. I\'ve seen a couple tweets saying AI is gonna revolutionize, um, a lot of stuff with a website by the end of 2023.

\n\n\n\n

I can\'t remember who said that online. And I was like, well, I\'ve been paying attention enough. But talk to me about ai, OpenAI and what you, you see on the horizon for, um, for WordPress particularly, and opportunities and possibilities. . Yeah,

\n\n\n\n

Jannis Thuemmig: so ai uh, specifically in our case with OpenAI, uh, there\'s, uh, a little differentiation.

\n\n\n\n

So [00:06:00] right now it\'s very much hyped, the G P T three. So the, the kind of chat ai as you can, as you can, uh, think of, which is basically you just type in something and it, it gives you like a very human answer back, which is really, really incredible. And, uh, we specifically talk about the, the OpenAI api, which kind of allows you to.

\n\n\n\n

Communicate data on a programmatic level, which means you basically don\'t even need to type something yourself, but you can already use a service to let these things run through the web automatically without ever touching this kind of data. And this is, this is just something that that works very well with, with the automation part.

\n\n\n\n

Right. So we are, we are basically looking into bringing more possibilities that AI through non-static data, and, uh, what I mean by non static data, it\'s probably interesting to, to understand what an AI actually is. So it\'s a pre-trained network, right? It has the data that was feeded to it at some point. And with OpenAI, it\'s made from [00:07:00] mostly 2021.

\n\n\n\n

So it has no actual new data. If you ask it something like what happened yesterday, for example, it could probably not give you an answer to, you could give it the information if you have it yourself. But it can never give you like the, the news and accurate information. And using things like automation, you can basically bring a whole new word to it because you can kind of give the AI the possibility through response and, uh, requests to send data through automation, uh, validate it somewhere else and send it back to the AI and tell the ai, Hey, look, there\'s new information.

\n\n\n\n

We can use that, uh, or, or learn about that and, um, send me some more information or summarize me something. So it\'s, it\'s just a very interesting time in, in regards to giving the AI actual information that you can feed it, uh, that is currently not within its its own, um, possibilities.

\n\n\n\n

Cory Miller: So you said something there.

\n\n\n\n

Um, I, I haven\'t even gotten that in depth with OpenAI, but So in [00:08:00] 2021 they fed it a ton of data you\'re saying, and then trained it to be able to, to answer questions and things.

\n\n\n\n

Jannis Thuemmig: Yeah, exactly. So basically they had a, a huge dataset or couple of datasets for sure about the information that they fitted. And the AI can basically make.

\n\n\n\n

An answer that is, uh, in a human real reform, and that seems like it is made from a human, but the data that was fed is all from 2021, right? So it is a static data if we, if we want to hear it or not. So if you\'re gonna ask the ai, what is the latest model of iPhone, for example, it\'ll probably tell you something like it\'s the iPhone 13, because I don\'t think it has information about iPhone 14.

\n\n\n\n

That would be something cool to try, but I guess it must be, uh, outdated information. And with that gap of, of using that, that automation in, in connection with ai, you can kind of close that gap and you can actually feed it real time data and use that data to, to do certain things within the AI [00:09:00] and, uh,

\n\n\n\n

Cory Miller: I see, thats a new one.

\n\n\n\n

Yeah, it does. Um, totally to me, and I\'m asking as a newbie to all of this, um, because I\'ve used it and I\'m like, this is pretty dang fast. And I\'m like, how the heck are they doing that? That makes total sense. And then from the training side, um, the model itself is, I was like, gosh, if this had access to that, and you could just ask it questions like that.

\n\n\n\n

It\'s the, it\'s the a hundred times better Google. Yeah, because, yeah, it, like, I was, I, I mean I asked what are the strengths and weaknesses of WordPress, for instance, and it came back. Um, but knowing it\'s, it\'s a little bit lag on the data side is interesting to me. Um, but I saw the potential for this to truly.

\n\n\n\n

Revolutionized some things on the web. Um, so it\'s, it, it\'s been really intriguing and I mean, I asked it all kinds of questions that I was just actually curious about and seeing what, not just from the what, [00:10:00] how the model would work, but the answer. And I was like, this is like a perfectly uh, formatted.

\n\n\n\n

Informative, um, short essay that I would\'ve gotten in college, you know, so that\'s

\n\n\n\n

Jannis Thuemmig: intriguing. It\'s actually you can, you can write like on demand stories for your kids based on your own characters. Just type in a sentence, say you run a short story and it spits you out a short story that you can read them from going to bed.

\n\n\n\n

It\'s amazing. It\'s just like incredible.

\n\n\n\n

Cory Miller: I\'m gonna have to try that today. I, I continue. This subject fascinates me and I think it\'s something we need to be thinking about and looking at and talking about in WordPress and Post Status, because this new technology coming and then how WordPress is placed in this.

\n\n\n\n

And for years, I think this is a segway to talk about OpenAI and WordPress specifically. But you know, I\'ve either built sites for people or known a bunch of people that build sites for clients. And you turn on this awesome, it\'s like you turn this car over, you pull this car up to \'em, and [00:11:00] you\'re like, here\'s your car.

\n\n\n\n

But you gotta drive it with content, with things inside the site, and it\'s such a great vehicle for that. But oftentimes people get hung up on that part of. Oh, I don\'t know what to, I don\'t know how to drive my car. Right? Like these, you know, WordPress sites with the right architecture, the right things can really drive and make a dent.

\n\n\n\n

That\'s our kind of thing with WordPress is like it\'s magic like that. But you still have to like, Drive it, you have to put gas in it and drive it, uh, with content. So that\'s a compelling angle for me with OpenAI. And I\'ve heard about all these things. Before we segue specifically to the integration you\'ve done too and some possibilities there, what, where do you see all of this and WordPress going?

\n\n\n\n

Jannis Thuemmig: Like, that\'s a very interesting question. Yeah, yeah. Uh, I think, I think it will be in relay, I mean, it\'s, right now we are specifically in the content age, right? So I, I\'ve seen a lot of people. [00:12:00] Going into the space where they try to create on demand articles using an ai, which is probably a terrible idea just through plagiarism because it\'s very easily detected if you don\'t lose like a rewriter and you use your very own wordings in between.

\n\n\n\n

So this is something that we will see switching, definitely. But what I see as an advantage in the future with WebPress is that people will use to. We, we learn to use AI for the advantage in the sense of speeding up their process. So it\'s also kind of a, a way of automating things, uh, in the sense that they don\'t need to write their content anymore from scratch, or they don\'t need to write a, think about copywriting that much.

\n\n\n\n

They just ask the ai, it\'s bit something out. They put it in, maybe adjust it, tweak it in their own way so that it has their very own style. And they probably just make the, the way of, of riding blocks 10 times a hundred times faster than it\'s right now. So we\'ll definitely see like a, a boost in performance and [00:13:00] probably block block posts over the long term.

\n\n\n\n

Cory Miller: Okay. Well, so that leads into this specific integration you have and the tutorial I, I was looking through before we started. Um, so you saw OpenAI has an api and tell us, tell us about that in WP Web Web Hook.

\n\n\n\n

Jannis Thuemmig: So, yeah, we, we basically started, um, after trying a couple of times how OpenAI works to, um, to integrate it with our plugin.

\n\n\n\n

So we, we usually go for creating integrations for different services and plugins, and, uh, in that case it\'s, it\'s once separately for OpenAI, which makes it compatible with all of the other services and, uh, plugins. We are integrated. And the main goal was just to provide the integration, right? Because it\'s so new, barely anyone understands the actual power of it and what is possible.

\n\n\n\n

So we, we just kind of created it out of the blue with a thought of, Hey, it would be cool to just have it, you know, let\'s see what, what\'s going to happen. And right now we are basically just [00:14:00] working on finding cool use cases. And, uh, there are definitely a couple, uh, like I\'ve, I\'ve, uh, showed you earlier.

\n\n\n\n

We already have a blog post on our. That basically describes how you can auto generate method descriptions using OpenAI and Yost seo. So you basically just feed it in the title and it spits your order, perfectly made method description that you can just use or adjust as you want. And these kind of things, they just now come through trial and error basically.

\n\n\n\n

And, uh, it\'s, it\'s very interesting to see where it goes. And I can see that with these kind of automat. Um, we can also provide what I mentioned earlier, that that possibility of feeding the AI new information that is not available within the AI itself. So because we can make these kind of workflows, um, if that makes sense.

\n\n\n\n

And this is, uh, this is mostly what we are trying to do right now. We basically just working on, on use cases, see what\'s possible, trying out different things and it\'s a super, super exciting. [00:15:00]

\n\n\n\n

Cory Miller: Yeah, absolutely. Because I mean, you talk about this car, you some a a site builder turns the car over and they start to use it.

\n\n\n\n

But that meta, uh, description is one thing. Like I honestly confess, I never do, you know, but it\'s, it\'s helpful, it\'s vital. And so like that one little use case in the bigger picture of what I can do, I think starts to step us into this and it is really interesting. Um,

\n\n\n\n

Jannis Thuemmig: oh, totally. Yeah. This is, this is literally just the, the tip of the iceberg.

\n\n\n\n

If you want, you can basically let the AI create a, a full schema, uh, like a shima for your, for your website. So whenever there\'s a blog post, it can write the how-tos and everything in, in kind of adjacent format and, uh, spits your order perfectly well formatted SEO description and, and everything keyboards, the, the, the whole how to, and this is just, it\'s just such a time saver.

\n\n\n\n

It\'s incredible.

\n\n\n\n

Cory Miller: Well, okay. Do you have a tutorial on that [00:16:00] too? Because that\'s really interesting. Um, , or if you don\'t, we need one. Um, but so you\'re going into OpenAI or chat GPT or whatever, and then you\'re saying you\'re asking a question or something like that, and then it\'s gonna give you back those things.

\n\n\n\n

Jannis Thuemmig: Yeah, exactly. It\'s just you literally ask it just a humanly written question, something like, give me back adjacent with each of these information. And it spits you out adjacent with each of the information. And Jason, you can always use on a technical level, right? So we can just leverage that out and use it through our plugin to use it in different automations and do different things.

\n\n\n\n

Cory Miller: Oh, that\'s super cool. Well, what do you have anything you wanna share about what you\'re doing next with this WP WebHooks?

\n\n\n\n

Jannis Thuemmig: Um. As a, as a use case, it\'s a, I mean, we, we definitely, for, for now we really try to just work on the OpenAI things mm-hmm. and try to find some cool use cases there. Uh, we had a lot of, um, a lot of actually customers reaching out about the possibilities as well and how exactly it works because the models [00:17:00] and the configuration is a bit complicated if you, if you\'re not fully aware of it.

\n\n\n\n

But, uh, yeah, we, we just follow the standards and, uh, things should be fairly easy. But yeah, for us, it, it\'s mostly, mostly OpenAI and creating new integrations. That\'s something we\'re, we are hardly focused on at the moment.

\n\n\n\n

Cory Miller: I, I really think this is, like you said, the tip of the iceberg that, um, I\'m really intrigued by our WordPress community post at Post Status to go, okay, here\'s this cool technology.

\n\n\n\n

How do we translate this into practical? Um, uses for the end client, the end user in WordPress. Um, so that, that\'s, that\'s interesting. We\'ll be excited to hear what, what you find in explorer and launch launch next.

\n\n\n\n

Jannis Thuemmig: Yeah, you should just see the block post, uh, our, our, our block. There will be a couple of more tutorials coming.

\n\n\n\n

They\'re already in the making, so in the next days we should see someone there.

\n\n\n\n

Cory Miller: Okay. Perfect. All right. Um, okay. So. You, you [00:18:00] showed me something as like this. I think this is just showing the power of what it could do when we start to get these types of integrations into WordPress. Do you mind showing me the one you were telling me about?

\n\n\n\n

Jannis Thuemmig: Totally. Yeah. Not a problem. Of course. I\'m just gonna share my screen, probably this one. Yes, so I, I was basically just fooling around the other day on. With our integration, trying to find some new cool ways we can use to, to present the OpenAI integration. And, you know, like, like I mentioned earlier, you can kind of ask the AI to create adjacent format, um, with specific data.

\n\n\n\n

So Jason is basically just a structured way of presenting data within the web that is something that the, the browser or the, the server can. And in our case, we, we, we wanted to have like, like in here, uh, just a simple field that you can write something and based on whatever you write, it updates the block post of [00:19:00] your choice.

\n\n\n\n

So in our case, we just created a quick contact form seven form as we have an integration with contact as well. And we connected that with open. To create a so-called Jason and update a block post based on a specific information. So I can just demonstrate it here. You can see I have three block posts available and let\'s just take this one.

\n\n\n\n

I just need the ID because that\'s the way we wrote it. So we have ID 97, and what I would like to do is, let\'s say I want to update the, the title of this post, right? So I can, I can write something like, Update the post with the id, let\'s say post title

\n\n\n\n

with ID 97 and change it to, um, this is a new title based on OpenAI. So it\'s, it\'s basically what we read as a, as a human tech, [00:20:00] right. But if I submit that and I let our workflow. The AI basically interprets that and, uh, changes it based on our parameters within, uh, Jason. And when I refresh here and, um, the flow ran, it should display it.

\n\n\n\n

See if it doesn\'t, no, it doesn\'t. Uh, so the thing is, because it depends what you feed the ai. So the AI basically needs to understand what you do. And, uh, in some, in some cases, that\'s, that\'s the problem with ai. It fits you out text, right? So you try to, you need to, to format. And kind of use the text in a different way so we can see.

\n\n\n\n

Okay. Just didn\'t follow it. Just what I\'m gonna do is, so to, to just, so the very same example, I just tried to type the similar thing again. Let\'s try it again. So, um, update the post with the ID 87 and change the title to, [00:21:00] um, OpenAI. Something new. Let\'s see now

\n\n\n\n

Cory Miller: I always love live demos, . Yeah, I know. When you were showing me before I was like, wow, that\'s super cool.

\n\n\n\n

Jannis Thuemmig: Yeah, it really depends on the AI, if I, if I do it right or not. Um, but it seems like you see that it\'s not completed. So basically something stopped within the AI and uh, yeah, I would need to, I would need to see what.

\n\n\n\n

Cory Miller: Yeah, so you were showing, you were showing just now the webhooks, uh, pro dashboard. Do you mind taking us for a spin around the Webhooks Pro dashboard?

\n\n\n\n

Jannis Thuemmig: Uh, yeah, totally. So it\'s basically like, you know, standard WebPress plugin and stuff. On the site menu we have, uh, our W2 Web Hooks Pro, um, menu item, and basically it\'s, it\'s separated into two main things, which is the automations, the flows, and the web. So there\'s, there\'s kind of a difference in between, because originally we came from the web website, which means it\'s kind of like a [00:22:00] one-way street for information to present.

\n\n\n\n

Let\'s say you, you update a, a post on your WebPress website and based on that post you can send data into a, a certain direction, like directly and instantly to inform another service about that there\'s a new post. But then we realized that there\'s more of a need to actually automate kind of certain work.

\n\n\n\n

And then we created something called Flows, which basically allows you to connect the, or create a consecutive order of triggers and actions. So web book triggers and actions to do certain things in a, in a specific flow as we call it. So I just head into it, uh, into one, which is the human posture. This was the example I tried to show you, which, uh, was currently not working out because of something that I need to see. Um, but what we have basically, within the floor. You can see we have a trigger, right? The trigger fires on a contact form seven. Within the settings, we basically selected the form that we created earlier, which is [00:23:00] embedded in, in the site.

\n\n\n\n

And we don\'t want to send the email as we just want to send the data to OpenAI. And it was tested. We set up some conditionals, um, stuff that\'s not really important for now, but, uh, this is, this is basically what causes the actual workflow to fire, right? So, This specific trigger comes along with all the data that was sent within this form, and we then reuse the data in the other kind of actions here.

\n\n\n\n

And as you can see, the first action is something, uh, is our OpenAI integration, where we basically sent that information that we had earlier, as you can see here, to OpenAI as a, as a text. And this is, this is what we read. So it basically says, get the posterity and the PostIt from the JSON, uh, in the JSON format.

\n\n\n\n

This is the sentence, and the sentence basically is a dynamic string that comes from the input that we sent within this form. So it makes more sense if, if we go through it logically while, while building it. But, um, [00:24:00] when you click into a field like this, you will see it shows a dropdown, and inside of the dropdown you will see all of the information that was sent within the trigger, including the question like, change the title of the post idea, ???

\n\n\n\n

So this is basically what we selected here. And this is kind of more details about the OpenAI stuff. Sure. And yeah, when you, when you continue safe, you can test the action directly within here. That\'s something I can try, um, just as an example to see what comes back. So basically right now I\'m sending a real request with the data that we got earlier.

\n\n\n\n

And this is basically the response. So we can see, we got some text back from the AI, which looks a bit weird as it\'s text. But within our plugin we have something like a formatter, which allows us to format the data and change into something readable. So I\'m just gonna quickly do that to, to give you a better example of what we get back.

\n\n\n\n

So as you can see, this is what we get back from the AI or from the formatter, which came originally from [00:25:00] the OpenAI. And this specific information we want to then use in another action to actually update the post. And this is, this is literally everything it does. You can just think it of simple steps that, like we have a trigger.

\n\n\n\n

The trigger causes, uh, runs whenever the, the specific contact form was sent, then we sent the data to OpenAI. We format it in a certain way, and then we update the post based on whatever data we got back from the OpenAI. Excellent. So, yeah, exactly. This is, this is basically it for that.

\n\n\n\n

Cory Miller: What, what are some of the automations.

\n\n\n\n

Yeah, I, I saw the create the automation. So what are the, some of the things that webhooks can do from the automation side?

\n\n\n\n

Jannis Thuemmig: Uh, you mean some examples for example? Ah-huh. Yeah. Yeah, yeah. Like I say, you can, you can, for example, connect the different services together. Let\'s, for example, say you have newcomer, right?

\n\n\n\n

So you can go to the, to the integration [00:26:00] screen. You can install any kinds of integrations that you, you are working. So we have around hundred right now. And let\'s, for example, say you have commerce installed, right? So you can then install commerce once it\'s, once it\'s available on your website and within one of those automation workflows, you can then say, whenever commerce fires, then send the data using, uh, a WebBook, for example, to mm-hmm.

\n\n\n\n

your bookkeeping system. Or send, send an email using the WordPress integration. So in here I can show you. Click send email, and then you have the possibility to send an email directly from your WebPress to the customer whenever, whenever, uh, an order was created. So it basically, it basically just allows you to do certain things that you would manually do within your dashboard.

\n\n\n\n

Mm-hmm. ? Yes. In an automated way

\n\n\n\n

Cory Miller: There\'s a bunch of those things for the Post Status setup out the way. I\'m like, oh gosh. Yeah.

\n\n\n\n

Jannis Thuemmig: I can\'t imagine. Same here.

\n\n\n\n

Cory Miller: And, and what are, what are workflows to, uh, or I\'m sorry, it\'s [00:27:00] webhooks. Oh, I thought that\'s a workflow somewhere. I read that wrong. Okay. Yes. So what

\n\n\n\n

Jannis Thuemmig: I can show you, it\'s, it\'s basically separated in two parts.

\n\n\n\n

It\'s sent data and received data. What it basically means is these are kind of the triggers available, right? So whenever a user created, or when a user was deleted or when a form was submitted, you can send data to a specific url. Let\'s say, for example, I want to send a URL to my website, um, I cannot call it demo, and I, I add my api endpoint here and I edit, and then you can see it here. Which basically means when ev, every time a user gets created, you can send a direct webhook request to this url and you can test it, you can customize it with, with more features, more setups, um, based on your needs. Gotcha. And this is, this is what I mean earlier, like a, a direct connection.

\n\n\n\n

And the receive data is basically the exact opposite. So instead of sending data out on a specific event, you send data in and to do something specific. So you can, for example, activate a plugin. As you can see, you can call a PHP [00:28:00] function, you can create a comment, a post a user. So we have basically mostly all of the options of WebPress available through, uh, web as well.

\n\n\n\n

Excellent.

\n\n\n\n

Cory Miller: You don\'t have a Slack integration, do you by chance ?

\n\n\n\n

Jannis Thuemmig: Um, that\'s the, that\'s the thing. Depends what Slack has as an api. Um, if they truly have an API and if they have an api and it can be integrated with something like an API key or a hero token, it can also be used with our plugin. Um, and that\'s an interesting point.

\n\n\n\n

That\'s good that you mentioned that we have something like,

\n\n\n\n

Cory Miller: um, it\'s a, it\'s a. Post Status specific thing, but I think a lot of membership sites, which is a big trend too. People building membership sites, course sites, you know, a lot of people like us obviously use Slack. Being able to, um, one, this is a nuance and I\'m, uh, sorry for sharing, but this is like create a private group or something like that.

\n\n\n\n

I\'ve looked in some of the Slack API and. I\'m using us as a [00:29:00] test for a second to say it is a broader thing. I think a lot of membership sites, they\'re using Circle, for instance, maybe they want to use something else. So I, I stopped you though. Keep going.

\n\n\n\n

Jannis Thuemmig: Oh, no problem. No problem. Um, yeah, but what I, what I mentioned earlier is that, like you say, with, with, uh, slack, we can kind of integrate with any service that allows, like simple API calls or web and uh, we have an integration available that is called Web itself.

\n\n\n\n

So, When you install one like that, for example, and you go to, let\'s say an automation workflow, I can, I can come within here, add a new action, and you see I have a WebBook endpoint available, which basically allows me to send data or send a request to a specific site. So if you have Slack, you would, you would uh, add your Slack U URL here, for example, right?

\n\n\n\n

Slack API or something, and then you. Select the method you want to use to send data, and you can send the data and add it here along. [00:30:00] So if there\'s a, a service out there that just follows the standard rep hook or api, um, standards, you can integrate them as well with our plugin. So there\'s not directly, uh, integration necessary. Basically.

\n\n\n\n

Cory Miller: Excellent. Well, one thing that\'s intriguing about this is for as long as I\'ve been in WordPress, I, it, it has led the way in truly democratizing publishing, but over the years, you see Facebook, Twitter, what name, whatever default. Closed wall type garden come out. And um, I just did an interview with Mattias who does activity plug plugin for the Fed averse.

\n\n\n\n

And I was like, the, you, you think about what you\'re doing here with webhooks and then the Fed averse is kind of bringing that power back to the. To the user and saying, okay, fed averse can help. To me, I just see the potential to go, let\'s, let\'s decentralize some of the social [00:31:00] networks. So when a billionaire buys the next thing, or they change their policy at one of these closed set social networks, you\'re, you know, all these people are affected by it.

\n\n\n\n

And, and taking some of that control. So that\'s where I see FE averse. And then I go, what\'s the power here is. Ground zero for what you\'re doing is your WordPress site, and with things like tools like this, then you can start, I don\'t know, it\'s just helping bridge that gap of power. There\'s so much usability and features that these closed gardens have, but tools like webhooks and potential with the Fed averse is like bringing some of that power back, and I see WordPress truly being in the space to lead and innovate and bring that power back to the users.

\n\n\n\n

Jannis Thuemmig: Totally. Yeah, I fully agree with you. The, the, an interesting point about that is actually that using our plugin, for example, you can use it kind of as a standalone on WebPress. So if you say you want to make automations, you don\'t necessarily need to use WebPress, but [00:32:00] you can just set up a WebPress environment and install our plugin and you can.

\n\n\n\n

Automate external services through WebPress. Right? So you can use it kind of as a middleman for yourself without actually using WebPress.

\n\n\n\n

Cory Miller: And you still maintain control

\n\n\n\n

Jannis Thuemmig: in a lot of ways you have full control. Yes.

\n\n\n\n

Cory Miller: Even if it\'s not a public facing site where you have content on like using that, that\'s the power, that\'s the other side of WordPress.

\n\n\n\n

Do this has been become this huge power powerhouse of a, you know, a software. I talked to a lot of people on the enterprise and they mentioned. the connections. There\'s a, um, my friend Kareem at Crowd favorite talks about WordPress being the open source hub to connect services. So, like your example there.

\n\n\n\n

I, I resonate with it cause I just talked to Kareem a couple weeks ago. I love that example. Yeah. Yep. Well, Gianni, anything else you wanna share, um, that you all have going on or you\'re excited about or anything I forgot to.

\n\n\n\n

Jannis Thuemmig: Uh, yeah, I\'m excited to make this tutorial work, so I think the next blockbuster [00:33:00] will see is probably about this example.

\n\n\n\n

Okay. .

\n\n\n\n

Cory Miller: I love it. Just to have, I love it. That\'s the beauty of being a part of this community as I get to ask cool, smart people that can do these things and see, see how they go. But I, I\'ll be playing around with open api. OpenAI\'s, API\'s, mouth, um, just cuz I was playing with that and like, wow, this is powerful and I love this kinda stuff and I love there\'s people like you experimenting with it, testing it, and giving users, um, that opportunity to do that.

\n\n\n\n

Um, so thanks so much today for being on the Post Status draft podcast. Uh, this is under our product People series. I love our innovators in our community like you, and so thanks for joining me today.

\n\n\n\n

Jannis Thuemmig: My pleasure, really. So it\'s an honor. Thank you very much as well for inviting me.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 14:44:47 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Olivia Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:32;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:85:\"WordCamp Central: WordCamp Entebbe: First Wordcamp to happen in Africa in 2023 is on!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:39:\"https://central.wordcamp.org/?p=3158482\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:108:\"https://central.wordcamp.org/news/2023/01/wordcamp-entebbe-first-wordcamp-to-happen-in-africa-in-2023-is-on/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3126:\"

\n\n\n\n\"\"\n\n\n\n

WordCamp Entebbe 2023 is set to be a major community event for WordPress developers, website designers, online publishers, students, and teachers to come together and share knowledge and experiences, network with other WordPress users, and gain inspiration for their work. Taking place on Friday, March 10th and Saturday, March 11th at the Uganda Wildlife Education Centre (UWEC) in Entebbe City, this WordCamp will be the first to happen in Africa and is poised to be a memorable event for all attendees.

\n\n\n\n

The event will feature a range of activities, including beginner’s training, inspirational talks, showcases, best practices, and the latest trends in WordPress development. In addition, there will be a Women in Tech panel discussion, aimed at inspiring and empowering women-led businesses to thrive in the technology industry. A Teacher’s Workshop will explore the integration of WordPress in the local education curriculum, providing teachers with the tools and resources they need to introduce WordPress to their students for web design projects and assessments.

\n\n\n\n

Attendees will also have the opportunity to take a free tour of the Uganda Wildlife Conservation Education Center, where they can learn about the animals of Uganda and the ecosystems in which they live. The center, which was founded in the 1950s to accommodate confiscated and injured wildlife, has grown considerably in recent years and is considered a premier facility for showcasing wildlife on the African continent.

\n\n\n\n

Accommodation options are available for those traveling to Entebbe for the first time. Attendees can find a list of hotels and guest houses through booking.com https://bit.ly/entebbehotels or by contacting the WordCamp team at entebbe@wordcamp.org for more information and guidance. The full schedule of activities will be published soon, and we look forward to welcoming you to WordCamp Entebbe 2023!

\n\n\n\n

Get Involved

\n\n\n\n

There are several ways to get involved! Check out the details below:

\n\n\n\n\n\n\n\n

Join the discussion via #WordCampEbbs hashtag on Twitter

\n\n\n\n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 11:58:51 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Kasirye Arthur\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:33;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:104:\"Do The Woo Community: Reflecting on the Past and Embracing the Future for WooCommerce with Paul Maiorana\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74261\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://dothewoo.io/2022-2023-woocommerce-paul-maiorana/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:442:\"

It\'s that time of year again when Paul Maiorana, CEO of WooCommerce, joins us for a review of the year and a looking into 2023.

\n

>> The post Reflecting on the Past and Embracing the Future for WooCommerce with Paul Maiorana appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 11:29:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:34;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:17:\"Matt: Thirty-Nine\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:22:\"https://ma.tt/?p=75200\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:34:\"https://ma.tt/2023/01/thirty-nine/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4687:\"

The last year of my thirties! WordPress turns twenty this year. Automattic is now ~2,000 people across 98 countries. There’s so much that has happened in the past decade yet it feels very much like we’re on the cusp of something even more exciting.

\n\n\n\n

This morning started well; I pulled the hammock out of the garage (it had been hiding from the rain) and read for a bit, trying to get my 5-10 minutes of sun in the first 30 minutes like Huberman suggests.

\n\n\n\n

Candidly, the last year was a really challenging one for me personally. There were some beautiful moments, and I consider myself the most lucky in my family, friends, and colleagues, yet among that same group there was a lot of loss, existential health challenges, and that weighed heavily on me. It’s also my last year to get on 40 under 40 lists! \"😂\"

\n\n\n\n

Usually when people ask me what I want for my birthday I don’t have a good answer, but this year I do! As Heather Knight wrote about in the SF Chronicle, the beloved Bay Lights are coming down in March. This has to happen — the vibrations and corrosive environment of the Bay Bridge is taking lights out strand by strand. Fortunately it’s now been a decade since the lights first went up, and there’s much better technology both for the lights and how they’re mounted and attached to the suspension cables. Finally, the lights were not visible from Treasure Island or the East Bay before, but this new version 3.0 will be, which is why the artist behind the lights, Leo Villereal, is calling it Bay Lights 360.

\n\n\n\n\n\n\n\n

Like the Foundation series, we can’t stop the coming period of darkness from happening, but if we raise $11M we can bring the lights back. If we raise it soon we can shorten the time they’re down to just a few months, so I’m working with the 501c3 non-profit Illuminate to help fundraise. The idea is to find ten people or organizations to put one million each, and raise the final million in a broader crowdfunding campaign, to re-light the Bay Bridge and give an incredible gift to the people from every walk of life that see the bridge, and hopefully have their spirits lifted by the art. I’ve heard 25 million people see the Bay Lights every year.

\n\n\n\n

It’s a lot to raise, but every little bit helps so please donate here, and if you are interested to do a larger gift please get in touch. I’m committing a million dollars to the fundraise, and myself, Illuminate director Ben Davis, and the artist Leo Villereal are happy to personally connect with anyone considering a larger donation.

\n\n\n\n

Because of some family health reasons I’m back in lockdown, so going to try and throw an online party tonight in the “Matterverse.” We’re going to party like it’s late 2020. \"🎉\"

\n\n\n\n

All birthday posts: 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Thu, 12 Jan 2023 04:37:53 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Matt\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:35;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:82:\"WPTavern: Automattic Launches Blaze Ad Network for Jetpack and WordPress.com Sites\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140985\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:93:\"https://wptavern.com/automattic-launches-blaze-ad-network-for-jetpack-and-wordpress-com-sites\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:5113:\"

Automattic is bringing Tumblr’s Blaze ad tool to WordPress sites with its launch today on WordPress.com and Jetpack. Blaze made its debut in April 2022, to the delight of Tumblr users who will gladly shell out cash to get people to look at their cat or promote a game they made. It’s an affordable way to attract new followers or just send out something funny into the universe, starting at $5/day.

\n\n\n\n
\n

Note To all small Twitch streamers. Tumblr Blaze is the best advertising tool out there. It has an embedding feature allowing you to embed streams. I went from 20 to 100+ live viewers rn! pic.twitter.com/aolZduCTEe

— Ian Miles Cheong\'s Prime Minister (@MToph91) January 3, 2023
\n
\n\n\n\n

WordPress.com users can now to go to wordpress.com/advertising, select a site, and promote content with Blaze. Jetpack users have access to the ad network inside the WordPress.com dashboard.

\n\n\n\n\n\n\n\n

After selecting a post, users are taken to the design wizard where they can add an image, title, a snippet, and a destination URL. The URL can be the post or page or it can direct visitors to the main website.

\n\n\n\n

When Blaze first launched on Tumblr there was no way to target the promoted content – it just displayed to random users. Now there are a few more options. When promoting content from WordPress.com or a Jetpack-enabled site, users can narrow the audience by device: mobile, desktop, or all devices, select from a few main geographic areas (continents) or serve it everywhere. There is also a dropdown with topics of interest, but they are fairly general, e.g. Arts & Entertainment, Automotive, Business, Education.

\n\n\n\n\n\n\n\n

After selecting the audience, users can set the budget for the campaign, starting at $5 with a max daily budget of $50. With a minimum of $5/day for a week users can expect an estimated 5,900 – 8,000 impressions. For $25/day, users can expect 29,700 – 40,200, and up to 59,500 – 80,500 for $50/day. Site owners can monitor the success of their ads in the Campaigns tab.

\n\n\n\n

Content sponsored by Blaze will be promoted across WordPress.com sites and Tumblr pages, an audience that accounts for an estimated 13.5 billion impressions per month.

\n\n\n\n

Blazing has become somewhat of an art in the short time it has been available on Tumblr. It will be interesting to see how ads originating from WordPress.com and Jetpack go over with the Tumblr audience.

\n\n\n\n
\n

There\'s a whole code of ethics around Blaze on Tumblr basically if you use it to do anything other than shitpost people hate you, you can also buy Tumblr merch and buy fake verification (which isn\'t seen as a serious thing, it\'s just a way to support tumblr)

— New Year New Simisear Fan \"🐀\" (@LakeUncalming) January 10, 2023
\n
\n\n\n\n\n\n\n\n

Creating advertising content that works across the disparate audiences between WordPress and Tumblr-powered pages may be a challenge for some site owners. Tumblr users can only target audiences by location for blazed posts. It’s possible that WordPress’ additional targeting options can help funnel the ads to sites where they will be most well-received, but the announcement says ads will be promoted across WordPress.com and Tumblr.

\n\n\n\n

Blaze campaigns require approval to be in compliance with Automattic’s Advertising Policy before being published. They are currently moderated in approximately 30 minutes but this may change in the future as more users try out Blaze.

\n\n\n\n

Automattic is treading new ground in creating its own ad network that any user across Tumblr and WordPress can tap into. It’s a strategic move to extend access to the world of WordPress, given that it’s such a large audience, and it will be interesting to see how the company improves the targeting options to meet the challenges of serving both audiences.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 22:52:36 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:36;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:107:\"Post Status: Improving 5ftF Contributor Journey • Building Interactive Blocks • Layout Classes • WP20\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146399\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:106:\"https://poststatus.com/improving-5ftf-contributor-journey-building-interactive-blocks-layout-classes-wp20/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:16911:\"

This Week at WordPress.org (January 9, 2023)

\n\n\n

Share your feedback about how to improve the Five for the Future contributor journey. Check out work underway on how to make interactive blocks easier to build, and take a walkthrough of layout classes in WordPress 6.1. It\'s time to start planning; how will you celebrate WordPress\' 20th birthday?

\n\n\n
\n\n\n\n\n\n\n\n

News

\n\n\n\n\n\n\n\n

\n\n\n\n
\n
\n

Community

\n\n\n\n\n\n\n\n

Core

\n\n\n\n\n\n\n\n

Meetings

\n\n\n\n\n\n\n\n

Developer Blog

\n\n\n\n\n\n\n\n

Docs

\n\n\n\n\n\n\n\n

Hosting

\n\n\n\n\n\n\n\n

Marketing

\n\n\n\n\n\n\n\n

Meta

\n\n\n\n\n\n\n\n

Openverse

\n\n\n\n\n\n\n\n

Performance

\n\n\n\n\n\n\n\n

Polyglots

\n\n\n\n\n\n\n\n

Plugins

\n\n\n\n\n
\n\n\n\n
\n

Project

\n\n\n\n\n\n\n\n

Support

\n\n\n\n\n\n\n\n

Test

\n\n\n\n\n\n\n\n

Themes

\n\n\n\n\n\n\n\n

Training

\n\n\n\n\n\n\n\n

Online Workshops

\n\n\n\n\n\n\n\n

Tutorials

\n\n\n\n\n\n\n\n

WPTV

\n\n\n\n\n
\n
\n\n\n\n
\n\n\n\n\n\n\n\n\n\n\n\n

Thanks for reading our WP dot .org roundup! Each week we are highlighting the news and discussions coming from the good folks making WordPress possible. If you or your company create products or services that use WordPress, you need to be engaged with them and their work. Be sure to share this resource with your product and project managers.

Are you interested in giving back and contributing your time and skills to WordPress.org? \"🙏\" Start Here ›

Get our weekly WordPress community news digest — Post Status\' Week in Review — covering the WP/Woo news plus significant writing and podcasts. It\'s also available in our newsletter. \"💌\"

\n\n\n\n
\n\n\n\n
\"Post
\n

You — and your whole team can Join Post Status too!

\n\n\n\n

Build your network. Learn with others. Find your next job — or your next hire. Read the Post Status newsletter. \"✉\" Listen to podcasts. \"🎙\" Follow @Post_Status \"🐦\" and LinkedIn. \"💼\"

\n
\n\n\n\n
\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 18:08:54 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Courtney Robertson\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:37;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"WPTavern: ClassicPress Community Votes to Re-Fork WordPress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140878\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:70:\"https://wptavern.com/classicpress-community-votes-to-re-fork-wordpress\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:3772:\"

In December 2022, the ClassicPress community voted on whether to re-fork WordPress or continue on with the project as-is. As WordPress continues to evolve, ClassicPress fell behind in pursuit of PHP 8+ compatibility. The fork is based on WordPress 4.9 and users are increasingly limited in what plugins will work with the five-year-old codebase.

\n\n\n\n

In a discussion limited to ClassicPress core contributors, Viktor Nagornyy, one of the project’s directors, announced the results of the vote: “The option to re-fork has 20 votes while continue-as-is has 18.” Nagornyy summarized previous discussions and suggested an approach that would be more realistic for the project’s limited contributors:

\n\n\n\n
\n

ClassicPress can’t be WordPress without Gutenberg, but it also can’t be its own CMS with a small core team at this time. There are simply not enough developers to make progress without backporting code from WP to move away from WP.

\n\n\n\n

An almost even split in the poll suggests the best option might be a hybrid one, find a compromise solution that will satisfy both sides.

\n\n\n\n

With a small core team, we have to find ways to be more efficient, to get more done with less. The only way to do that is to leverage all the work that’s done by WP contributors. As the core team grows, we can always explore the possibility of splitting away from WP but at this point in time, it’s simply not feasible.

\n
\n\n\n\n

Some participants in the previous discussion saw re-forking as postponing the inevitable, kicking the can down the road until the next re-fork, but it is the only option if users want to retain compatibility with the rest of the WordPress ecosystem.

\n\n\n\n

“If you read recent threads, you find out that the community expects plugin compatibility with WordPress… another reason for the re-fork option,” ClassicPress core committer Álvaro Franz said.

\n\n\n\n

Franz, who is also the author of the WP-CMS fork based on WordPress 6.0, previously said he would be unwilling to help with a continuation of the current version based on WordPress 4.9.

\n\n\n\n

“It [ClassicPress] doesn’t have to be a competition (and it never could compete with WordPress anyways), but it can be a leaner version, for people who are already disabling Gutenberg via plugins, for developers who want a different approach to the way they develop their projects (closer to ‘the classic’ experience, but yet… modern!),” Franz said.

\n\n\n\n

“Eventually, it won’t make sense to run a fresh copy of WordPress to then go and install a plugin that ‘disables’ half of it. What’s the point? Why not have a version that covers that specific use case?”

\n\n\n\n

As part of Nagornyy’s proposed hybrid approach, he suggested the project retain some changes that were introduced in ClassicPress in v1.x, such as the privacy-oriented changes (anonymizing data CP sends to APIs), the news widget, and ensure that all API endpoints use ClassicPress APIs as in v1.x.

\n\n\n\n

The discussion continues around how to proceed with the fork. ClassicPress contributors are leaning towards using Franz’s WP-CMS fork based on WordPress 6.0 but have not finalized the details yet.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 15:10:57 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:38;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WPTavern: #58 – Lax Mariappan on How Headless WordPress Works\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:48:\"https://wptavern.com/?post_type=podcast&p=140972\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:77:\"https://wptavern.com/podcast/58-lax-mariappan-on-how-headless-wordpress-works\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:56075:\"Transcript
\n

[00:00:00] Nathan Wrigley: Welcome to the Jukebox podcast from WP Tavern. My name is Nathan Wrigley.

\n\n\n\n

Jukebox has a podcast which is dedicated to all things WordPress. The people, the events, the plugins, the blocks, the themes, and in this case, how Headless WordPress works.

\n\n\n\n

If you’d like to subscribe to the podcast, you can do that by searching for WP Tavern in your podcast player of choice, or by going to WPTavern.com forward slash feed forward slash podcast. And you can copy that URL into most podcast players. If you have a topic that you’d like us to feature on the podcast, well, I’m very keen to hear from you, and hopefully get you or your idea featured on the show. Head to WPTavern.com forward slash contact forward slash jukebox, and use the form there.

\n\n\n\n

So on the podcast today, we have Lax Mariappan. Lax is a web developer based in the Philippines. He’s an open source enthusiast and lover of all things WordPress. Lax has been tinkering with websites since high school. But it all changed when he discovered WordPress in 2010. Lax currently works as a backend engineer at WebDevStudios.

\n\n\n\n

We talked today about Headless WordPress, and it’s a complex topic. Headless is the concept of decoupling the WordPress admin from the front end of the site. WordPress will continue to work as expected, but the presentation layer will be done by a different technology. React Gatsby and Remix being some popular choices.

\n\n\n\n

This implementation of WordPress is complex, requires technical knowledge above and beyond that needed for a more typical WordPress install. But it has its benefits.

\n\n\n\n

Lax talks through all of this in great detail. How keeping on top of all the additional dependencies Headless WordPress requires can be time consuming. How it can create difficulties for content editors who don’t always get to see what their work will actually look like in real time. Why this approach to WordPress can take more time and resources during the build.

\n\n\n\n

Lex explains how these problems typically crop up, and how it’s possible to plan ahead and build in solutions for all the problems that you might encounter.

\n\n\n\n

If you’ve ever thought about going headless with WordPress, then the podcast today is for you.

\n\n\n\n

If you’re interested in finding out more, you can find all of the links in the show notes by heading to WPTavern.com forward slash podcast. Where you’ll find all the other episodes as well.

\n\n\n\n

And so without further delay, I bring you Lax Mariappan.

\n\n\n\n

I am joined on the podcast today by Lax Mariappan. Hello Lax.

\n\n\n\n

[00:03:30] Lax Mariappan: Hello, Nathan.

\n\n\n\n

[00:03:30] Nathan Wrigley: Very nice to have you with us on the show today. I have to commend you for your staying power, because Lax and I have tried to record this episode a couple of times and he’s been incredibly, incredibly thoughtful about getting his, all of his equipment and everything working. So thank you, first of all, I would like to express my gratitude for you staying the course.

\n\n\n\n

But before we get into the podcast, Lax, I wonder if you wouldn’t mind spending a moment just introduce yourself. Tell us who you are, where you are, who you work for, how long you’ve been using WordPress, all of those kind of things.

\n\n\n\n

[00:04:06] Lax Mariappan: Thank you. It’s good to be on WP Tavern, it’s one of my favorite publications, and also the favorite podcast. So I’m Lax, Lax Mariappan. I’m from India, and also I’m from Philippines. So I would say I live in both countries, and I use WordPress since my school days, like 2009. So I was looking for a platform to build a website for an event or something, and then I found out Blogger versus WordPress, and I liked WordPress more even that time.

\n\n\n\n

So since then, I’m using WordPress almost every day. And my first job I got started working as a PHP developer, I would say, and then fully focused on WordPress. And I wrote my first plugin in 2011. It’s a very simple one. It’s now kind of obsolete because Facebook changed it a lot. So I wrote a plugin for something to fetch Facebook feed. So, and then my journey goes on. Right now, I work as a backend engineer at WebDevStudios. So where I get a chance to learn and work more with headless CMS every day almost.

\n\n\n\n

[00:05:09] Nathan Wrigley: Your work at WebDevStudios, I don’t know a great deal about the company, but my impression of the company is that you work with, how should we describe it? Enterprise clients. You’re dealing with fairly large projects. I would imagine sizable budgets. Those kind of things, right?

\n\n\n\n

[00:05:27] Lax Mariappan: Yeah, yes. Enterprise level.

\n\n\n\n

[00:05:28] Nathan Wrigley: So when we decided we were going to have this conversation, Lax introduced the subject to me of headless WordPress. Now this is a word which I imagine some of you have heard before. Maybe some of you have never heard the word before. Perhaps there’s a subset of you which have experimented with it, but I’m expecting that the majority of WordPress users have not.

\n\n\n\n

So, first of all Lax, would you mind giving us a very, in depth I suppose is the right word. Give us an analysis of what headless WordPress is because I’m sure many people think they know what it is, but perhaps they don’t.

\n\n\n\n

[00:06:06] Lax Mariappan: So headless, or decoupled CMS, so first we all know content management system, right? So WordPress, we are using WordPress now as a content management system. It started out as a blogging platform. We used it mainly for blogging. And then WordPress introduced custom post types, taxonomies and all that sort of stuff.

\n\n\n\n

So we are now using WordPress to build simple to complex websites. Forums. Some people use it for their colleges, universities as a social media platform, and some of them use it for a job board and everything, right? So we have plugin for everything and we can customize it and we use it.

\n\n\n\n

So when it comes to the traditional CMS, we call that as monolithic. I hope I’m not using too much jargon here. Monolithic in the sense it has everything into it. So for example, if you go to a website, the header, footer, the sidebar, and the content that you see and the forms and everything that comes from the same CMS itself. So it is going to be, let’s say, in the case of WordPress, it’s built mostly with PHP and JavaScript.

\n\n\n\n

So everything is going to be PHP template with a bit of JavaScript and CSS to it. But when you say on the contrast, headless CMS, it means, so you can consider that as a, I would not say person. Maybe something like, you can imagine something that doesn’t have a head. So in the sense the body is the same, head is different.

\n\n\n\n

So you can imagine that as, you are going to use the same admin panel and you are going to have the same WordPress features. You can add the content, you can add menus, you can edit anything, you can add users, all that stuff. But when I view the website, so it’s not going to be your theme. So it’s not the typical way of how WordPress gets rendered.

\n\n\n\n

So instead we will be decoupling it. So that is WordPress admin will stay on another site. It can be on a subdomain or a sub folder, but the front end is going to be a different platform. So it’s going to be hosted in a, mostly a JavaScript based stuff. So you can use either React based frameworks like React itself or Gatsby, Next.js or Remix, or anything that you like.

\n\n\n\n

And also you can either go in another route as well. So you can make it like a fully static website, or you can render it on every time as a server side rendering as well. So every call will go to the server and renders.

\n\n\n\n

Okay, so now we can call that a small intro about headless. You may already know this one. It became a buzzword a couple of years ago, right? But now everyone wants to go as headless. I see that company goes headless, or my competitor goes headless. So I want to go that way. But, unpopular opinion. Maybe you might hear some other people say that too. Headless is not for everyone, or I would say not for every use case.

\n\n\n\n

It depends on how much content that you publish. What are your goals and what you want to achieve. So headless is good, it’s performant, it’s fast, secure, and it gives you more freedom and flexibility, especially in terms of performance it’s really good. But I would say it’s not the something like you should go headless. It’s not the answer.

\n\n\n\n

[00:09:10] Nathan Wrigley: So essentially you’re saying that there are scenarios where this is desirable, but there’s going to be other scenarios where WordPress, in the traditional sense of the word. The regular WordPress that you download, perhaps use a hosting company and it’s all driven by PHP. The normal way of doing WordPress. That might be the best solution for lots of people.

\n\n\n\n

Okay, so we’ve got our WordPress website, which we can interact with, and then the content that comes out of that website is pushed to something else. And probably we’ll get into what the options are there. But let’s take the use case of a company which comes to you and says, okay, we’ve heard this buzzword. We think that we want to go headless.

\n\n\n\n

What are the benefits of going headless? Let’s forget about all the problems that might be associated with it. Can we just iterate through the things that you will gain if you manage to pull off a headless WordPress website. Now, I know there’s going to be all sorts of different scenarios there, but maybe just pick out the low hanging fruit. Some of the things which you believe are really beneficial.

\n\n\n\n

[00:10:17] Lax Mariappan: Yeah. The first and foremost, or the popular one, is the performance. So WordPress uses PHP templates. We will do everything with PHP and Javascript and also a little bit of caching to render our traditional CMS like traditional pages. If you use a normal WordPress installation with a theme. So that’s how it’s get rendered.

\n\n\n\n

So there you can see it depends on the hosting company as well, and also how much plugins that you use and how you configured them. So that affects the performance of a site. But when it comes to headless everything is going to be bundled, and there will be how a normal JavaScript based application gets rendered.

\n\n\n\n

So it’s going to be a modern web application where you have control over, for example, if your page doesn’t use certain CSS classes, those CSS will not get loaded for that page. So I would say the assets that are loaded, it will be less. And the images will be more optimized. In either case, like in traditional too you can optimize images, but it’s like the performance is the first one, I would say.

\n\n\n\n

It’s going to be both developers will love it and also the site owners, and also, let’s say marketers, Everyone will like the performance aspect of it. And in terms of headless, I would say developers will like it, especially in terms of, so you can repurpose the content. So if you are having a CMS, WordPress as a headless CMS, you can use that same endpoint, get the data and display it in a different formats quickly.

\n\n\n\n

Other than a WordPress theme. So for example, if you’re using a WordPress theme, you have to create multiple templates. So this is a template for mobile, and this is something that, for example, if you want to use it for a landing page, you may have to do some small or extra changes. But when it comes to headless, you can just customize it in a way that you want to.

\n\n\n\n

For example, I want to have a landing page. I don’t want certain stuff to be there. So you can turn on, off certain components, that’s it. So it’s like you can render the blocks and render the content faster. So developers and designers will like it. And also, in terms of the security, that’s where I’m more interested in cybersecurity especially. When people say WordPress sites are not secure, that triggers me actually. Yeah, I do get angry.

\n\n\n\n

So it’s like, you don’t have to worry about that. So you don’t have to worry about changing your login page url. Adding captcha to your login form, all that stuff. Because that URL is going to be safe and secure. No one knows where you are hosted your CMS.

\n\n\n\n

[00:12:49] Nathan Wrigley: Can I just interrupt there? So could you explain that, because I imagine there’s a bunch of people scratching their head at this point. Because normally, let’s say you have a website, it’s example.com. You’re going to go to example.com/wp-admin, and there is your login page. But there’s something in between here. I’m not sure that we explained that quite. So just explain why the login is secure. Explain where it is and why it’s not normal WordPress.

\n\n\n\n

[00:13:19] Lax Mariappan: Yeah, so I mean, normal WebPress is also secure but people can guess it, right? Say example.com/wp-admin, so they know. They can see from the source code and the page source, they can see oh, this looks like a WordPress site. And then they can guess the admin url. So slash wp-admin, it’ll redirect them to the login page, right.

\n\n\n\n

But when it comes to headless, the example.com will be hosted somewhere, and the front end that you see will be different. So for example, let’s say CMS is your WordPress installation, all WP. So you can call that like wp.example.com. So that’s where your WordPress stays in. But when you go to the example.com, that’s your front end, so that’s just JavaScript and html. So it’s like, if somebody wants to hack your site or somebody wants to, just guess what will be the admin url. So they cannot.

\n\n\n\n

[00:14:10] Nathan Wrigley: It’s a difficult concept to understand if you haven’t encountered this before. But what you’ve got basically is a WordPress website, which is the container for the content, but it isn’t the website and we’re not used to that in traditional WordPress. You go to example.com/ wp-admin, get redirected, log in, do all the things, and click publish, and as soon as you click publish, it will be present on the website. That’s not the way that this is working because the WordPress website is completely decoupled from the thing which is presenting it to the world, right?

\n\n\n\n

[00:14:48] Lax Mariappan: Yeah. Yeah. Completely decoupled.

\n\n\n\n

[00:14:50] Nathan Wrigley: So given that, there’s no connection between, okay, here’s my website at example.com and where I might log in. And because of that there isn’t the capability to just guess the login page and then bruteforce an attack and so on. So in terms of security, it offers that benefit. The thing which people are most worried about, somebody getting your admin password going in and spoiling your site. That’s highly unlikely because they simply won’t know where to look.

\n\n\n\n

[00:15:23] Lax Mariappan: Yeah. And also, so for example certain normal pages like comments, so that’s where we get a lot of spam, right? So comments will go to comments.php. When you submit a form without any data, or maybe if it’s spam data, it just goes there, right? But when it comes to headless, we will be using some extra customization for the comments and everything.

\n\n\n\n

So it’s not going to be the data will store as comments in the database, and it’ll be, you can view them as comments in the admin panel. But when you are viewing it in the site, so you are reading a blog post, you have a comment form, so that form is HTML and JavaScript. So that’s not how a typical, a normal WordPress form, normal comments form.

\n\n\n\n

So that’s where you will get less spam as well. So you don’t have to worry about that too. Like people submitting spam data and also any other form. So that’s another thing. And you don’t have to worry about any other security related stuff, because it’s just static.

\n\n\n\n

So people cannot do anything or manipulate data. So it’s going to be just HTML stuff. Whatever they can do is just view the data. So I would say in the headless, so if you are viewing some pages or we are in a archive page and post archive, news archive, any archive page or any other page that does the data and fetches the data from the database, all that stuff.

\n\n\n\n

So all that stuff will be protected routes. So people cannot easily guess. Sometimes you might encounter database related attacks, right. So you may hear cross site scripting attack or any other stuff like, somebody trying to get data either they pull your data or they want to insert some other data to the database. That’s not the case.

\n\n\n\n

Everything is going to be static, like just html, and it’s only read only. So people are not going to input any data. And the input will be just maybe a comments form, contact us form, something like that. And that will be handled. It depends on what form provider you are using, or how you configure it, but still it’s more secure that way.

\n\n\n\n

[00:17:25] Nathan Wrigley: So just to reiterate the point one more time, just in case anybody hasn’t been paying attention. We have our WordPress website. It is used by the developers, by the content creators, by the editors. They do their normal work inside of WordPress, but the thing which is being viewed on the front end by the population at large is completely separate.

\n\n\n\n

You’re just sucking the data out of WordPress and putting it into whatever you like. The security’s fairly obvious, you’ve explained that really well. The performance, obviously, if all that you are showing is static html, essentially. That’s going to load really, really quickly. Nothing needs to be built at the time that the page is viewed and so on and so forth. It’s already been created.

\n\n\n\n

This all sounds amazing and of course that raises the question, why aren’t we all doing it? And you have given us, in the show notes you’ve given me, three different things which we perhaps should talk about, and some of them, you explained the problem and then we’ll get to the solution.

\n\n\n\n

So the first one that you talk about is dependency hell, you’ve described it as. And, I’m guessing that having a headless site is not straightforward. We’re very used in WordPress to, novices can install WordPress incredibly quickly. You basically upload a zip file and unpack it and connect it to a database, and these days, you know, you go to a hosting company and not even that. You just click a button and, wow, there’s your WordPress website 30 seconds later.

\n\n\n\n

I’m guessing that this is not the case for headless. There must be all sorts of complex layers of things going on in the background, and you say that in many cases it can become very difficult. Dependency hell. So describe the problem of all the dependencies.

\n\n\n\n

[00:19:13] Lax Mariappan: So when you have a WordPress installation, we will be installing plugins, right? You might be, if you are using WordPress for a while, you are already aware of the jQuery migrate plugin. All that stuff. So WordPress uses jQuery even now. So jQuery is a dependency that WordPress requires. WordPress depends on jQuery in admin panel, and also on the front end.

\n\n\n\n

So if you want to get rid of jQuery, it’s kind of, WordPress may not be the same, if you want to eliminate that. Because WordPress depends on it. So it’s something like, let’s say you cannot say that as a oxygen, but it’s something that we all need it. So we need that to survive. So WordPress needs jQuery to work normally.

\n\n\n\n

So similar case, when you are building a headless site, you will be requiring a lot of frameworks, libraries, and also packages. So for example, if I’m going to choose Next.js as my front end platform, front end framework. So Next.js is built with React. If I want to use Next.js, I may want to use some other Next.js related libraries.

\n\n\n\n

So it is something like if you are on Android, you may want to add extra apps on your phone. If you are an iPhone, you’ll be adding some more extra apps to extend, right? It’s the same case. Similar to plugins. Instead of that plugins, we will be adding packages. So that packages helps the developers to add extra features that we need.

\n\n\n\n

So the problem here comes in is, everything gets stacked in and one will be dependent on another. So, for example, if someone is installing a package like for SEO, and maybe that package will require something else. And let’s say if Nathan is maintaining SEO package and I installed it, and for example, for whatever reason, Nathan becomes a musician and he doesn’t, he is not interested in SEO anymore.

\n\n\n\n

So he may not be more active in maintaining that dependency, maintaining that plugin or that package. So what happens is I’ll be waiting for him to fix the bug or some errors. Or I will waiting for him to upgrade to the lightest version. But it’s not the case, right? So, my Next.js package will be waiting for Nathan, so it’s like I’m depending on him, but he’s not available. So in that case, I have to go and do that work as well. So that adds to our development timeline.

\n\n\n\n

And then, so this is just one package and one scenario. So this happens with multiple packages and stuff. And this is not just Node or NPM packages. It also happens to WordPress stuff as well. So, for example, let’s say we have a popular forms plugin, or we have a popular slider or any other plugin.

\n\n\n\n

So you will install that plugin and you want that plugin to work with headless. So how we are using headless, it’s the data is stored in the WordPress, and we want to get the data through either Rest API. It’s a method that we, you know, you go to a url, you ask the WordPress, hey, give me this data and it’s going to give. Or you’ll be using GraphQL. It’s the same. You go to an endpoint and you’re going to say, hey, I’m looking for this post. I want five posts from this date. So it’s going to give that data as well.

\n\n\n\n

So either you use Rest API or GraphQL. The problem is a plugin that you are using, your popular forms plugin, your popular slider, or any other plugin that you’re using. LMS plugin, E-commerce plugin or any plugin, like a payment gateway. So you have a plugin and you want to use it with headless. So that plugin should work with the Rest API or Graph QL. So if that doesn’t work, if that doesn’t give you the flexibility, and then you are still stuck there.

\n\n\n\n

Because you cannot go and create everything on your own, right? So we cannot reinvent all the wheels. We don’t have time to create everything from scratch. So that’s where it’s like that becomes a bottleneck. So you are like, hey, I found the plugin. I started working on it. It works up to this mark, but it’s not a hundred percent. So it’s like it does its job 80%. Now I have to go fill in that 20%. It adds to the budget, it adds to the development timeline. So that’s the dependency hell.

\n\n\n\n

[00:23:15] Nathan Wrigley: Yeah. So in the case of all of the technology, which is in the background if you like, which we haven’t really talked about too much, but like you said, the things which you are requiring from third party developers. There’s a dependency there, and it’s very similar to the dependency that you may have on plugins, you know, you want them to be updated and so on, but you are adding extra dependencies. And of course, the more dependencies you’ve got, the more costly, time consuming it is.

\n\n\n\n

I’m guessing that most of the things that you are depending on, in addition to WordPress and you described what a few of those were, you could, I suppose, do some due diligence and figure out which projects have been well maintained, updated frequently, and so on. And I guess in the open source world, much of the dependencies that you’re using will be open sourced, so you could fork them. But again, you are creating probably a large amount of work for yourself and your team.

\n\n\n\n

[00:24:13] Lax Mariappan: Yeah that’s true. Well said. So it’s like, since it is open source, it’s good. Like lot of reviewers. We have a lot of eyes on the code, and you can fork it. You have the freedom to do whatever you want. But still you are looking for a solution and that becomes a problem. You have to fix that as well. And that adds to the, another dependency, another dependency. It becomes a cycle that you cannot escape sometimes.

\n\n\n\n

[00:24:36] Nathan Wrigley: I guess this is a bit like a seesaw. You know, on the one hand you described all of the benefits, performance, security, and so on, of headless. And then on the other side is, is all of the things that we are now describing. You know, the dependencies and so on. You’ve got to weigh up at the beginning of the project whether one thing is worth all of the time and effort that may be required to do it.

\n\n\n\n

And I’m guessing in many cases, certainly at the enterprise level, the answer’s going to be yes, because the budget is there, we can put enough bodies to work to make all of this happen, and if we need to fork things, there’s enough people on the team that can do that and maintain the project, which has fallen into disuse. But for a little project the seasaw may tip heavily against something like headless just because of the things that you’ve described there.

\n\n\n\n

Okay. So that was our first thing, dependency hell. The second thing that you wanted to talk about was the fact that in the WordPress world, especially in the last five or six years or so, we are really used to what you see is what you get, WYSIWYG. You save something in WordPress, you publish something and you have almost a hundred percent certainty of what it’s going to look like. The backend looks like the front end, especially with things like page builders and so on. But you say that that’s not always the case with headless solutions. Why is that?

\n\n\n\n

[00:25:55] Lax Mariappan: We will be creating custom blocks. So, either there are a popular way of building now custom blocks is with ACF. So you all might be aware of and using it, even though you are not a programmer, you might be using it, right? So ACF is easy to install and create some custom fields. So you can use ACF to block, to build blocks for the site.

\n\n\n\n

So those blocks can be used or you can build your own custom blocks. You can use any block starters like, frameworks that are available now. Or you can just follow our, WordPress comes with packages that you can on build command, so you can just build your block in a matter of seconds.

\n\n\n\n

But still, all this stuff. So for example, if you are having custom blocks, I’m not talking about just normal blocks, like where you add a paragraph or image or something very simple. That is easy to build and that’s easy to see. That’s different. But I’m here talking about something complex.

\n\n\n\n

So for example, you can imagine that as an Elementor widget or, some other items that it comes with the page builders. So, let’s say a slider, maybe tabs, accordions, all that stuff, right? So that can be added through the blocks itself. But you cannot preview them, because when you add them in the admin panel and we add them in the content. Those content gets, you know, you can choose like, oh, this is the tab title, this is the content.

\n\n\n\n

And you can keep adding the content, but you don’t know how it’s going to render in the front end. But let’s say if you are using some, there are a lot of free blocks and also even premium blocks available. So if you are using a block to build them, and then using the normal WordPress installation. Or you can use WordPress with the full site editing, the modern themes, or the hybrid themes, like old plus full site editing themes.

\n\n\n\n

Still they both work well. Like you can preview, oh, okay, this is the tab I added this content. I can’t view this one. But when it comes to ACF blocks or other certain custom built blocks, you cannot preview them.

\n\n\n\n

So when a editor or a user adds content, they may get lost. So I have a slider. I want to add three, four images to it. I may get lost. Oh, what’s the third image? What I have added there, and how it looks? Is the images correct? Is the text rendered properly or should I reduce any title or text or anything, right? So all this stuff becomes a little tricky. And also sometimes it becomes a pain for the content writers, content editors, and also the site owners.

\n\n\n\n

[00:28:24] Nathan Wrigley: So in the normal, traditional WordPress, let’s say we’re creating a page, we add a page, and we use whatever tool it is that we want to use for that. We add in some blocks. We are perhaps using Elementor, whatever it may be. And we click publish and then we are able to immediately view that because WordPress is working in the traditional sense of the word. The page gets pushed through the templating engine and it’s rendered with its template and we can see it right away.

\n\n\n\n

But because that’s not happening here. And the mechanism for rendering that page is entirely different. You can’t necessarily view it immediately. Have I kind of encapsulated that? What you are doing in the backend, because it’s decoupled with the presentation layer on the front end, you can’t necessarily always see it?

\n\n\n\n

[00:29:16] Lax Mariappan: Yeah, so that’s the challenge. So the solution here is to customize the way you built. So for example, we can give them a preview button so they can preview what are the slides, and how they look. And they can see that immediately in the editor itself. Like when they are adding content in the block editor, they can see it.

\n\n\n\n

And also the other way is to have a button, a preview button. So that will preview before the content gets published. So, you can change the workflow. So if somebody hits, instead of publish, you can have like a preview button or keep it as a draft. So that way it’s like nothing goes to the front end without your approval or preview, right? So you have to preview it and see, oh, make sure everything looks correct, and then you can say, hey, I want to publish it. Yes, confirm, publish it, and then it goes to the frontend.

\n\n\n\n

[00:30:04] Nathan Wrigley: That’s fascinating. That’s really ingenious. So, because we can’t necessarily see it on the frontend, you and your team have built a custom preview system. So on a block by block basis, you can see what that block will look like when it’s rendered. So in the example of your slider, presumably where we’ve got three or four fields. We’ve uploaded maybe some text, we’ve uploaded an image, and it’s just a bunch of fields. Normally we’d click publish and we’d go to the page and preview the page and we’d see it right away. But in your scenario, you are going to hit a button inside the block to show what that block and that block alone will look like. Have I understood that?

\n\n\n\n

[00:30:48] Lax Mariappan: Yeah, that’s what we did. Because the users, they are used to the traditional WordPress. And especially that was with classic editor, I mean the old editor. So if you insert an image, they can see it’s an image. And if you insert something, you can see. And we are all used to the page builder era, right? So if you add a accordion, you can see how the accordion is going to look.

\n\n\n\n

But when it comes to headless, all this stuff is going to differ. So, the tabs, accordions, sliders, and also anything else, any other custom stuff that we built, we added a preview button, and when you click on the preview, you can see that right away.

\n\n\n\n

Then you can make sure like, oh, the colors are correct, the image is correct, and everything renders properly. Because sometimes if you are not looking at the content and adding content, you might miss some data, right? So you might have missed a small setting that says full width, or you know, boxed. So then you feel like, oh, why this looks so awful. Oh, I’ve missed this full width button. So that’s how the preview button works.

\n\n\n\n

[00:31:49] Nathan Wrigley: So if I’m looking at the block and it’s a, let’s stick with the slider just for the sake of it, and I’ve uploaded my images and whatever fields were required and I click the preview. Does it literally happen inside that block? Or is this some kind of modal which pops up and shows things? Or is it, is it literally taking over the block itself?

\n\n\n\n

[00:32:09] Lax Mariappan: Ah, it’ll be within the block. Like it will replace, so for example, if you have a block and you are adding some content to it, and when you click on the preview, it’ll replace where you are adding the content, right? It’ll replace the form. Form of the block where you are saying like, hey, this is the title, this is the subheading, this is the description. Instead of that, it’ll just render the titles, heading and description.

\n\n\n\n

[00:32:32] Nathan Wrigley: Right, and then you toggle that off again once you’re, once you’re happy. So, ah, that’s really interesting. So the workflow there is really very different. And I’m presuming that after a period of time, the people who are editing, creating this content, that just becomes part of the process? They just understand that, okay, rather than viewing the whole page or whatever it may be, post whatever, I’m just viewing this little bit, and I’ve done it several times now and I’m confident that if it looks right inside the block preview, then I can click publish, wait for everything to happen, and hopefully that page will go live. And, it’s just a different workflow that you have to get used to. But once you’ve done it several times, it’s, familiar and normal.

\n\n\n\n

[00:33:14] Lax Mariappan: Yeah, it becomes part of the workflow. And also, like we discussed earlier, your site will be like, CMS.example.com. And the front end will be on example.com. Sorry, every time you have to go to example.com/about, example.com slash contact us. Instead of that we will have a preview button. So, you can preview each block and you, if you, or feel like, hey, I want to see how the whole page looks like, you can click that preview, and that will take you, or that will show you immediately, oh, this is how the front end, like example.com/the page will look like.

\n\n\n\n

[00:33:45] Nathan Wrigley: Yeah, that’s a good point. We’re so used to the preview button being connected to the URL in question, because it’s being rendered by WordPress. You click the preview page button or whatever it may be, and it takes you to the correct place. In this case, there’s no connection between what the URL will be and where you currently are, so yeah, that’s fascinating.

\n\n\n\n

Just as a bit of an aside. We haven’t got into this, but I think it would be a good topic to discuss for a couple of minutes. If WordPress is separated from the presentation layer, this sort of headless notion. How often does the website get regenerated, if you know what I mean? So for example, if we click publish in our headless WordPress website, what is typical there? Are you going to generate the page immediately and store it as static html? Or do some clients have different expectations there? You know, for example, if you are a, a site which needs to publish things regularly, perhaps you need that capability.

\n\n\n\n

I click publish. I want that page to be live within a matter of moments. Or it may be that you’ve got a website where it doesn’t really matter if the pages are not built, I don’t know, three hours, six hours a day, whatever it may be. Do different clients have different expectations there?

\n\n\n\n

[00:34:56] Lax Mariappan: Yeah, that depends on how the publication frequency is. If you want to publish immediately, we can do. If you are okay with publishing the changes after two, three hours, still we can do. So it’s about how you want to set, how you want to build the things.

\n\n\n\n

So here, few things to consider. You can go with static, fully static website. That’s just static and only when a page gets updated. So for example, you have a hundred page. All of them are static and those pages will not be regenerated. So if you change just the about page and only that 99 pages will remain the same. Only that about page will get regenerated again. You can go that route.

\n\n\n\n

And also you can go with, every time in the page gets rendered, you can go server side rendering. So every time that’s new, so you can go that route as well. So that depends on how you want to render the data and everything has pros and cons. The normal way is like how Next.Js does now. Because it is like, keep everything static and if you want to render something, you can still regenerate the specific page.

\n\n\n\n

So this way it’s like you don’t have to build everything all the time. So you can build what has changed in the WordPress. You can see that in the headless frontend. And also you don’t have to wait for it. So, for example, if I go make some change and click update and you can see that immediately.

\n\n\n\n

[00:36:21] Nathan Wrigley: Really interesting, because there is no exact way of doing this is there? You can just build it in whichever way you think is most beneficial, or whatever the client needs. You know, if, if it’s a newspaper website where, really I need to click publish, and within a few moments I need that page to be live because the content that we’re creating is tremendously important to be fresh and new and so on. But it may be that, yeah, you don’t have that expectation and you’re quite happy to have it work in a different way and publish on a, a much less frequent basis. I can’t really imagine a scenario where anybody would say no, I’d rather it was published less frequently, but maybe there are scenarios where that’s beneficial. I don’t know.

\n\n\n\n

Okay, and the last point that you wanted to talk about was, the whole conversation has proven to be really interesting, but it’s pretty clear that there’s a lot more work involved in this kind of website. And so your first point was about the fact that the dependencies, lots of dependencies. Your second point that was that you don’t always get to see what you see is what you get in operation. And the third one is basically the amount of time it takes, the amount of resources it takes. You’ve described this as headless asks for more. Tell us about that.

\n\n\n\n

[00:37:34] Lax Mariappan: Yeah, so when it comes to creating a normal WordPress, like a standard WordPress theme. So what you do is like, you start with your prototyping tool. Like it can be Figma, Adobe XD or anything. So you have your design ready, right? You are creating mock-ups, discuss with the client, and then create a mock-up and then find the variations, all that stuff. And you are settling in, hey, this is my design. And now I’m going to create the theme.

\n\n\n\n

So, I want to create this many templates. I want to create this many menus, all that stuff. When it comes to traditional stuff, it’s like, you don’t have to consider too many things. So it’s kind of straightforward process and like designers and developers can, the engineers can work hand in hand. And it’s, you can follow Agile like, build stuff, reiterate and just deliver it.

\n\n\n\n

So that’s how that works. But when it comes to headless, so you have to consider a lot of things. I would say the first thing is the knowledge or, you know, expertise. With WebDev Studios, we are, I would say kind of one of pioneers and also experts in WordPress plus headless stuff. So we have launched, it’s a open source like we have Next.js starter template. So if you want to try out Next.js a headless frontend for your WebPress site, you can just take a look at WDS Next.js starter. It’s free and it’s in GitHub, so you can just start using it.

\n\n\n\n

So, expertise comes one, like whether you should be, have sound knowledge in that. So you can go and fix stuff. You know what you are doing and you know what to expect and all that stuff. But this requires something like, for example, I am a backend engineer. I have limited React knowledge. I’m now catching up with React, Next.js, all that stuff. But I would, I would not say I’m an expert at it. I build stuff, I still use Next.js every day, but it’s like, I won’t say I’m an expert at it.

\n\n\n\n

So expertise is one. So your team should have sound knowledge in the framework or anything that you do. Or even if you don’t have sound knowledge, let’s say if you are doing something like, something very new, like Remix got released only one or two years ago, right?

\n\n\n\n

So if you want to go use Remix, You should be an expert in React and you should play around with React. So that’s the time. So my point is like time, it asks for expertise and it asks for time. So when it comes to just normal WordPress theme, probably you might finish the theme, let’s say, in a few weeks, or at least a few days even sometimes. With page builders finish it in few days or few weeks, right?

\n\n\n\n

But maybe if you are building it from scratch and you are doing a lot of customization, it may take a while. But when it comes to headless, may take even longer. So more expertise, more time, and all this adds up to more budget.

\n\n\n\n

This may sound like, oh, well should I do all this stuff? It’s kind of worth it. So you don’t have to, for example, if you have your, the front end components ready you may be having your storybook, like where you want to see how the button should look like, how the elements, how the panels are. Let’s say how each component will look like and how they render, all that stuff, right? So when you have all these parts ready, you can go from, for example, today I’m using Next.js, sooner I can move to something else, like I can use Remix. Or I can use something else that’s going to be hot in the market in future.

\n\n\n\n

But when it comes to the typical WordPress, you are going to change everything from scratch. So if you want to add a new theme, so maybe if you want to change the look and feel, that’s different. So everything has pros and cons, but the short answer is the headless CMS ask for more.

\n\n\n\n

[00:41:13] Nathan Wrigley: Yeah. It does sound like not only do you need more time to develop all of this for the reasons you’ve just described. It’s more complicated, so it takes more time. There’s more moving parts, shall we say. And it may also be that you need to spend some of that time not just building the thing, but learning how all of this hangs together, because there’s an awful lot going on in the backend here. And if you don’t have expertise in that, presumably things could go pretty wrong.

\n\n\n\n

With that just before we end. You’ve obviously decided at WebDevStudios that this is an approach. I don’t know if you build the majority of your sites in this way or subset or a proportion of them, not sure. But, typically what is the amount of time longer it would take to get a website out? Let’s say, for example, that if you were just going to use WordPress as is a normal WordPress website, and you built an exact same website, but did it headless. And let’s imagine a site with, I don’t know, several different custom post types.

\n\n\n\n

It’s got hundreds of pages. I’m just kind of making up something off the top of my head. But typically, you know, does it take twice as long, three times as long, 50% longer? What, what are we looking at?

\n\n\n\n

[00:42:28] Lax Mariappan: I’m going to answer just like other engineers do. It depends. But it’s like, I would say it takes a long, maybe you can say, maybe you can say double, but it should not take more than double or something. So that’s where I would say start with more of research. So you should not change frameworks or libraries in between. Like once you started as React, go with React. And if your team is, they are very comfortable and they’re knowledgeable in React, use that. If you are going to use Vue.js or Astro or any other framework. When you start with something and you can go with it.

\n\n\n\n

So, it is a matter of discovering what the client needs and where the goals meet. How we can achieve it. And once we are very clear on that, you can start developing. And during the development phase itself, we can see what are the possible, you know, the bottlenecks or what causes the issue, what could be a problem, and we can figure out other different approaches and solutions.

\n\n\n\n

So, for example, you don’t have to let’s say, PayPal is not the only payment provider right now, right? The payment gateway. So we are using so many different stuff and they do the payment integration quickly. But before those days, let’s say 10, 15 years ago that case was different, so now we have more options.

\n\n\n\n

So similarly, you don’t have to create a form and you don’t have to wait for someone to, the third party or some other open source in a package or something to be ready. So either you can build something on your own if you have time and budget, or you can fork something and then you can adjust to it.

\n\n\n\n

Or the other way is, I would say you can go with some existing third party or SaaS or any other solution, which is just already there and you can see how you can use it with WordPress. So these are the stuff that can reduce your development time.

\n\n\n\n

So when you say if you are, I don’t know exact hours or something, let’s say a thousand hours. So if you say a thousand hours for a normal WebPress installation, so headless may take a little longer, 1,500 or 2000 or anything. But it depends on what the client wants and what framework you choose and your expertise, like, I mean, the whole team’s expertise. And also how well we plan, organize, and go.

\n\n\n\n

So sometimes it’s like just the client takes so long to respond, or sometimes it’s just like, even the client is clueless or what’s happening. So that adds up to some stuff. And I would like to also highlight, when you hear all this stuff, somebody listening is, they will be scratching their head like, so headless is yay or nay.

\n\n\n\n

So, recently, I cannot say the client name and stuff, but I would say, how we figured this out and how it is kind of helpful. So we had to publish more than 20 websites. That’s for a single client. And all of them are different, and all of them are headless, but that’s for a single parent company.

\n\n\n\n

So what happened is, we had the architecture ready, right? So we, we know what happens when you publish. We have everything ready. I mean, the back end and the front end ready. So things become more easier that way. The development time is actually just for one site and then other sites, it’s just like, it was fast.

\n\n\n\n

But we had enough configuration and enough options we given to the client. So every site is not going to look exactly the same. They have their own customizations. But still it’s like amount of development time is the same or is actually less when you compare to traditional. But it depends. It depends on what’s the use case? How, what you are trying to build and everything.

\n\n\n\n

[00:45:52] Nathan Wrigley: Yeah, it really does sound, there were so many good perspectives at the beginning where, you mentioned performance and so on where this is definitely going to be worth it. I guess if the client is willing and the budget is available and the expertise is there, then this sounds like an incredible option. Steep learning curve probably, but a lot of benefits on the backside of that.

\n\n\n\n

Lax, just before we round it up, if somebody has been thinking about playing with headless and they’ve listened to this and they think, okay, I’d like to take that a bit further. Couple of things, firstly, where can they get in touch with you? But also have you got any guidance about resources that they may find useful?

\n\n\n\n

So that could be a website or a book or whatever it may be. So let’s start off with resources and then we’ll turn to you to finish it off. So what resources do you recommend to learn about headless in general?

\n\n\n\n

[00:46:49] Lax Mariappan: In general it’s like you can start with WP Engine has their own blog. They have stuff about headless WordPress and they also have some of packages and stuff they maintain. They have Atlas. It’s a platform they are planning to go full fledged on headless stuff. And also you can read about GraphQL, WP GraphQL. Their team is more active and they share a ton of stuff on how to customize and maintain stuff with headless.

\n\n\n\n

And also you can, like a shameless plug. So I’d also highlight about our WebDevStudios blog. So you can see a lot of headless related articles, tips, and tricks. If you want to play around like, you know, you don’t have to spend something to test it out. So you can go with a lot of free starter templates.

\n\n\n\n

So we have, WDS has like WebDevStudios has a starter template. We have Next.js starter. So that’s a headless thing. All you need is your WordPress, and then you can install that on a locally in your laptop or machine, and then you can just test it out, how it looks, compare the performance and everything.

\n\n\n\n

And also, like other developers and writers have their own stuff. Like Colby Fayock is a popular WordPress developer. He has his own Next.js starter. So you can just simply Google WordPress headless starter, and you can find a lot of starter templates. If you are a developer, go this route or if you are a, you know, site owner or you are just hobbyist, you want to just try or understand a little bit more?

\n\n\n\n

You can still do that reading the resources, right? You can actually check our blog as well. WebDevStudios blog. We have, I would say a couple of headless related stuff. That’s one of the popular article last year. Why headless WordPress is trending. So you can see why it is trending, what to expect. You can read more details in that blog.

\n\n\n\n

[00:48:40] Nathan Wrigley: Thank you very much. And then finally, just to finish this off. Where could people get in touch with you? Are you available on social media? Maybe an email address? Whatever you’re comfortable with sharing.

\n\n\n\n

[00:48:50] Lax Mariappan: Sure. You can find me on, you know, Lax Mariappan. I’m on all the social media like Twitter, Instagram, Facebook, and everywhere you can find me. So you can reach out to me as an email as well, laxman.0903@gmail.com. Anywhere like GitHub everywhere is the same. Luckily I got my name on all the social media, so you can find it.

\n\n\n\n

[00:49:10] Nathan Wrigley: Lax Mariappan, thank you so much for chatting to me today. I really appreciate.

\n\n\n\n

[00:49:16] Lax Mariappan: Thanks Nathan. It’s been great. So I’ve been listening to WP Tavern Podcast for a while. Especially, I like to catch up with what’s going on. The new stuff with WordPress. So it’s good to be on the show,

\n\n\n\n

[00:49:28] Nathan Wrigley: Well, you are most welcome. It’s been a really interesting and informative episode. Cheers.

\n\n\n\n

[00:49:34] Lax Mariappan: Cheers. Thank you.

\n
\n\n\n\n

On the podcast today, we have Lax Mariappan.

\n\n\n\n

Lax is a web developer based in the Philippines. He’s an Open Source enthusiast, and lover of all things WordPress. Lax has been tinkering with websites since high school, but it all changed when he discovered WordPress in 2010. Lax currently works as a Backend Engineer at WebDevStudios.

\n\n\n\n

We talk today about Headless WordPress, and it’s a complex topic. Headless is the concept of decoupling the WordPress admin from the frontend of the site. WordPress will continue to work as expected, but the presentation layer will be done by a different technology. React, Gatsby and Remix being some popular choices.

\n\n\n\n

This implementation of WordPress is complex, requiring technical knowledge above and beyond that needed for a more typical WordPress install, but it has its benefits.

\n\n\n\n

Lax talks through all of this in great detail. How keeping on top of all the additional dependencies Headless WordPress requires can be time consuming. How it can create difficulties for content editors who don’t always get to see what their work will actually look like in real time. Why this approach to WordPress can take more time and resources during the build.

\n\n\n\n

Lax explains how these problems typically crop up, and how it’s possible to plan ahead and build in solutions for all the problems that you might encounter.

\n\n\n\n

If you’ve ever thought about going Headless with WordPress, then the podcast today is for you.

\n\n\n\n

Useful links.

\n\n\n\n

React Library

\n\n\n\n

Gatsby

\n\n\n\n

Remix

\n\n\n\n

WebDevStudio Next.js WordPress Starter

\n\n\n\n

GraphQL

\n\n\n\n

WPGraphQL

\n\n\n\n

WebDevStudio Blog

\n\n\n\n

Colby Fayock’s website

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 15:00:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:14:\"Nathan Wrigley\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:39;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:71:\"Do The Woo Community: 95% of Websites Have an Issue with Color Contrast\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74180\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://dothewoo.io/color-contrast/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:379:\"

Even just by getting your color contrast right, which is very easy, anyone can do it. You just use a contrast checker.

\n

>> The post 95% of Websites Have an Issue with Color Contrast appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 10:43:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:40;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:86:\"WPTavern: WordPress Performance Team Working Towards Unbundling Performance Lab Plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140668\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:97:\"https://wptavern.com/wordpress-performance-team-working-towards-unbundling-performance-lab-plugin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4476:\"

WordPress’ Performance Team met this week with the express purpose of responding to Matt Mullenweg’s recent request to stop adding functionality to the Performance Lab plugin which could otherwise work as a standalone plugin.

\n\n\n\n

At the end of December 2022, the Performance Team published instructions for how to test the new SQLite implementation, which was bundled into the Performance Lab plugin as a module. Mullenweg commented on the post, indicating he saw the SQLite functionality as better suited to becoming a standalone community plugin:

\n\n\n\n
\n

Can we please make this its own community plugin, hopefully to become a canonical one, and stop putting additional things like this into Performance Lab — it feels like we’re stuffing things into PL unnecessarily.

\n\n\n\n

In mid-October I have requested that we stop this unnecessary bundling before with @tweetythierry around WebP, which was put into Performance Lab, so it is disappointing that another large function like SQLite was bundled into Performance Lab plugin.

\n
\n\n\n\n

In an effort to galvanize a base of testers for upcoming performance features, the Performance Team has leaned towards bundling new performance-related functionality into the plugin. Although they are already developed as self-contained modules so they can be easily extracted as individual plugins, the concern is that their visibility would be greatly reduced. The Performance Lab plugin has more than 30,000 active installs. Any standalone plugin would take time to build up to a user base, whereas functionality added to Performance Lab has an instant audience.

\n\n\n\n

“Agreed that there are definitely valid use cases for stand alone plugins, remaining mindful of some of the advantages of a single hub plugin such as development/maintenance, adoption, promotion, developer onboarding/contribution etc. which the Performance Lab facilitates well today as a central performance focus community hub plugin,” Performance Team contributor Thierry Muller said in response to the unbundling request.

\n\n\n\n

Muller outlined three different options contributors discussed in this week’s Performance Team meeting:

\n\n\n\n
\n
    \n
  • Option 1: Keep PL as is, but additionally deploy modules as individual plugins
  • \n\n\n\n
  • Option 2: Make PL a “wrapper” focused on central infrastructure and recommendation of individual plugins
  • \n\n\n\n
  • Option 3: Deprecate PL completely in favor of individual plugins
  • \n
\n
\n\n\n\n

Option 3 seems to be the least attractive to those who participated in this week’s discussion, as it introduces more hurdles for discoverability. Performance Team contributor Felix Arntz noted that one benefit of option 1 is the plugin would continue to work as-is for the 30K people who currently have it installed and that option 2 “would require a complex migration that users likely would not understand.”

\n\n\n\n

WordPress developer Jonny Harris suggested that having each functionality in its own plugin helps with testing but also asked what defines a module.

\n\n\n\n

“Would the current Site Health checks all be together, for example?” Harris asked. “SQLite and WebP are clearly their own modules, but what about smaller things?”

\n\n\n\n

Arntz suggested contributors continue the discussion regarding the scope of how the current modules could be distributed as plugins. He suggested every module could become its own plugin where some modules become standalone plugins and others would be grouped together into a few “topic specific” plugins.

\n\n\n\n

Contributors are discussing the different approaches in more detail on a GitHub issue and will be voting on the best approach. The vote will be open until Friday, January 20, 2023.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 03:34:14 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:41;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:49:\"HeroPress: Why small can be just the right choice\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:56:\"https://heropress.com/?post_type=heropress-essays&p=5014\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:150:\"https://heropress.com/essays/why-small-can-be-just-the-right-choice/#utm_source=rss&utm_medium=rss&utm_campaign=why-small-can-be-just-the-right-choice\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:7334:\"\"Pull\nHere is Ellen reading her own story aloud.\n\n\n\n

I feel honoured to write an essay for HeroPress. While thinking about what I should write about, I wanted to make sure it will be helpful to others.

\n\n\n\n

Of course, everyone’s goals are different. My partner Manuel and I started to create WordPress products, because we saw the opportunity to build a small business and keep it a business we both felt comfortable to work in over the years. And that’s what we did. We love to travel and searched for a way to live the nomad lifestyle long before the term was even a thing. We travelled and worked on our blog and themes. And don’t get me wrong, it was not easy in the beginning. We had to build an audience first, so we wrote blog posts about everything we learned while keeping financially afloat with small client projects. We put endless hours of work into our blog, before even dreaming of one day earning income just with our themes. But we loved every minute of it.

\n\n\n\n
\n

We worked from Thailand, Sri Lanka and New Zealand, and we felt creative and free.

\n
\n\n\n\n

We went to WordCamps and creative conferences along the way and met so many new people with similar values and goals.

\n\n\n\n

Having these experiences formed our way of thinking about the way we wanted to work moving forward. The benefits of being a small team of two seemed so obvious to us. We could make decisions fast and react to new trends without asking anyone for permission. As long as we built something others liked, we would always be ok. So that’s what we focused on. We built one theme after the other and loved the creative freedom this work gave us. The positive feedback and listening to the stories our customers shared on how our themes helped them reach their goals kept us going.

\n\n\n\n

Living abroad

\n\n\n\n

As we could work remotely from any location. We didn’t need an office or a local team. Keeping our business so flexible allowed us to move from Germany to New Zealand in 2015. After about two years working towards it, we were able to apply for a business visa and eventually for permanent residency four years later. Living away from family is never easy, but the opportunity to live in another country surely teaches us so many valuable lessons we would never want to miss. It’s a true gift, all made possible by our small WordPress business.

\n\n\n\n

Reacting to changes

\n\n\n\n

Fast-forward to 2018 and the WordCamp Tokyo, where we first got the chance to dig deeper into the Gutenberg project during contributor day. We knew changes were coming, and we needed to react with our business. Even before, we felt that building one theme after the other felt a bit tiresome and not like the most effective way for WordPress users to build their site design. We were never convinced by the page builder solutions, as it just seemed too bloated and untrue to WordPress core to bring a wow effect to us. We love to keep things flexible and minimal, and adding an entire framework on top of WordPress never felt like a great idea to us.

\n\n\n\n
\n

So here comes this Gutenberg thing, a promise to a more flexible, component based way of creating designs for WordPress.

\n
\n\n\n\n

We felt like this is meant to be for us. So once home from the WordCamp we started to build blocks and explore how this new WordPress would work. We did not realize back then how big these changes would become and how much it would impact our work and our business.

\n\n\n\n

But it felt good to build something new and to try to find a better solution to offer for our theme customers. We struggled to gain footage for quite some time, as there were just so many new technical things to figure out and so much was unclear. But we still never doubted that we are on the right track, as with every new release the opportunities seem to get better and more stable.

\n\n\n\n

And just now we are just about to relaunch our business websites with a brand-new block theme that is solely built with our blocks, WooCommerce blocks and WordPress core blocks. It finally feels like all the work comes together and themes and the Gutenberg project are ready to be merged into one and released for production.

\n\n\n\n

Opportunity to pivot

\n\n\n\n

During all these changes, we had the time to think about the future of our WordPress business and what we want our road ahead to look like. Many others around us have sold their independent businesses or took a job at one of the big WordPress businesses. I feel like it’s also a natural path of WordPress and all of us growing up.

\n\n\n\n
\n

For us, we feel like we are just getting started again, finally having found a way to have fun creating for WordPress again.

\n
\n\n\n\n

Building one of our last classic themes, we felt like we had lost the fun in designing for WordPress. We felt like themes were stuck, being either too inflexible and or way too bloated to be any good. It felt like we were trying to build, squeeze out a solution into a product that technically was never meant to be this way.

\n\n\n\n

Block themes, the site editor, patterns, and blocks come as a chance for us to do it better. It’s a big shift and a difficult project to pull off, for sure. WordPress is used by so many people in so many ways. But block themes make WordPress lighter, and they don’t stand in the way of other add-ons as much as classic themes felt they were. It’s amazing how we can take all the components apart and mix and match them together. There are still missing pieces, but we are getting there.

\n\n\n\n

For us, we are taking this shift that we are sort of making together with WordPress, as an opportunity to make things better. We always felt like we wanted to offer more support and help to our customers. But we never found the time. So with our upcoming relaunch, we are taking the chance to change that. We will offer new services and are exploring more ways to offer our customers what they actually need. It feels like a breath of fresh air to us, and we haven’t had so much fun with WordPress in a long time.

\n\n\n\n
\n

It’s funny, who would have thought that a piece of software can impact your life in such a big way.

\n
\n\n\n\n

WordPress has impacted where we live, who our friends are and which destinations we like to visit. We feel more open-minded because of WordPress, we believe in the power of open source projects and we believe that a group of people from all over the world can build something meaningful together.

\n

The post Why small can be just the right choice appeared first on HeroPress.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Wed, 11 Jan 2023 01:15:30 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Ellen Bauer\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:42;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:63:\"WordPress.org blog: WordPress is Turning 20: Let’s Celebrate!\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://wordpress.org/news/?p=14155\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:74:\"https://wordpress.org/news/2023/01/wordpress-is-turning-20-lets-celebrate/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:1474:\"

2023 marks the 20th year of WordPress. Where would we all be without WordPress? Just think of that! While many technologies, software stacks, and fashion trends have come and gone throughout the past two decades, WordPress has thrived. This is due to the fantastic work and contributions of the WordPress community, comprised of thousands of contributors; and millions of users who have embraced the four freedoms of WordPress and the mission to democratize publishing.

\n\n\n\n

Let’s celebrate!

\n\n\n\n

Throughout the beginning of 2023, leading up to the official anniversary date of WordPress’s launch (May 27, 2003), a number of different events will celebrate this important milestone, reflect on the journey, and look toward the future.

\n\n\n\n

Please join in!

\n\n\n\n

Over the next few months, be sure to check WordPress’s official social media accounts along with the official anniversary website for updates on how you can be involved in this exciting celebration by contributing content, collecting cool anniversary swag, and much more. 

\n\n\n\n

Use the hashtag #WP20 on social media so the community can follow along.

\n\n\n\n

If you have something planned to celebrate that you would like to be considered for inclusion on the official website, please use this form to share the details.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 21:38:49 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:11:\"Dan Soschin\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:43;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:88:\"Do The Woo Community: WooCommerce, Payments and Crypto with Keala Gaines and Dave Lockie\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74249\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:52:\"https://dothewoo.io/woocommerce-payments-and-crypto/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:416:\"

Keala Gaines from WooCommerce and Dave Lockie from Automattic chat about the relationship between WooCommerce and Crypto.

\n

>> The post WooCommerce, Payments and Crypto with Keala Gaines and Dave Lockie appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 10:11:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:44;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:85:\"WPTavern: Gutenberg Times to Host Webinar on How to Use New WordPress Layout Features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:30:\"https://wptavern.com/?p=140874\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:96:\"https://wptavern.com/gutenberg-times-to-host-webinar-on-how-to-use-new-wordpress-layout-features\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:4790:\"

Gutenberg Times will be hosting a live Q&A webinar titled “Layout, Layout, Layout” on January 11, 2023, at 05:00 PM in Eastern Time (US and Canada) via Zoom. This event is open to WordPress users of all experience levels who are interested to learn more about how to use WordPress’ layout features when building sites with blocks.

\n\n\n\n

Host Birgit Pauli-Haack will be joined by WordPress veterans Isabel Brison, Andrew Serong, and Justin Tadlock. Brison will be demonstrating different layout scenarios during the presentation, and attendees will be able to participate with questions.

\n\n\n\n

Any user who has attempted to layout a design in WordPress has likely tried out container blocks that offer layout settings. These blocks include Columns, the Cover block, and the generic Group block.

\n\n\n\n

The event will cover how to manipulate layouts by defining the width of post content, arranging blocks horizontally or vertically, right or left aligned, and inside container blocks.

\n\n\n\n

“In terms of block styling, Layout is a complex feature because it affects child blocks in ways that go beyond CSS inheritance,” Pauli-Haack said.

\n\n\n\n

WordPress 6.1 introduced more layout controls and flexibility in the block editor, but Pauli-Haack said the dev note on updated layout support was written more for developers.

\n\n\n\n

“Feedback from users through the FSE program and other connections revealed that handling the layout settings for container blocks is not particularly intuitive and takes some trial and error to find the right combination,” she said. “The Live Q & A will bring a better understanding to users and #nocode site builders.”

\n\n\n\n

When Pauli-Haack started the Live Q & A’s in 2018, she routinely brought in guests who were building the block editor, with the intention of having users meet them and discuss features like full-site editing, block themes, case studies, and discuss challenges.

\n\n\n\n

“Since then, quite a few initiatives of the official WordPress project have come to life,” she said. “There is the highly successful Full Site Editing outreach program, spearheaded by Anne McCarthy, who now holds regular Hallway Hangouts with community members and contributors.”

\n\n\n\n

People are also learning the ins and outs of site editing through the efforts of the training team, which began creating courses and lesson plans and hosting workshops on Meetup.com in 2021. These are also recorded and uploaded to WordPress.tv and YouTube. WordPress.org also launched a blog for developers in November 2022. With all these new learning opportunities, Pauli-Haack is changing the focus for her live events.

\n\n\n\n

“For the Gutenberg Times Live Q & As, I am now looking at topics and discussions about more complex concepts, more case studies, and technology on the cutting edge,” she said. Most recently, the show featured the developers and digital strategies of the Pew Research Center, a high profile site that was built with a block-first approach.

\n\n\n\n

“We are also in planning phase to hold a Live Q & A with the developers of GiveWP who are using Gutenberg as a framework to build the next generation of their popular donations plugin with the components and scripts that Gutenberg uses, but outside the post or site editor,” Pauli-Haack said.

\n\n\n\n

She also has another Live Q & A planned with the WordPress VIP design team that works on design systems for companies that need a streamlined way to stay within their design standards. Pauli-Haack intends to talk with them about a plugin they created that lets designers automatically create a website’s theme.json file with all the styling pulled directly from Figma designs.

\n\n\n\n

The upcoming Layouts webinar is free but attendees need to register to get the zoom link. An archive of all the past Live Q & A events is available on the Gutenberg Times website. The best way to stay informed about future events is to subscribe to Gutenberg Times’ Weekend Edition, as subscribers get an early invitation for the next Live Q & A’s.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Tue, 10 Jan 2023 03:37:42 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Sarah Gooding\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:45;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:23:\"Matt: State of the Word\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:22:\"https://ma.tt/?p=75018\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:42:\"https://ma.tt/2023/01/state-of-the-word-2/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:365:\"

A few weeks ago, but what feels like a lifetime ago, I was in New York City with a few dozen extra special people from around the WordPress world. Alongside Josepha and the community we presented this review of how WordPress did in 2022, and vision for what’s coming:

\n\n\n\n
\n\n
\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 09 Jan 2023 23:25:18 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:4:\"Matt\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:46;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:85:\"Post Status: Support Inclusion in Tech with Winstina Hughes — Post Status Draft 136\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:32:\"https://poststatus.com/?p=146189\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:92:\"https://poststatus.com/support-inclusion-in-tech-with-winstina-hughes-post-status-draft-136/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:52731:\"

In this episode, Winstina Hughes joins Cory Miller to talk about the  Support Inclusion in Tech project created to champion diversity, equity, and inclusion in the WordPress community by providing assistance to WordCamp speakers for travel and hotels.

\n\n\n
\n\n\n\n

Estimated reading time: 59 minutes

\n
\n\n\n\n\n\n\n\n

Transcript

\n\n\n\n

Winstina Hughes  is a long-term community member and organizer within WordPress. She joins Cory Miller to discuss Support Inclusion in Tech, an effort to increase representation of minority and underrepresented speakers at WordCamp by providing needed financial support. This offers everyone in the WordPress community the chance to share their expertise and contribute resources so everyone has the opportunity to engage.

\n\n\n\n

Top Takeaways:

\n\n\n\n
    \n
  • Creativity is our common bond. WordPress is the playground where we all came to tinker and build for fun or business. It is the software magic where we discover what new things we can do each day, how to make ideas become reality, and how we might leverage what we learn to create a better world.
  • \n\n\n\n
  • Ripple effect of inclusion: When you provide the ability for a large group of people to participate, travel, and network, the impact extends beyond the WordPress community to create the bigger changes we want to see in the world. This is our community magic.
  • \n\n\n\n
  • Fifth Freedom in WordPress: We are all familiar with The Four Freedoms of WordPress. This is the 5th – full participation. Removing the financial barrier will bring us closer to the reality of being a truly inclusive community.
  • \n
\n\n\n\n
\n\n
\n\n\n\n
\n
\n

\"🙏\" Episode Partner: Gravity Forms

\n\n\n\n

Gravity Forms is a powerful form builder for WordPress and the #1 choice for businesses and web professionals across the globe. Its vast array of features, intuitive drag-and-drop form editor, and extensive ecosystem of add-ons, ensure customers can design beautiful, intelligent, and accessible forms for any project requirement.

\n
\n\n\n\n\n
\n\n\n\n

\"🔗\" Mentioned in the show:

\n\n\n\n\n\n\n\n

\"🐦\" You can follow Post Status and our guests on Twitter:

\n\n\n\n\n\n\n\n

The Post Status Draft podcast is geared toward WordPress professionals, with interviews, news, and deep analysis. \"📝\"

Browse our archives, and don’t forget to subscribe via iTunes, Google Podcasts, YouTube, Stitcher, Simplecast, or RSS. \"🎧\"

\n\n\n\n

Transcript

\n\n\n\n

Welcome to back to Post Status draft. I\'m with a good friend of mine when Winstina hughes. I met with Winstina a couple years ago in the post status community. We\'ve got to meet in person, talk numerous times, and, um, I\'m excited about what we\'re gonna be talking about here. Um, she\'s got a new, a project called support inclusion in tech.com and we\'re gonna dive into that today.

\n\n\n\n

But, uh, hi, Winstina. Hi. And pumped to finally have you on,

\n\n\n\n

Winstina Hughes: I\'m excited to be with you.

\n\n\n\n

Cory Miller: Could you tell us what you do in WordPress?

\n\n\n\n

Winstina Hughes: Okay. Um, What do I do in WordPress? every time I speak with, you know, every time I have one of these, um, you know, opportunities to speak with someone in the community, I end up like re repeating the question.

\n\n\n\n

Um, cuz it really helps me. I am a community member, um, and I\'m also, you know, a, an organizer, um, a meetup organizer and a board camp organizer. I started, um, going to [00:01:00] meetups in New York City and I transitioned into, Speaking, um, at Word Camp, New York City, and then I was invited to become a meetup organizer.

\n\n\n\n

And so, um, my, you know, my participation in the community was, um, you know, like in the early, um, you know, 2010s. And then around 2015, 2016, um, I started, you know, speaking at, at New York City, and then I became an organizer. I meet up organizer. In 2018, I led my first word camp and my only word, camp , hundred twenties, um, a budget of 120,000, a team of 18.

\n\n\n\n

Uh, it was an amazing experience. They were wonderful people and it was. Really tiring .

\n\n\n\n

Cory Miller: Yeah. You know, over the years, Winstina, I\'ve had so many dear friends that have been Word Camp organizers and really I go, oh my God, I love you so much because of what you\'re doing for the community. But I also go, I hope you [00:02:00] still like word this afterwards because it\'s a such a labor of love that I think, um, so often we don\'t really give the credit and thanks to the people, That do this voluntarily.

\n\n\n\n

Yeah, like you\'re talking about all the stuff you\'re done. So anyway, I wanna say thank you because I\'ve said it so many times to dear friends over the years going, thank you for what you\'re doing. I\'ve always shied away from it because it\'s so much work and I see all the passion and energy that you and other organizers have and I\'m really thankful cuz I think that is so critical to the entire community to have these, and now we\'re talking in 2022.

\n\n\n\n

But we hear WordCamps are back. You and I got to see each other in San Diego at Word Camp US. Yes, yes, yes. So

\n\n\n\n

Winstina Hughes: anyway, so Word, word camp Us. I was a co-organizer for Word Camp US this year. Um, and so yeah, you\'re right. Like we had a chance to teach other again there, and that was like, yay. That was awesome.

\n\n\n\n

Cory Miller: It was, yeah.[00:03:00]

\n\n\n\n

Yeah. A absolutely. Well, okay, so what drew you to, okay, how did you start with WordPress? Were you using WordPress for, uh, your own website, somebody else\'s website? How\'d you get started with the actual software?

\n\n\n\n

Winstina Hughes: So I started with WordPress in 2006, 2007. Um, I had a college course that was . Yeah, I, I had a college course.

\n\n\n\n

Um, and our professor required us to add, um, you know, the work that we\'d done, uh, into a wordpress.com blog. Um, it was a geographic information systems class. And, uh, we were looking at public health data at the census block level. Um, and so we were actually, you know, looking to see. You know, where, um, there were instances of like, um, I don\'t wanna say disease, but you know, like different illnesses.

\n\n\n\n

And so what what\'s really interesting is that you can, that schools get access to that data and you can actually like, You can [00:04:00] essentially imagine, and I don\'t wanna go too far deep into it, but imagine you have like, you know, Google Maps, right? And like when you have Google Maps open, you can do street view.

\n\n\n\n

So Google Maps lets you like go from that whole, um, like that map into like street view where you jump in as a person. So, uh, this data essentially took you away from just the geographic element, um, and the typography and like really. The census, you know, track level, like essentially, um, you know, looking at neighborhoods and, you know, the instances of disease in those neighborhoods.

\n\n\n\n

And so he, you know, he gave that to us as our final assignment. Um, you know, we did some like, uh, some heat mapping to show where there were greater concentrations of a particular type of illness, , right. Um, or, um, you know, disease or, you know, Uh, I\'m not exactly sure like what, what we [00:05:00] were calling it, but that\'s what our assignment was.

\n\n\n\n

And, uh, he asked us to, you know, take like a picture of the map and to post it in wordpress.com and that\'s how it all started with that , with that assignment. Um, so we were you.

\n\n\n\n

Cory Miller: We were using WordPress at the same time. That\'s the same year I started with WordPress when you started. I did not know you went that, that far back with WordPress.

\n\n\n\n

So I love that. Uh, yeah, I do. Thank you. And then you said like in 2010 you started actually, uh, getting involved with community events. And this is relevant to us talking about support, inclusion and tech. So what drew you to start participating in volunteering and contributing to WordPress?

\n\n\n\n

Winstina Hughes: So I went to New York City Meetups, um, and, uh, WordPress, New York City, uh, is the one that\'s in closest proximity to where I lived.

\n\n\n\n

I could just take the train in. Um, and it was, it was great. Like I, I really felt, um, that the community there was, was [00:06:00] open, like the organizers were open and, and they were welcoming. Um, Dana, rendy, uh, those were organizers at the time, Steve Bruner, who was an organizer. Was he is the organizer, , he started it and he\'s, he\'s kept it, you know, um, really like strong, like, since its inception.

\n\n\n\n

Um, and so like just going to these events and meeting these, these like wonderful generous people, these kind people, um, you know, meeting Kevin Cre, Christiana there as well. Um, and you know, just that environ. Was what led me to continue attending events. Um, and they really encouraged me to submit a talk to speak at New York City, um, ward Camp, New York City.

\n\n\n\n

And I submitted a talk to speak there and, you know, since that time I\'ve been more engaged in. Event organizing component, [00:07:00] um, or part of the community. So it moved beyond just, you know, um, Like learning, you know, to use Word Pro, you know, building sites and breaking them, uh, the best, right? Yeah. Like, that\'s the best way.

\n\n\n\n

That\'s the only way you can really learn. I mean, I, I started, you know, with different hosting plans, I\'ve had like four or five, like I have multiple domains. Like I think when you\'re in our space, you got a chance to really create. And, um, and that\'s what I was able to do and what I\'m able to continue doing, and.

\n\n\n\n

Now moved from just creating and building with WordPress to assisting with supporting, you know, our community through events like meetups and, uh, word camp organizing and supporting inclusion in tech is, is an extension of, um, of this work, this contribution that I\'ve been doing. It, it, it pieces together so many different elements that I\'ve come to, like I\'ve come to see and I\'ve come to understand [00:08:00] and. It\'s, it\'s a solution that I propose to, um, some current challenges that, um, I\'ve heard being expressed. Yeah,

\n\n\n\n

Cory Miller: I, uh, I wanna scroll back for a second and say that when you\'re talking about create, I sometimes it, for as long as you and I have been in WordPress sometimes forget that magic of being able to create something on the web or in the, in the world.

\n\n\n\n

See this cool tool called WordPress, so I appreciate that. I think that\'s what we rally around in the WordPress community and particularly to post status is helping build tools and projects and things on top of this magical thing we call WordPress. So that was a, when you said create, I was like, it\'s just little tingle of magic came up of that\'s, that\'s why we, I think that\'s our common bond in

\n\n\n\n

Winstina Hughes: WordPress.

\n\n\n\n

I agree. I agree. And I think that, uh, when we create as community members, um, and not necessarily [00:09:00] just as. Business owners or, or, um, you know, those who are providing like services. That\'s a component of creating. But, you know, in the middle of doing all that, I think, you know, I mean, I like to sit down and just literally play and see, you know, what could I do with it today?

\n\n\n\n

and, um, I entered a com competition, um, held by Sustainable New Jersey, um, right around the time I completed graduate school. And there were municipalities that were seeking, um, solutions for challenges that they had. And there was the city of East Orange and they wanted like a marketplace, um, and a place for their planning department to, you know, add their documents and also something for their green team.

\n\n\n\n

And when I saw this, I was like, I could use WordPress and e-commerce. So I created like a WooCommerce marketplace for them to sell, you know, for residents that would sell their products and services. And I demoed it. Um, and then I also had a website and also a Buddy press site. Um, and the buddy press site would be for their green team members.

\n\n\n\n

And I think that [00:10:00] like, when, when we create with WordPress, like we\'re able to like see like, you know, These asks and really apply like our knowledge of what we know the c m s can do and then provide a solution. And the city was actually really happy with the solution. Um, and I made it to the finals of the competition.

\n\n\n\n

Um, but there was another, uh, but there were other teams that that won it. Um, but it was, it was really exciting to show what WordPress, you know, software and what WooCommerce can. Uh, that\'s the

\n\n\n\n

Cory Miller: dream. Um, that, that\'s so awesome. Thank you for sharing that backstory. As much as we\'ve talked and stuff, I haven\'t got the chance to ask those questions and, um, it\'s a good reminder for me about, you know, I think if you go long enough in the community, you start to, well, I, I\'ll say I start to.

\n\n\n\n

Forget some of these nuances, [00:11:00] like being able to go, here\'s a project idea, this could be done in WordPress, you know? And that the tools are mostly freely available. Yes. And you can start and build something online.

\n\n\n\n

Winstina Hughes: Exactly. Yeah. You just, you know, download . Yeah. Yeah. Well, yeah.

\n\n\n\n

Cory Miller: So that leads me to support inclusion and tech. And you mentioned you saw a problem or problems and challenges in our community that you wanted to help make some a solution to toward it that became support, inclusion and tech. But can you talk about that a little bit? Cause I know my, my understanding and you continue to help me expand my understanding of all this is it\'s not just one particular country with DEI, it\'s a global thing. But could you talk a little bit about the problems and challenges that you saw in the space.

\n\n\n\n

Okay.

\n\n\n\n

Winstina Hughes: Absolutely. Um, absolutely. So I, you know, really wanna, like, I wanna hold true to like, um, to how [00:12:00] I, um, shared it on my website. Um, but really the backstory is that. There was a conversation that erupted on Twitter, um, about the need for more diversity on Word Camp, um, organizing teams. And this started, uh, due to, um, you know, uh, some, some thoughts that were expressed about Word Camp Europe, uh, where WordCamp Europe\'s organizing team, um, not being.

\n\n\n\n

Very reflective, um, of, you know, more ethnicities or a wider range of them. Um, it was a really difficult conversation that was happening. And my take on it really is that it\'s not where camp you\'re specific, right? Like, I mean, let\'s, you know, let\'s really step back and think about the fact that, you know, there\'s so many ethnicities around the world that have a ch [00:13:00] like it\'s really.

\n\n\n\n

When you\'re in the minority as a group, Really up to the group that\'s in the majority to weave you into those experiences and those opportunities. Um, and when that doesn\'t happen, then you have groups that don\'t have an opportunity to be, to participate and to be involved and, you know, support inclusion and tech.

\n\n\n\n

I mean, considering this was a conversation about word camps and our participation in them. Support, inclusion and tech really seeks to assist us in solving the challenge of, um, not having as much, you know, ethnic or, um, or just diverse representation within the Word camp experience. It doesn\'t seek to, you know, um, it doesn\'t, it doesn\'t seek, you know, to like solve, um, Like these, you know, the world that we [00:14:00] live in.

\n\n\n\n

And it doesn\'t seek to solve like, um, you know, diversity and inclusion outside of the WordPress space. Um, but I believe that in, in providing these, um, these opportunities within our community, since we\'re so large, that the ripple effects can extend well beyond the WordPress community. I believe that when you, Absolut.

\n\n\n\n

When you provide such a large group of people, the ability to, um, to participate in work camps, um, the ability to travel to them, the ability to network to them with, when you attend, um, the ability to like, you know, seek, um, you know, out more relationships, friendships, professional relationships. Then there\'s this ripple that extends outside of our community and I think.

\n\n\n\n

That level of empowerment can extend outside of WordPress and those ripples can assist us in diversity inclusion beyond, [00:15:00] um, you know, our, our involvement in WordPress. But you know, this, this particular solution is intended to solve the challenge that I saw, you know, um, being expressed, you know, within our community.

\n\n\n\n

And so the thought is really, Since, you know, since there\'s a take on it. And there\'s, it\'s a, I mean, it\'s an, it\'s an honest one, right? We don\'t see enough people of color. We don\'t see, um, enough, you know, people of, um, other minority groups, um, you know, uh, from other parts of the world. Um, You know, we are seeing an equal, more equal balance of, um, men and women.

\n\n\n\n

Uh, you know, but when it extends beyond that into like, you know, more representation in terms of like, you know, a wide range of religions, which ties to ethnicity often. Um, and when you\'re looking at representation in terms of those of us who, um, have like neuro [00:16:00] diversion, you know, um, you know, like, uh, characteristics and those of us who, um, you know, who we choose to love, , you know, the what society, um, you know, asks of us , right?

\n\n\n\n

Like, and um, and when we choose to hold true to that or when we\'re dealing with the physical limitations, um, that, you know, that we were born with or when we\'re in minority. Groups, you know, that have a harder time, you know, uh, receiving opportunities, um, to participate and to increase, you know, their reach and even, um, you know, the professional opportunities that are available to them.

\n\n\n\n

You know, like this. What can we do to, um, to really like solve. To solve that. Mm-hmm. And I thought, what could I do within our community Yep. To, you know, to integrate, you know, like all of those of us who are, um, Either, you know, disadvantaged [00:17:00] or not as represented into WordPress programming and support, inclusion and tech, um, seeks to, you know, take away that financial barrier, which I believe is really what, you know, can limit our participation.

\n\n\n\n

We want to participate, we want to speak, but if we can\'t afford to speak , right? I mean, if we can\'t afford to travel to the conference and if we can\'t afford a place to stay at the conference, um, then. Like, why would we even think to apply to speak at the conference? Right? Like,

\n\n\n\n

Cory Miller: yeah. That\'s, that\'s really beautiful, Winston, because, um, there\'s a couple of takeaways I, I got from this.

\n\n\n\n

Number one is, I, I\'ve always believed, um, at least in my world, that WordPress has been. The, an inclusive place, ever growing inclusive community. That\'s like a mirror to my world, the way I want my physical world here in Oklahoma to be. And I have [00:18:00] so much learned from our community leadership, uh, over the years, um, that there\'s a cons.

\n\n\n\n

Consistent push and drive from the entire community and the leadership to be truly diverse, truly inclusive in all those words. And I, I learn a lot from this. Um, so the mirror I, and I do think WordPress is, our community is so powerful cuz we\'re distributed all over the world. So if we make change in our community, in our WordPress, That should be, that should be reflected.

\n\n\n\n

And I think that\'s another, we talked about the software magic. This is the community magic. Exactly. Uh, the other thing is, I, I love and I respect because I try to take too much on that, you said, Hey, here\'s something I\'m passionate about, being an organizer, being at these community events, how special and valuable they are to you and other people instead.

\n\n\n\n

I\'m gonna make this dent first. Yeah. Like, I\'m gonna, I\'m gonna take on this aspect first. You. Beautifully and clearly [00:19:00] shared. This is the thing I\'m trying to take on in this bigger, bigger, um, change that you wanna see. We wanna see in the world. Thank you. Okay, so we\'ve got this now we\'ve got a website. Um, you\'ve got a website up to kind of share this.

\n\n\n\n

Now. Take me through, if you would, I am, pretend for a second you\'re talking to someone that is in an underrepresented, uh, in, in tech. Of, um, situation. Mm-hmm. , how\'s the process to, to get on the, Hey, I want to go to these WordCamps. I want to speak, but I do need some assistance. What does that process look like for, for support inclusion and tech?

\n\n\n\n

Winstina Hughes: So support, inclusion and tech. Um, also weaves into other initiatives in order to, to assist our speakers. Um, and so when you\'re accepted, support inclusion tech, it become, moves into the position to, to assist you once you\'re accepted into [00:20:00] Word Camp. Um, as soon as you get that, you know, acceptance, you know, go to https://supportinclusionintech.com/.

\n\n\n\n

Um, and you\'re simply just, you know, gonna put in the word camp that you were accepted in. And then there are two components, um, in addition that they\'re suggested , right? Like you\'re encouraged to do this. Um, you know, uh, we\'re in the community of consent and so, um, you have, you know, um, You\'re gonna give, you know, the consent to be included in these other initiatives, um, you\'re not gonna be forced into it.

\n\n\n\n

Uh, there\'s underrepresented in tech and there\'s also the WordPress diversity, speaker channel. Um, both of those, uh, are ways of. Further supporting diversity and inclusion and representation within the WordPress space and creating, you know, um, you know, successful opportunities for us to, um, to, you know, to put together great speaker applications and then to also, um, you know, move beyond just submitting [00:21:00] them. Um, but to being accepted.

\n\n\n\n

The, the ask is that, you know, once you\'ve been accepted to camp and you\'re starting the process of, you know, receiving funding through supporting inclusion and tech, that you also participate in those other two initiatives as well. Um, because you know, in the process of doing that, it\'s further supporting the work that we\'re doing in the WordPress community. Exactly as you said, Corey, that, you know, the word WordPress leadership already has been putting in, um, you know, the work to, you know, to assist us in resolving the challenges that face society as a whole. And so there are initiatives that currently exist and those two in particular, I think.

\n\n\n\n

You know, are ways that we can continue to support underrepresented minority groups in the WordPress community. Um, and so in the process of, you know, uh, applying for the funding, uh, you\'re encouraged to, you know, to list yourself on underrepresented tech to join the, um, the, the diversity speaker channel [00:22:00] on make WordPress.

\n\n\n\n

Um, and then once you\'ve just put on, put that information in and you\'ve identified the type of support that you\'re seeking, um, you just like, and it starts from there. Like I start, um, you know, pairing you with, you know, with a partner that you know can, can step in and provide, you know, the funding for you.

\n\n\n\n

And so, you know, they\'re gonna cover your travel and they\'re gonna cover your hotel. Um, and that way in order for you to participate, you\'re not going to be paying anything really that you know, out of pocket. For that participation, um, in that WordCamp. And that\'s really the goal. Um, the goal is to remove the financial barrier to your participation.

\n\n\n\n

Cory Miller: Yeah, that\'s fantastic. By the way, I wanted to sidebar for a second and say underrepresented in tech, uh, by Allie and Michelle Frechette. If, if you\'re listening to this and, uh, you also as a be becoming a member of underrepresented in tech, get a free [00:23:00] uh, professional membership at post status?

\n\n\n\n

Winstina Hughes: Yes. Yes. I started, I and let\'s not also forget too, that like there are other opportunities as well as Post Status has been, um, you know, looking into as ways of increasing, you know, diversity and representation within the Post Status community. Um, so underrepresented tech and that membership, and I know that there\'s some other ways that you\'re working on it too, Corey.

\n\n\n\n

Um, you know, I think, I think when we can pull all our efforts together. We have a stronger community. Um, and you are, you know, you\'re, you\'re offering that and then supporting inclusion and tech, you know, encouraging, you know, speakers to, to register and to participate in those two other programs. Strengthen all our efforts. Yes. Um, and, and that\'s, you know, that\'s the process of it. And so once you\'ve submitted, you know, your. once you submitted that form, you know, just letting me know, like the speaker registration that you\'re seeking, the [00:24:00] support. Um, you\'re also gonna complete the blind directory listing and that blind directory really.

\n\n\n\n

That Blind directory listing has the word camp that you\'ll be speaking at. Um, and it has the type of support that you\'re seeking, whether it\'s just beach travel or hotel, or both, and that\'s it. Um, no one in the community, um, you know, needs to know who you are. They don\'t need to know what your need is. Um, they don\'t need to know where you come from.

\n\n\n\n

And they don\'t need to know what makes you underrepresented and what makes you a diverse speaker. Uh, it\'s simply a way for, um, for companies that are considering sponsoring to see that the need does exist. And it\'s also a way for our community to see that the need does exist, um, and that we do have members that are seeking the support.

\n\n\n\n

Um, that, that blind directory listing is, is just a way, you know, for our community to see that, um, that our need is there. Um, yeah, and it\'s also a way of, um, uh, [00:25:00] keeping everyone up to date on the work that\'s happening.

\n\n\n\n

Cory Miller: So I know we\'ll have two asks. The first ask is if, um, you need assistance, want assistance to go to a WordCamp to be sure to go to supportinclusionintech.com?

\n\n\n\n

Winstina Hughes: Yes. Once you\'ve been accepted, go to supportinclusionintech.com. Complete the form for speaker registration, and you\'ll, um, you\'ll be paired currently, um, with four companies, uh, that, um, that have partnered to work on this.

\n\n\n\n

Cory Miller: That comes to our second ask. Yeah, that\'s right. Okay. So tell me how, um, now this is very relevant for post status because we\'re a bunch of professional and business members in our community.

\n\n\n\n

So the second ask is, we need someone, one, participants, people that need and want assistance go and speak at Word Camps. And the second part of this is the sponsors and partners. Can you tell me a little bit more, more about that and [00:26:00] how that.

\n\n\n\n

Winstina Hughes: Okay, so starting off, partners are sponsors, , um, partners are the first, um, you know, companies that expressed an interest in supporting this project.

\n\n\n\n

Um, you know, this initiative. Uh, and so like that is my way of thanking you, um, by, you know, by acknowledging. that you came into this, um, wholehearted and opened armed. And so thank you to the four companies, um, that have done this, uh, that have stepped forward to say that they will support. Um, you know, it was really exciting once the call went out, um, from Word Camp US that they were seeking, uh, support for underrepresented.

\n\n\n\n

Speakers. It was really exciting because Master wp, um, stepped in at that time, you know, to say that they thought that this was a great project. And, you know, they\'re the fourth company they joined, um, GoDaddy, Post Status, and Yoast, um, you know, the original three that said that, you know, that they would love to support this initiative.

\n\n\n\n

And so, um, now we have, [00:27:00] we have four of them and there are also several companies as well that are providing in-kind donations. Um, and, you know, they\'re doing so, makes it possible for support, inclusion and tech, um, you know, to, to function, right? Because like, you have the website and then there are all these different like plugins that make it functional and make it possible, you know, for, um, for, for it to run and function the way that we need to.

\n\n\n\n

Um, so if your company that wants to. Sponsor speakers, you know, you just have to go to the site. Um, there is a section there for you to register your support, um, your register, your desire to support. It\'ll ask you, um, you know, to provide, you know, like a contact. Um, it\'ll ask you the type of, uh, How you want to provide this support.

\n\n\n\n

Um, would you prefer to reimburse speakers for their expenses or are you, um, ready and, you know, willing and able to pay for their, um, their travel and their hotel in advance of their trip? [00:28:00] Um, so, you know, once you\'ve identified your contact, you know, your contact is the type of support that you want to provide, you know, then, you know, we\'ll have an opportunity, I\'ll have an opportunity, you know, to really. Sit down with you and for us to have a conversation about like, you know what would be your process, you know, what would make it easy or for you to be a part of this initiative? Um, this isn\'t a cookie cutter means of support for, for companies, because you\'re all different.

\n\n\n\n

Um, how GoDaddy, you know, is providing support is different from how Post Status is providing support is different from how Yoss is providing support. And it\'s different from, you know, how, um, Master WP is and, uh, When I started this, and I, you know, I, and I wrote on my blog, like, really this proposal on https://winstinahughes.com/.

\n\n\n\n

I went into it, um, you know, with the understanding, personal understanding is that it\'s gonna [00:29:00] take a couple years to understand the needs of our community and the ways, you know, companies and our ecosystem can support these needs. And in the last six months, Exactly what I, you know, anticipated, um, is what I\'ve been able to, you know, to, to see.

\n\n\n\n

And, you know, currently, um, there have been three, you know, requests, um, for, you know, to participate, you know, um, for funding, for support, for camps and, um, two unique, you know, individuals have, have made those requests. Um, and you know, so right now it\'s a question of. You know, like assisting them, you know, with the process of how, you know, our four partners, you know, can support them in that way.

\n\n\n\n

Um, and I think that answers part of your question. Um, the second part of the question is like, so how is this financial component gonna work? Right? [00:30:00] Like, are companies giving me money? No, you\'re not , like, I\'m not receiving, you know, um, any of the money. Is the financial support that you\'re providing. Um, instead it\'s looking at your company\'s processes, um, you know, your, your financial processes, your accounting processes for you to, you know, step back and think like, how could we as a company provide this level of support?

\n\n\n\n

Um, you know, it could be that you already have an existing program. Yoast already has a diversity fund. Um, and so Yoast partnering with me is a way of, um, you know, kind of bringing the need that exists to them as well. Um, and so therefore they\'re able to like further serve the community, um, you know, through those who are expressing an interest through support inclusion and tech.

\n\n\n\n

Um, the way Post Status, you know, is seeking the support speakers too, is different from Yoast. Um, and, you know, uh, [00:31:00] Yoast has a budget, um, and.

\n\n\n\n

Has their own system and their own ways of support. Um, and so they also have a budget and then Master wp, they also have a budget. And so once that budget has been met, then you know the partners essentially gray out for that year. Um, and they become active the next year. Um, and so. , that is a way of making this sustainable.

\n\n\n\n

You know, you, you pledge how much you can support, um, speakers financially, and once that has been met, then your, I mean, your capacity for the year is, is, is met. And then next year, once you\'ve reallocated your budget, or not re reallocated, but once you\'ve defined, you know, your budget, um, for the year, then you would go, you know, back into the process of supporting [00:32:00] speaker.

\n\n\n\n

Cory Miller: And I wanted to say from personal experience here, that there\'s many way, there\'s, there\'s creative ways to support these speakers, uh, to go, you know, uh, you, you talked about hotel and flights. Yeah. And, um, I, I wanna, I wanted to say that one standard to say this is not an unapproachable. Opportunity to support d uh, diversity inclusion in tech.

\n\n\n\n

Um, this is very manageable for most members at Post Status, by the way. So, you know, flight costs, uh, depending on where it is in the world. Um, I think the first question you asked me was, what\'s your budget? Yeah. And that\'s a great way. So as you come in and click sponsor, just be thinking of these things with when st for how you can.

\n\n\n\n

Help support this amazing project. Um, and that there\'s creative ways to do that. And I, I think Winstina, most members, business members at Post Status can make a meaningful contribution in this way [00:33:00] through this, your project here. And I love the fact also, I know we talked about this too, you wanted to be real careful.

\n\n\n\n

You wanna say you want the support to go to the person as best as possible. A lot of nonprofits have overhead. You have graciously generated your time and your talent to this project, and I, I, I love the way you\'ve done it too, even though I go, gosh, Winston, I love that you have this passion. Um, but thank you so much for this.

\n\n\n\n

But I know you give of your own time. For this particular project, but as you talk to Ena, if you\'re listening to this now, there\'s creative options and ENA is so good at helping you, helping understand where you\'re at, and then pair it with people that need assistance.

\n\n\n\n

Winstina Hughes: Thank you. Yes, and that is, that is the goal.

\n\n\n\n

In terms of my contribution to the WordPress community, burnout is so real and because of the fact that I work full-time outside of the WordPress space, the WordPress ecosystem, um, I\'m really [00:34:00] cognizant of the fact that I need to perform well. And at a high level , right? Uhhuh , um, at work, you know, and in my personal life.

\n\n\n\n

And WordPress fits into, um, you know, into that. And so I\'ve been able to contribute in different capacities since I was in college and. Graduate school, first attending in college, um, or post-college in graduate school, moving into speaking and organizing, um, and now working, you know, professionally maintaining, you know, organizing as a meetup organizer and a WordCamp organizer, and understanding that this can really lead to burnout.

\n\n\n\n

You know, um, my ultimate decision is, you know, that for the next two years, I\'m not gonna be a WordCamp speaker, and I\'m also not gonna be a. Organizer, you know, this, these are the ways that I can, you know, I can continue to contribute. I can contribute through support, inclusion and tech. Um, you know, but really pair, pair down all the other ways that I could burn out.

\n\n\n\n

[00:35:00] And so by maintaining, Being a New York City meetup organizer and hosting at least a minimum of six meet meetups a year, and, um, really pivoting and concentrating my energy towards support, inclusion and tech. I can sustainably contribute to the community. And so this is a perfect opportunity to really share with you, um, that, you know, I want to meet with every speaker.

\n\n\n\n

You know, that expresses the interest for support. So as you submit your, you know, your speaker registration and you join the directory listing, I will, um, you know, I\'ll ask to meet with you, for us to have a conversation, for me to understand your needs and to share. what it is I understand and I\'ve learned over time, and also how our partners seek to support.

\n\n\n\n

So we\'ll have that conversation. It\'s gonna be on the weekend. I hope you graciously incorporate that into your schedule. Um, because, you know, I, I work during the week, [00:36:00] um, and so, you know, we\'ll meet once. Uh, hopefully within a week or two of your registering as soon as possible. Especially it\'s, it\'s, it\'s ideal, uh, not ideal.

\n\n\n\n

It\'s encouraged to register as soon as possible, um, because the closer you get to your ward camp, you\'re gonna. Most likely, um, be reimbursed if you apply much sooner, like a, like two or three months in advance. You know, there are companies that will be able to, you know, cover your, your, your costs, um, of participation in advance of your trip.

\n\n\n\n

If you are reaching out like three to two. You know, to the time of, of your support that the time that you need, then you\'re looking at being reimbursed for your expenses. And so like, you know, that\'s, that\'s something to, to keep in mind when it comes to registering, you know, for this is that companies will be able to assist you with removing this.

\n\n\n\n

It just might be [00:37:00] later. When your need is expressed closer to the time that you\'re speaking that it\'s more, it\'ll be a reimbursement instead. Um, and so that\'s something to keep in mind, the timing in which you submit your interest, and also the fact that, um, you know, that we\'ll be meeting on a weekend. Um, there\'s this speaker that just registered and he wanted to meet with me.

\n\n\n\n

Um, On Christmas, he\'s in another part of the world. I mean, you know, like, so yeah. Um, and so I just, you know, I just like, I think when, and I had a con, you know, I just like responded and let him know that it\'s, it\'s Christmas for me. I\'m, you know, I\'m a Christian and I\'m so celebrating my holiday today. Um, you know, and, you know, like, uh, let\'s, let\'s meet next week.

\n\n\n\n

Um, so, you know, uh, we\'ll have like, you know, we\'ll have these conversations and we\'ll, we\'ll see. And you know how. Um, you know, how you and I can, can have that conversation and [00:38:00] meet and how your need can be met. And I\'ll also meet with, you know, companies that wanna sponsor as well. And I wanna tell you, I want you to tell me what\'s realistic for you.

\n\n\n\n

Um, I want just, just to, just to give you a sense of how some of the companies are. In fact, um, you have, uh, of the four partner. You have one partner who seeks to provide support, um, you know, within the us. Um, as of our last conversation, you know, the desire is to support minority speakers, um, specifically people of color, um, specifically, you know, black Americans, um, to improve or those of black descent to improve, um, their numbers.

\n\n\n\n

WordCamps in the US. Um, our last conversation was, you know, this is the direction that they wanna go. This is the greatest impact that they think that they can achieve. Um, and [00:39:00] I\'m, I\'m so glad that I get to listen to what everyone. Hopes to do, you know? Um, because it gives me a sense too that our community is really thinking through, like, this is how we\'re gonna solve it, right?

\n\n\n\n

Like, this is how we\'re gonna make the dent that we wanna see. So this company already knows this is how we\'re gonna make the dent that we wanna see. And there, there. Process too, is that they\'re just gonna give you a blanket amount of money and they\'re not gonna micromanage how it is you spend it. Um, they just simply ask, you know, that you, not simply, the requirement is that you put it towards your WordCamp experience and that\'s where they are with it.

\n\n\n\n

Um, there\'s, you know, another company host of course, has an established diversity fund and they have processes already in place for the support. And so you\'re simply gonna go through the existing process that, um, Yoast has established and they have a generous fund. Um, and their support, um, is something [00:40:00] that they\'ve been offering the support for a long time, and they\'re very, um, they\'re really respected , you know, for that effort.

\n\n\n\n

And, um, I\'ve had an opportunity to like, you know, to speak with someone who has been a part of their support in the past or received it and they speak so highly of, of Yoast um, and that\'s, you know, Yoast has already thought it through and they\'ve already walked through. You know, Corey and I, you and I have spoken about, you know, the budget, you know, that you\'re, that post status is set aside and, and you\'ve already shared.

\n\n\n\n

You know, what is the need? Like, we\'re not micromanaging, right? Like, let us know what type of support that you need, and we\'re just gonna provide that to you. Um, and so like you\'re, you are already thinking about like, how can we make this happen? Like, you know, if you need to, you know, it\'s a flight, you know, wherever it is.

\n\n\n\n

It doesn\'t have to be domestic, right? Like, it doesn\'t have to be in the us it could be anywhere in the world. Um, and, and that\'s, you know, that\'s like, [00:41:00] Post Status is thinking, and then GoDaddy is currently working through their process. Um, and I do believe that because of the fact that they have teams around the world that GoDaddy\'s reach will also be of, um, I think GoDaddy\'s reach will also extend beyond like the domestic, you know, like within the US and they\'ll be able to provide support as well toward camps.

\n\n\n\n

you know, around the world. I\'m anticipating it\'s possible that, um, GoDaddy\'s like impact could, you know, be especially strong with, um, Uh, WordCamps, like Word Camp Asia or Word Camp US or Word Camp Europe. Um, you know, because they\'ll have team members there and when they have team members there that can help facilitate and smooth the process over for, for those that they\'re going to be supporting, but they\'re working through their processes to make this established as well.

\n\n\n\n

And so I think [00:42:00] that, you know, just by me sharing that, you can tell that, you know, each of, you know, my partners are, are working within. Um, you know, like their business processes and their financial processes and also their vision for impact. Um, and I think that\'s really important.

\n\n\n\n

Cory Miller: So to recap, here\'s what I\'ve heard.

\n\n\n\n

So support inclusion and tech.com is the bridge between those that want have the desire to share their exper experience and expertise at word camps, but need some financial assistance to get their flights and hotel. That\'s what f support inclusion and tech.com does. Second, as a participant, as someone.

\n\n\n\n

Um, if you first need to apply and get, uh, approved to speak at work camp, then come to support inclusion and tech.com and, um, sign up, have a conversation.

\n\n\n\n

Winstina Hughes: Mm-hmm. , once you\'ve been approved.

\n\n\n\n

Cory Miller: Have a conversation with we, Tina. [00:43:00] And then third, the third recap is our ask for, um, well buzzer asked in our community.

\n\n\n\n

Uh, if you\'re looking to speak to Word Camp, go to support inclu or go apply, get approved, come to support inclusion in tech. And then second for those businesses out there. You know, you have a heart, you wanna support this. That\'s our community. That\'s who WordPress is. Uh, go to support inclusion and tech.

\n\n\n\n

Click on the sponsor link and have a conversation with ena. Think about your budget. Think about what you wanna do, uh, when Cena is so creative in helping just make these connections happen so you can really make a difference in our community. Did I get it all right?

\n\n\n\n

Winstina Hughes: You did. You did get it right And, okay.

\n\n\n\n

And I think that support inclusion tech also. It goes through vetting process as well to confirm that those who are seeking assistance, you know, to participate actually have been accepted. And that\'s why, that\'s why the steps are what they are. Um, partners aren\'t gonna [00:44:00] question, oh, is this need real? You know, that vetting is gonna happen in advance.

\n\n\n\n

So when you receive a speaker interest, You know that this is someone who has been accepted a Word camp, and they understand the process and they\'re working within, you know, your, your policies and your procedures, um, in order for them to participate. So it removes all those questions. Um, you know, so that and that, yeah, that\'s a part of it.

\n\n\n\n

Cory Miller: Well, Winston, my friend, thank you so much for this important work, uh, holding the banner up. I know this takes a lot of time. I know you\'ve got a full-time gig. I know you\'ve got a life

\n\n\n\n

Winstina Hughes: more,

\n\n\n\n

Cory Miller: um, But I so much appreciate you post. I just appreciate you, our members do for doing this vitally important work and making a difference in our world that can, like we said, can be a reflection in all these thousands of communities we go out to, to say, how can I be more inclusive?

\n\n\n\n

[00:45:00] How can I make sure everybody is represented as at least an opportunity to be represented? So I really appreciate you, Winstina, and your work and also just ringing the bell with me and teaching me and sharing, um, how we can make, make that difference. So I appreciate. Thanks for being. I\'m thanks for being on.

\n\n\n\n

Winstina Hughes: Sorry. No, no. I mean, I absolutely, like, this gives me life and it makes me wanna show up in the world, you know, different and energy. I wanna exercise more like , you know, like this is, this is, this is really in a lot of ways just like giving me energy to contribute. And so, um, to like, just to be able to like, work with you, you\'re, you\'re, you know, I\'m, I think you\'re awesome

\n\n\n\n

You know that, ditto. You\'re, you have a beautiful family. You know, like your energy is like, you have such great energy and so just a chance to work with you and like the amazing people that I\'ve had a chance to, it, it just, it gives me life and it makes me want to live more, you know? So like, let\'s, let\'s [00:46:00] see what we can do to continue to support our community so that the four freedoms, you know, I think that it\'s, , it\'s creating a fifth freedom, which is, you know, for all of us to be able to participate in a truly inclusive, um, community.

\n\n\n\n

And, you know, that speaks a lot to what the co-founders of WordPress. I think, um, you know, what, what they created and, and where they want, um, what their vision is and, you know, from their vision where we\'re, um, going and or how we\'re evolving as a community. I mean, to have 40% plus of a reach on the.

\n\n\n\n

There\'s so many people around the world that are impacted by this project, you know? So, um, yeah, I love

\n\n\n\n

Cory Miller: that. Let\'s, let\'s add the fifth Freedom. I love that win. Coined by Win, and I love that leadership vision for our community. We need it. Thank you. Thank you, ma\'am. You have a good rest of your year and we\'ll see you in the next year.

\n\n\n\n

For everybody listening, thanks for listening. Tune in, go to support inclusion in [00:47:00] tech.com, and also Winstina Hughes is in our post Slack community. So you can go at Wednesday and you can ping her and, um, get the conversation started there. So thank you, Eena.

\n\n\n\n

Winstina Hughes: Thank you. Thank you, Brent.

\n

This article was published at Post Status — the community for WordPress professionals.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 09 Jan 2023 19:17:12 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:13:\"Olivia Bisset\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:47;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:59:\"Do The Woo Community: Do the Woo is Headed to WordCamp Asia\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:28:\"https://dothewoo.io/?p=74283\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:58:\"https://dothewoo.io/do-the-woo-is-headed-to-wordcamp-asia/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:373:\"

We are looking forward to attending WordCamp Asia and also are proud to be a media partner this year.

\n

>> The post Do the Woo is Headed to WordCamp Asia appeared first on Do the Woo - a WooCommerce Builder Community .

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Mon, 09 Jan 2023 10:19:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:5:\"BobWP\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:48;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:99:\"Gutenberg Times: Gutenberg Changelog #78 -State of the Word, WordPress 6.2, Gutenberg 14.8 and 14.9\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:53:\"https://gutenbergtimes.com/?post_type=podcast&p=23141\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:114:\"https://gutenbergtimes.com/podcast/gutenberg-changelog-78-state-of-the-word-wordpress-6-2-gutenberg-14-8-and-14-9/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:41325:\"

Birgit Pauli-Haack and Hector Prieto talked State of the Word, Gutenberg releases 14.8 and 14.9, WordPress 6.2 and beyond. 

\n\n\n\n

Show Notes / Transcript

\n\n\n\n\n\n\n\n

Show Notes

\n\n\n\n

State of the Word

\n\n\n\n\n\n\n\n

Gutenberg Times Live Q & A

\n\n\n\n

Gutenberg Times Live Q & A: January 11th at 5 pm ET / 22:00 UTC Layout, Layout, Layout.

\n\n\n\n

Isabel Brison’s talk at WordCamp Asia

\n\n\n\n

WordPress and Gutenberg Releases

\n\n\n\n\n\n\n\n

Stay in Touch

\n\n\n\n
\n\n
\n\n\n\n

Transcript

\n\n\n\n

\n\n\n\n

Birgit Pauli-Haack: Hello, and welcome to the 78th episode of the Gutenberg Changelog podcast. In this first episode of 2023, I wish all our listeners a wonderful, happy, prosperous and healthy new year. In today’s episode, we will talk about Gutenberg releases 14.8, 14.9, WordPress 6.2 and beyond. I’m your host, Birgit Pauli-Haack, curator at the Gutenberg Times and WordPress developer advocate, a full-time contributor to WordPress Open Source project. My guest today is Hector Prieto, full-time contributor on the WordPress Core team, coordinating multiple WordPress and Gutenberg releases. And it’s a great pleasure to finally have you on the show, Hector. Having a conversation about Gutenberg and WordPress with you is a wonderful way for me to start this new year. Happy New Year, feliz año nuevo, Hector. How are you today?

\n\n\n\n

Hector Prieto: Happy New Year. Hi, Birgit. I’m excited to join you on the podcast. It’s my pleasure.

\n\n\n\n

Birgit Pauli-Haack: Oh, the pleasure is really all mine. Where are you right now? Did you have a great holiday break?

\n\n\n\n

Hector Prieto: I’m currently in Alicante in Spain, very close to the Mediterranean Sea. And today we have a lovely sunny winter day with nearly 20 degrees Celsius. I had a few days to recharge and spend time with the family. What about you, did you enjoy your holidays?

\n\n\n\n

Birgit Pauli-Haack: Yeah. Well, that’s some warm weather there in Alicante. I would love to have that. But here in Florida it’s balmy, too. It’s about 27 degrees, so we are in the air conditioning right now. Yes, my husband and I, we spent the week in Mexico City between Christmas and New Year’s. We saw some great art, powerful murals from the ’50s and ’70s and ’60s. And we had fantastic food and a fabulous New Year’s event. It was great, at a restaurant over the roofs of Mexico City, so we really liked it.

\n\n\n\n

Hector Prieto: Wow, sounds really nice.

\n\n\n\n

Birgit Pauli-Haack: Yeah. Well, Hector, as you are the first time on the show, maybe you can share briefly with our listeners your WordPress origin story. When did you come across WordPress the first time and what do you work on now?

\n\n\n\n

Hector Prieto: Well, my first time working with WordPress was around 2015 when I worked at the startup agency building sites. However, it wasn’t until 2020 that I first moved into the contributor space, and here we are. I am currently sponsored by Automattic to work full-time in Core in project management-related duties and supporting the development of WordPress.

\n\n\n\n

Birgit Pauli-Haack: That’s wonderful. Well, thank you. So 2015, that’s just about two years before Gutenberg was introduced into the community. Did you, at your agency, have Gutenberg on the radar already, or did you heed the call to learn JavaScript deeply?

\n\n\n\n

Hector Prieto: It wasn’t until 2018 that we started using Gutenberg for the first time, when it was first released in 5.0.

\n\n\n\n

Birgit Pauli-Haack: Yeah. Then the time between learning about WordPress and then starting contributing, that’s about five years. That’s pretty much the time that it took me to really embrace the contributing on WordPress, but I started at the Community Project in 2014. 

\n\n\n\n

Announcements

\n\n\n\n

All right, so there are a few announcements that were happening since the last podcast episode. If you haven’t watched it yet, the recording of Matt Mullenweg’s State of the Word is available on WordPress TV. The transcript and answers to the questions that didn’t make it into the recording can be read on the follow-up post, State of the Word Reflections. Josepha Haden Chomphosy kicked off the State of the Word with a reminder on the four freedoms of WordPress, that you are free to run the program, you’re free to study and change the code, you’re free to distribute your code and also redistribute WordPress.

\n\n\n\n

She also recorded a separate WP briefing with her reflections in episode 45, State of the Word Reflections in which she highlights, among other things, learn WordPress, that 12,000 students actually went through the courses and the workshops already since the inception. And she also highlighted the WordPress Playground, which is a tool to run WordPress in the browser. You don’t need a server, you don’t need a database. You can run it in the browser and test plugins and themes. I think that changes how we approach some of the discovery for WordPress. We talked about it on the show here as well, but it’s definitely something that will have so many ramifications in the WordPress space later on when it’s still very raw and very not production ready. It’s just an idea that has already a proof of concept. And then the recap posts from the community are linked in the Gutenberg Weekend Edition 239 from December 17th, and you can check it out from there.

\n\n\n\n

I also have a side note that the Pew Research Center received a shoutout for the politology quiz that they built with blocks and had one million people already taking it. Seth Rubenstein is the lead developer and was a guest on a Gutenberg Times Live Q&A last year. And he gave a great demonstration about their team’s work with the block editor, so as they went for the Gutenberg first approach building the website. The recording is available on the Gutenberg Times YouTube channel, and also we have a post here on the Gutenberg Times website as well. So as always, all these links are in the show notes of the 78th episode. So Hector, do you have any comments on this? What is your most exciting topic from the State of the Word? You had a few takeaways?

\n\n\n\n

Hector Prieto: Yeah, there were a handful of them. I would actually highlight everything, starting with WordPress Playground. It’s such amazing technology and it’s going to open so many doors. But if I had to pick something, maybe for me because it is the thing I’m the closest to, it was a great recap about the progress WordPress made in the site editing front during the last year, to the point nowadays we can create themes directly in the editor just with blocks and patterns. This brings us very close to wrapping phase two and starting exploration around phase three in 2023. So it’s great to see that all these progress.

\n\n\n\n

Birgit Pauli-Haack: Yeah, you’re right, you’re right. And it’s been such a long journey as well. I look back at some of the history on the Gutenberg Times and the Gutenberg podcasts, and we first started talking about full-site editing in January of 2020. That was even pre-pandemic, and we had quite a few developers on our live Q&A talking about the first concepts about that. So now, three years later, it’s almost finished and it’s really cool. There are still some things to be done, but I am really excited about the start of phase three of collaboration and I have been constantly trying to unify all the various tools and methods and interfaces to streamline my workflow to produce content for the web. And if I don’t have to use multiple tools to collaborate with people, I will have arrived on internet nirvana. Yeah, it’s a high calling of course, but yeah, we are all in a space where we could maybe make it happen. So I’m really excited about that.

\n\n\n\n

Hector Prieto: Yeah. Also, it’s worth noting that even when we move to phase three and we can call a wrap on phase two, phase two will not be fully finished because there’s always going to be things to do related to site editing improvements, new tools. So I can see contributors working in new features for phase three and also iterating on phase two items. Another big takeaway for me during State of the Word was seeing how much Gutenberg itself has matured. And it’s now been used in more projects such as Tumblr, bbPress, and even in some mobile apps like Day One. Also, let’s not forget how WordCamps have made a comeback after COVID hit and stopped all the in-person events. And we went from one single WordCamp in 2021 to up to 22 in the last year, in 2022. That’s amazing.

\n\n\n\n

Birgit Pauli-Haack: Yeah, that’s a nice iteration of the numbers. 22 WordCamps in ’22.

\n\n\n\n

Hector Prieto: Exactly. Especially since the community is what makes WordPress what it is, it’s the most important part of WordPress. So that’s really good to see.

\n\n\n\n

Birgit Pauli-Haack: Absolutely. Having the first WordPress in-person event in WordCamp Europe, I realized how much I missed interacting with everybody else in the community and seeing new faces and interacting with old friends. I looked up the number of WordCamps that were done in 2019, in-person WordCamps, and there were 148, or 145, something like that. So there is quite a bit of time to go between 22 to 142 or something like that.

\n\n\n\n

But it’s coming back especially because all those WordPress meetups, the local meetups, are all coming back as well. I think there was a note in the State of the Word that out of the 500, 260 have already come back to in-person events. And we know that WordPress meetups are actually the prerequisite to actually have local WordCamp organizers together to organize a WordCamp. So yeah, it’s all coming back and I’m glad that it’s coming back because of the connection that you have in the community. Yeah.

\n\n\n\n

Hector Prieto: As I mentioned earlier, I came to the contributing space in 2020. It was during the pandemic, so actually my first WordCamp was the only WordCamp in 2021. And my second WordCamp was for computer ware in last year. So it was really nice and refreshing for me to meet all the other contributors. It is something special, for sure.

\n\n\n\n

Birgit Pauli-Haack: Absolutely, yeah. It was great to meet you, Hector, although we had so many meetings with people on Zoom. Yeah.

\n\n\n\n

Hector Prieto: Fun times.

\n\n\n\n

Birgit Pauli-Haack: Yeah.

\n\n\n\n

Hector Prieto: Well, circling back to State of the Word, I would also like to point out that, last but not least, it’s really cool to see how Openverse has grown since joined WordPress about a year and a half ago. And I’m super excited to see that coming, Openverse integration in WordPress that will allow users to directly search and add images from Openverse into their WordPress site without leaving the editor at all. That’s super cool.

\n\n\n\n

Birgit Pauli-Haack: Yeah, that’s super cool. And I think it would also be really cool to have that also go back to if somebody uploads an image to WordPress and checks the check mark, also put it into Openverse. I think that part would really make it to a 360 kind of integration. I also love that there’s not only for images, but there is a lot of audio already uploaded to the Openverse that you can use on podcasts or on videos, and add free without having to think about royalties and buying for it and all that. Yeah, so free to the community.

\n\n\n\n

Hector Prieto: There’s so many possibilities there. The future is exciting.

\n\n\n\n

Birgit Pauli-Haack: Yeah, it’s really exciting. And I’m glad that it’s all happening in conjunction with WordPress. The same with the WordPress photos library, where people can just upload their photos and have it be it in the public domain and make it available to the broader community. It’s really cool.

\n\n\n\n

Hector Prieto: Yeah.

\n\n\n\n

WordPress 6.2

\n\n\n\n

Birgit Pauli-Haack: All right. So between Christmas and New Year’s, Hector, you published the release schedule proposal for 6.2. I think it was something we were all waiting for. Kind of, okay, how do we plan first quarter when we don’t know when the release is coming? So you provided. So if the release team concurs, what’s the plan? When will we see the first Beta?

\n\n\n\n

Hector Prieto: If the proposed plan is approved, the first Beta release will be on February 7th, which is 10 days before the first of our WordCamp Asia takes place.

\n\n\n\n

Birgit Pauli-Haack: Excellent. In the planning schedule, you also have a call for contributors to volunteer for the release squad. So if you, dear listeners, are inclined to take part in it and you already have a little experience in contributing, throw your hat in the ring by commenting on the release post on the scheduled proposal post. And also throw your hat in the ring also means for those who English is their second language, also means raise your hand, you want to volunteer to be part of it, and then the release team is coming together. When do you expect that you will have a final plan?

\n\n\n\n

Hector Prieto: The call for volunteers is open as we speak. Considering the end of the year vacation people are taking, contributors taking, I think we won’t have anything until end of next week or the following one. We’re leaving some extra time for people to come back from the holidays and chime in.

\n\n\n\n

Birgit Pauli-Haack: All right. Okay. Yeah, so there are only two more Gutenberg releases before the feature freeze, if I calculate that correctly. We better get started in reviewing all the great new features that are coming in, in a more consolidated way.

\n\n\n\n

Hector Prieto: Definitely. I encourage all of our listeners to start testing and giving feedback. It’s always super helpful. Also, compared to the past releases, the proposed 6.2 schedule both include a fourth Beta release compared to the previous three ones to leave some extra buffer time between WordCamp Asia and release candidate one, which will be on March 7th for a final release on March 28th.

\n\n\n\n

Birgit Pauli-Haack: Oh, okay. Yeah, so contributor day at WordCamp Asia is definitely going to be part of it and that is really cool to have. Maybe we need to organize some tables that do some testing there. I don’t know how far the work of Asia contributor day team is about that, but having that plan definitely gives us all focus on that contributor day. All right, cool. So to repeat that, final release could be March 28th, so that’s about three months from today. And we will have a 6.2 release, provided everything works out as we anticipate now.

\n\n\n\n

And I have a reminder for our listeners now for next week. The Gutenberg Times Live Q&A, Layout, Layout Layout will be happening on January 11th at 5:00 PM Eastern. That’s 22:00 UTC. And in this show, Isabel Brison, Andrew Serong, Justin Tadlock and I will discuss the opportunities and challenges for all the layout features for site builders. And we will be available for questions and answer them.

\n\n\n\n

And Isabel Brison will also give us a demo of the various layout scenarios to use. She has, with Andrew, been instrumental in building all the features into the site editor and the blocks, and it’s going to be a very interesting show. It’s also going to be a little preview on Isabel Brison’s talk at WordCamp Asia in February 2023. So join us, link us in the show notes, and don’t forget you need to register there and to be… We will have a recording, of course, with the show notes and as well as a transcript, but it’s always good to have your questions answered live by the experts on the panel, and we have some great experts there. 

\n\n\n\n

What’s Released

\n\n\n\n

So, that brings us to the latest Gutenberg releases. First, there’s Gutenberg 14.8. That was released in December 12th. Ryan Welcher was release lead and it had 167 PRs merged by 42 contributors, five of which were first contributors. So welcome to the project, first contributors. So Hector, what’s the most significant enhancement in this release?

\n\n\n\n

Hector Prieto: Well, Gutenberg 14.8, so several changes to the site editor user interface, and introduce something I’m super excited about, which is browse mode. Thanks to this first iteration of browse mode, users can switch between editing and browsing modes in the site editor, making it much easier to navigate through templates and template parts or even add new ones through the sidebar. It’s a feature that has been long awaited and it’s finally here and I’m super excited.

\n\n\n\n

Birgit Pauli-Haack: Yeah. And it helps you with where you land when you click on the site editor. You are now not landing into editing your homepage and so now you have a better entrance into the site editor. And I really like that because it gets you better settled into what you’re going to do.

\n\n\n\n

Hector Prieto: It makes for a nicer onboarding and it’s less dangerous, let’s say, because it’s much more difficult to break your design just as soon as you land on the site editor.

\n\n\n\n

Birgit Pauli-Haack: Yeah, totally. So the navigation block also had some enhancements, especially with the migration from the old menu. So if you have a location primary, it will now fall back to the navigation menu from the classic menu. That is really helpful on the transition. There are other fallback updates made that it also uses the most recently created menu from the classic theme when you start migrating to a block theme.

\n\n\n\n

Enhancements

\n\n\n\n

So that is definitely a good help for transitioning from a classic theme to a block theme. But also it kind of decreases the mental load that you don’t have to recreate all your menus when you switch out the theme, which is something that was sometimes really critical in the classic menu, in the classic theme space, where everybody had different menu locations. And so I don’t think that it’s completely solved yet, but this is definitely a first step.

\n\n\n\n

Hector Prieto: Yes, it’s a step on the right direction. We all know building menus is one of the most challenging aspects of building your site. And contributors are making a huge step for making the menu building process much easier.

\n\n\n\n

Birgit Pauli-Haack: Yeah, absolutely. Yeah. There’s also one that came with 14.8 that is for the query block. The parent block is they removed the color block support just because it was always clashing up against the other blocks that are in the query block for the post template for the title and the excerpt. You could kind of get lost in which color did we do, and where do we do that? So removing it from the wrapper query block is definitely a good choice because it removes some of that confusion of where colors are actually set.

\n\n\n\n

Hector Prieto: Exactly. Contributors have seen a few inconsistencies when adding the color to the wrapper query block, between the title, between the navigation links. So now the colors block supports are all in the inner blocks and there’s no space for confusion.

\n\n\n\n

Birgit Pauli-Haack: So what else do we see there? Yeah, I think that was it on the 14.8 release, on the highlights. There are certainly the sidebar tabs for the navigation blocks. There is great work on the experimentation that happened. So right now we have five areas of experimentation in the Gutenberg plugin and there is only two more freeze, two more releases to get them out of experimentation into the production of the Gutenberg plugin. One of them is the sidebar for the navigation block. The other one is the separated settings tab in the sidebar that separates the styles from the features. And then the others, I don’t recall right now. Hang on, I’m going to check them out. I just had it there and then I closed my browser because, I don’t know, sometimes I just randomly close browser tabs, which is a really good way to confuse myself.

\n\n\n\n

Hector Prieto: The type interface is making good progress and it’s something we would likely see out of experimental very soon.

\n\n\n\n

Birgit Pauli-Haack: Oh, yeah. And then is the global styles for custom CSS is actually in… We all wait for that, but it’s now in the experimental stage and need to be switched on through experiments menu item on the Gutenberg plugin. And then the other one is the color randomizer utility that lets you mix the current color palette randomly and change it out. That’s kind of a funky way of handling your website to do a randomized color palette, but it certainly is a proof of concept of something bigger. Was there anything else in the 14.8 you want to mention, Hector?

\n\n\n\n

Hector Prieto: Well, there are a few other main highlights that you might have seen, our listeners might have seen in the release post. One of them is super interesting, which is the custom CSS rules for your site. There’s now a tiny CSS text field where you can add your custom CSS directly in the editor. As we all know, with great power comes responsibility. So it’s nice that you can add a custom CSS directly in the editor, but let’s not overuse the important.

\n\n\n\n

Birgit Pauli-Haack: Yeah, that’s definitely a way to… But that was before, so site editors or site users or site owners who used the custom CSS piece found that that was the missing piece to actually sign on to the full site editing, because they couldn’t do those very fast changes like changing a font size somewhere or changing a space somewhere or change the color of a border very easily by just using the developer tools, identifying the marker, the selector, and then just change the color in a custom CSS. Yeah, it opens up the capabilities for that.

\n\n\n\n

You need definitely have file editing capabilities on the server and that sometimes was not available to anybody. But those who used it, they really missed it in the file site editing, in the site editing features, so that is really a good thing. And there is also a… It’s not yet released and it’s not merged yet in, but I know that Carolina Nymark is actually working on custom CSS for single blocks. And I think that’s also a good way to, in the paradigm of getting atomic design going, that that’s probably a better approach than having custom CSS being pulled in for every site page or page with the custom CSS. Rather do it per block.

\n\n\n\n

Either way, it’s kind of a interesting feature that people want to have some control or at least go back to that what they are used to do and figure out how they can change it. Well, that definitely was a changelog of 14.8. I don’t think we’ve forgotten anything. I think we, in the release post by Ryan, it was a reorganized…. Oh, the style book is definitely something that was in 4.8. We haven’t talked about it. So do you want to talk about it, what that does?

\n\n\n\n

Hector Prieto: Oh yeah, definitely. The style book is a super cool new feature, which is extension of the style site editor. The style book in a nutshell gives you an overview of all the available blocks you have in a single place so that you can easily browse all the blocks you have available and play with their design.

\n\n\n\n

Birgit Pauli-Haack: When Gutenberg first came out, there were quite a few initiatives where you could have a unit test for blocks, where I think Rich Tabor actually had a plugin and I also worked with some of our clients back then when that we had a list of all the blocks in a page and then looked at it, how the theme works with it. And that was kind of a block unit testing in design. And with the additional features that come with site editing, it was a hard time to figure out what is a change in color on the paragraph block will have additional ramification throughout the site, or when you change style variations.

\n\n\n\n

So I’m really glad that the style book, that’s a menu item in the site editor. You can go there and then see all the blocks that you have. And you get an access to the style variations of your theme so you can select them and then see how the blocks change. And that is so powerful that you don’t have this save and surprise effect anymore. You really look at it and say, “Oh yeah, I like it.” And you also see where the style variations may not be entirely working for your site because there are some things that are left out. So this is so powerful for the experience with the block editor.

\n\n\n\n

Hector Prieto: For our listeners to picture it in their mind, it’s like having a page with demo content with all the blocks. You have it registered either core blocks or third party blocks. So as soon as you install it, applying that provides blocks, all these third party blocks will appear in the style book. And you will be able to see all of them together, play with the global styles, play with the accelerations, and see how they affect all these first party and third party blocks in a single place as if you have demo content page but automatically generated for you.

\n\n\n\n

Birgit Pauli-Haack: Yeah. So it really offsets the need for these block unit testings and it’s very, very powerful. Yeah, I so agree. I think we’ve got it all now. Let’s move on to the next release, which was 14.9. And it has at the time of this recording not been released, but it will come out any hour now. For those who use the Gutenberg plugin on their sites, it’s the first Gutenberg release for 2023. 132 PRs by 46 contributors. Again, five new contributors in there. Congrats for your merge of your PR, and welcome to the project. Thank you so much for your contributions, for all of them.

\n\n\n\n

Hector Prieto: It’s refreshing to see all these new contributors, even in these more maintenance oriented releases that happen during holidays. So congratulations to you all, and welcome.

\n\n\n\n

Birgit Pauli-Haack: What are the highlights? What did you see, or what’s in the release?

\n\n\n\n

Hector Prieto: There are a few changes. They’re mostly iterative, building on top of past features and enhancements. One of them, one very cool, is a new push to global styles button that appears in the cyber blocks, which allows users to, once they edit the blocks’ style and they like it and they say, “Hey, I like this how this is looking or how this image is looking. I would like all my image blocks to look like this.” It allows them to push those styles to global styles so that they automatically affect all the blocks of that type.

\n\n\n\n

Birgit Pauli-Haack: Right. And that’s also why it’s good to have this style book handy so you can actually see if you made a mistake or something like that and said, “Oh no, I didn’t consider this, so let’s do one more time.” Yeah. So that’s a great feature. Yeah, absolutely.

\n\n\n\n

Hector Prieto: Also, for those who like building patterns, now when registering patterns, there’s a new property that allows you to specify in which template a pattern makes sense. Let’s suppose, for example, we are building a 404 pattern. Previously it would be released everywhere, so it would appear everywhere in all kinds of templates. Now you can limit it to only appear on a 404 template, so it doesn’t bring noise to other templates where it doesn’t make sense. So this is going to improve pattern discoverability in general as patterns.

\n\n\n\n

Birgit Pauli-Haack: And it also improves separation of concerns. As you said, it will not show up on every page where even if it’s not suitable for the pattern. But it also themes can then, or plugin can now create custom post types and that all, and just make those patterns available for certain custom post types. I think that is definitely a missing piece that has now been added to it. Excellent. Yeah, I’m really excited about that.

\n\n\n\n

Hector Prieto: Yes, there’s a minor update following up on Gutenberg 14.5. So we are thinking, we’re looking at two months ago, three months ago, when the list view and the document outline were merged in a single panel. We have seen there are a few improvements that can be made in the design. So now, for example, the word count has been moved to the top of the outline for more clarity.

\n\n\n\n

Birgit Pauli-Haack: Yeah, there was some confusion. Where is it now? Yeah. And then you didn’t see it at first because when you hit on update, you post and then the little notification ball totally covered that piece. So it took a while till that goes away so you see the word count and the time to read and also the block count. There were a few pieces missing. I don’t know why they’re missing, but they probably don’t seem to be very important for content creators to see. And the outline, having the other one on the list here in one it definitely makes sense to have that.

\n\n\n\n

So if you haven’t seen that yet, it was in Gutenberg 14.5. It will come to 6.2, so checking it out through the Gutenberg plugin is definitely worth trying, worth a look so your site owners or the clients are prepared to find it in a different space. What I’m also very excited about is that there is now an option to import widgets from the sidebars into template parts. And that is in the whole idea of transitioning from a classic theme to block themes or make a site be better prepared to move to a site, to full site editing block theme. This is definitely a step forward. Any additional thoughts on that?

\n\n\n\n

Hector Prieto: Oh yeah, absolutely. This is a very important milestone towards block adoption because it allows users to migrate from classic widgets to native blocks. It’s worth noting that it doesn’t work on template focus mode yet, it’s only available for the block inspector. But this is definitely a step on the right direction to increase block adoption.

\n\n\n\n

Birgit Pauli-Haack: Excellent, yeah. And George Mamadashvili, who heads… That’s his PR. He also has a nice video on how he demonstrates how it’s going to work. So I hear quite a few people celebrating this piece to make the transition over. Another one is, this is minor thing, but the configurable settings for the fluid typography in the theme JSONs now has a minimum font size, so it can be anchored on the smallest font size. So the fluidity then can increase the font size on a bigger screen. There was a hard coded value of 14 pixels before, with no way to change. And now you can have the minimum font size, like 16 pixels or 18 pixels depending on your site needs. That’s a minor thing, but I think it is something that quite a few designers were missing.

\n\n\n\n

Hector Prieto: Yes, absolutely. It’s a minor improvement, but we’ve seen lots of these minor improvements in the last, I don’t know, four or five Gutenberg releases building on top of free typography. And when you look at them altogether combined, you can see huge improvements on how the feature is becoming more and more powerful by the day.

\n\n\n\n

Birgit Pauli-Haack: Yeah, absolutely, absolutely. I think that was at 14 point… No, one thing is still really important from the release and that is the adding shadow presets support for the theme JSON. So you can do box shadows on your blocks or wrapper blocks, and that is now available for designers of themes. There is no user interface for that yet. But as we said repeatedly here on the podcast, things will be…

\n\n\n\n

Hector Prieto: It will come.

\n\n\n\n

Birgit Pauli-Haack: Hmm?

\n\n\n\n

Hector Prieto: It will come.

\n\n\n\n

Birgit Pauli-Haack: But it’s important to make it work for the theme developers first. Before you have all the added implementation for site owners that want to change it, you first need to know how it’s actually working so you can see where the pieces are that need to be surfaced in a user interface. So there is a new setting object called shadow, and then you can add different palettes to it for natural and crisp and sharp and soft shadows. And the PR has quite a few information about how that’s implemented. It gives you quite a few use cases on how you can do the shadow boxes for the buttons, for cover block, for menu block. If you have a sticky menu, then you can put a little shadow underneath it to see the difference between the page and the menu. So there are quite a few design use cases to try that out.

\n\n\n\n

Hector Prieto: I’m curious to see what designers come up with thanks to this new setting. I can see lots of 3D buttons and shadow buttons and all these cool things.

\n\n\n\n

Birgit Pauli-Haack: You could even do the outlines of the shadows, kind of, if you have an outline… Yeah, there are some great designs out there right now. So, from the changelog we are on, anything else that you wanted to talk about here that we missed? I know that Tonya Mark has updated the tracking issue for the web fonts API. And what’s merged in this release is the change of architecture to use the Core’s dependencies API with the web fonts API. And there’s a call for testing out there, or it will be out there, and making sure that how you use it. She has an update where she asked how you can help. And that is if you have an idea about naming the API, should it be webfonts, or web fonts, two words, or just the fonts API, which I tend to be the fonts API, but there is a renaming before everything gets into the Core that we’ll be name things right.

\n\n\n\n

And then the other one is a call to test the new architecture and share feedback on your testing reports and using the web fonts API. I’m not quite sure how the planning is because it seems to be still blocked furthermore through additional architectural work. Hector, do you think that that will come with 6.2, or is it now a little late for 6.2?

\n\n\n\n

Hector Prieto: Well, Tony and the other contributors are making their best to have this feature land in 6.2, so I’m pretty positive it can make it in 6.2. And the best way to ensure it can land in that version is to help with testing and with feedback. That will help unlock the architecture redesign and the renaming and everything that’s currently being discussed right now.

\n\n\n\n

Birgit Pauli-Haack: All right. Okay. Yeah, if you all are contributing to things, dear listeners, help getting that over the finish line. It definitely needs some testing. So I think that’s the end of talking about Gutenberg 14.9. We’re coming also up on the hour, so I think we can go to closing things. Are there anything that you want to point out that are on the roadmap for 6.2 that you want to have our listeners know? And if not, how can the listeners get in contact with you, where to best meet you online?

\n\n\n\n

Hector Prieto: Well, you can reach out to me in WordPress Slack. Handle is Prieto. I guess it will be written in the show notes. So please feel free to ping me there or in GitHub or in Track. I’m using the handle everywhere, so that’s easy. I would just like to circle back to the 6.2 planning and reminding everyone the call for volunteers is open. So if you’re interested in participating in the squad, you are more than welcome. We will assist you if it’s your first time. If you’re an assistant contributor, you are also welcome and we can learn from you. So everybody’s welcome, that’s the long story short.

\n\n\n\n

Birgit Pauli-Haack: Yeah. And the only thing that I want to remind you is about the next week’s Gutenberg Live Q&A with Isabel Brison, Andrew Serong and Justin Tadlock on Layout, Layout, Layout. January 11th at 5:00 PM Eastern and 20:22 UTC. That’s 10:00 PM on UTC. And as always, the show notes will be published on gutenbergtimes.com/podcast. This is episode 78. And if you have questions and suggestions or news you want us to include, send them to changelog@gutenbergtimes.com. That’s changelog@gutenbergtimes.com. So thank you so much, Hector, for joining me here for the first Changelog podcast in 2023 to spend the time on preparation as well as in the show. Thank you all for listening and goodbye and again, Happy New Year.

\n\n\n\n

Hector Prieto: Thank you for having me and see you soon. Happy New Year, everybody.

\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 07 Jan 2023 19:14:10 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:19:\"Gutenberg Changelog\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}i:49;a:6:{s:4:\"data\";s:13:\"\n \n \n \n \n \n \n\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";s:5:\"child\";a:2:{s:0:\"\";a:5:{s:5:\"title\";a:1:{i:0;a:5:{s:4:\"data\";s:121:\"Gutenberg Times: 209 Block Themes, Query Block Variations, Forms with Blocks, Block Art and more – Weekend Edition #240\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"guid\";a:1:{i:0;a:5:{s:4:\"data\";s:35:\"https://gutenbergtimes.com/?p=23042\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:4:\"link\";a:1:{i:0;a:5:{s:4:\"data\";s:124:\"https://gutenbergtimes.com/209-block-themes-query-block-variations-forms-with-blocks-block-art-and-more-weekend-edition-240/\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:11:\"description\";a:1:{i:0;a:5:{s:4:\"data\";s:27679:\"

Howdy,

\n\n\n\n

Tomorrow is the 5-year anniversary of Gutenberg Times. It feels like I just started yesterday to be fascinated by the possibilities of the block editor. For many people, it actually was just yesterday that they dipped their toes into the world of the new thing. Not you of course. You have been a wonderful subscriber and reader for a while now, and I am infinitely grateful for your support. Thank you!

\n\n\n\n

Welcome to all new subscribers this year. So glad you are here.

\n\n\n\n

Let’s dive into the sixth year together, and learn what will be next for the block editor and what other people make with it and for it. The ecosystem seems to keep expanding quite a bit with the block editor.

\n\n\n\n

Wishing you and yours a fabulous 2023. May you be prosperous, happy, and healthy!

\n\n\n\n

Yours, 💕
Birgit

\n\n\n\n

PS: Reminder: Hope to see you next week at the Gutenberg Times Live Q & A. Get your seats now for January 11, 2023, at 5pm / ET 22:00 UTC

\n\n\n\n\n\n\n\n\n\n

Developing Gutenberg and WordPress

\n\n\n\n

Hector Prieto published the WordPress 6.2 Planning Schedule Proposal, and it’s also a call for volunteers for the release squad. The 6.2 release squad will then decide on the final release schedule. For now, Feature Freeze and Beta 1 would be on February 7th, 2023. Tthere are four Beta releases planned before release candidate 1 will be available on March 7th, and a final release on March 28th, 2023.

\n\n\n\n
\n\n\n\n

Reminder: January 10, 2023, at 9:30 ET / 14:30 UTC: Hallway Hangout: Performance Considerations for Block Themes Anne McCarthy wrote: “At a high level, we’ll go through general intros (what each person does/focuses on), current work underway to address performance, what work is being done specifically for block themes, and general open Q&A. Hallway hangouts are meant to be casual and collaborative so come prepared with a kind, curious mind along with any questions or items you want to demo/discuss.”

\n\n\n\n

From the WordPress Developer Blog

\n\n\n\n

Justin Tadlock published a tutorial for building a book review grid with a Query Loop block variation. WordPress 6.1 introduced an extension to the Query Loop block, which allows plugin developers to filter existing functionality in core WordPress rather than building custom blocks to query posts. This tutorial shows how to build a WordPress plugin that display a list of book review posts including post_meta` data, using a block variation for the Query Loop and set up rendering it on the front end.

\n\n\n\n

Nick Diego tweeted: I always knew the Query Loop block was incredibly powerful, but I had never explored integrating post metadata into custom block variations! Learn how in this fantastic article by @justintadlock on the new WordPress Developer Blog.

\n\n\n\n
\n\n\n\n

Micheal Burridge composed a Roundup post to review 2022 from a block developer’s perspective in is post. You’ll find a select list of resources, to get started or to catch up on the development from the last 12 months, via the Make Blog, WordPress TV and the Learn WordPress site.

\n\n\n\n

Gutenberg plugin releases

\n\n\n\n

Gutenberg 14.8 was released on December 22, 2022, and release lead Ryan Welcher highlighted in his post What’s new in Gutenberg 14.8? (21 December)

\n\n\n\n\n\n\n\n

Sarah Gooding reported on the release as well via the WPTavern: Gutenberg 14.8 Overhauls Site Editor Interface, Adds Style Book

\n\n\n\n
\n\n\n\n

Gutenberg 14.9 is the first release of 2023, and release lead Justin Tadlock pointed out a few new features in his post What’s new in Gutenberg 14.9? (4 January):

\n\n\n\n\n\n\n\n

On the WPTavern, Sarah Gooding took the version for spin and reported on the new magic: Gutenberg 14.9’s New Magic: Push Block Changes to Global Styles

\n\n\n\n
\n\n\n\n

In the upcoming Gutenberg Changelog episode 78, Hector Prieto was my guest. He is a full-time core contributor and coordinator of multi-release WordPress and Gutenberg releases. We discussed Gutenberg 14.8 and 14.9 as well as 6.2 release schedule proposal and other topics. The episode will hit your favorite podcast app over the weekend.

\n\n\n\n\n\n\n\n
\n

🎙️ New episode: Gutenberg Changelog #78 -State of the Word, WordPress 6.2, Gutenberg 14.8 and 14.9 with Birgit Pauli-Haack and special guest Hector Prieto

\n
\n\n\n\n

Plugins, Themes, and Tools for #nocode site builders and owners

\n\n\n\n
\n\n\n\n

Sarah Gooding wrote about Block Protocol Announces New WordPress Plugin Coming in 2023 It will allow users to embed interactive blocks that are compatible with Gutenberg, and will include blocks for drawing, GitHub pull request overview, timer, calculation, and more. The plugin will also include new blocks powered by OpenAI DALL-E and GPT .

\n\n\n\n

The Block Protocol project is open source and designed to be an open protocol, and WordPress hopes to integrate more with it in the future.

\n\n\n\n
\n\n\n\n

In the latest WPTavern Jukebox podcast episode, Damon Cook, developer advocate at WPEngine, discussed with Nathan Wrigley the future of website styling in WordPress. Wrigley wrote in the introduction: ” Block-based themes are revolutionizing website styling. You’re going to be able to change any aspect of your website from the UI that you’re familiar with. The hope is that it’ll make styling more accessible to a wider audience.

\n\n\n\n

Damon talks about the fact that we’re in a period of flux right now. The documentation and tooling needed to work with website styles is maturing, but is by no means complete.”

\n\n\n\n
\n\n\n\n

Torsten Landsiedel scratched his personal itch and built the plugin Ignore block name in search, after finding that the WordPress built-in search included in the findings posts where the search keywords are in the HTML comments of blocks, and with that skews, the search result less relevant. It’s particular helpful when your blog is about working with the block editor or about content creation with WordPress. Landsiedel feels that the block editor makes the shortcomings of the built-in search feature worse because blocks contain full words, and not just HTML tags. It’s been a long-standing issue, that this plugin now solves.

\n\n\n\n
\n\n\n\n

209 Block Themes are now available in the WordPress repository with new submissions by Themeisle, sparklewpthemes, olivethemes, deothemes, sonalsinha21, Blockify, hamidxazad, WPZOOM.

\n\n\n\n\n\n\n\n

Ana Segota of Anariel Design also announced Yuna, a block theme for Nonprofits that comes with 100+ Design Patterns, you can add to your page with a simple drag and drop. Use built-in options to arrange and style them any way you want. It also includes built-in styles for the popular GiveWP donations plugin and is also ready to house your ecommerce store.

\n\n\n\n

Making Block Art

\n\n\n\n

Curious about some art behind Matt Mullenweg during State of the Word? Below are those pieces designed for the Museum of Block Art which represent the creativity that Gutenberg blocks inspire. Be sure to stop by and experience the museum’s digital interactive exhibit.

\n\n\n\n

You can see

\n\n\n\n\n\n\n\n\n\n\n\n

Anne McCarthy, instigator and curator of the Museum of Block Art, wrote an insightful blog post about how she approached making art with the Block Editor. Take a look Behind the scenes of creating art with WordPress.

\n\n\n\n

Chuck Grimmett has more examples of WP Block Art on his blog.

\n\n\n\n
\n\n\n\n

Rich Tabor and Courtney Portnoy discussed The creative side of blocks on WordPressTV. Rich Tabor walks the viewers through one of his block art creations. It’s quite inspiring to watch Tabor’s exploratory creative process using the block editor. I learned quite a few things about the power of the various color features: gradient, nested group blocks, and how to replace the theme’s primary and secondary colors for the whole site. You’ll also get an introduction to the Museum of Block Art, where Rich and other block artists showcase their creations. (also mentioned in GT 239)

\n\n\n\n

Form Plugins working with Blocks

\n\n\n\n

Two plugins emerged that take advantage of the block editor and its components and scripts so site owners and builders can use them to create forms.

\n\n\n\n

Munir Kamal, created a block integration for the popular CF7 Forms. It’s aptly names CF Blocks and available in the WordPress repository. He wrote in the description: “With CF7 Blocks, you can easily create and customize contact forms within the familiar block editor interface. No more fiddling with short codes or HTML – just drag and drop blocks to build your forms exactly how you want them.” Sounds spectacular, doesn’t it?

\n\n\n\n

In here article New CF7 Blocks Plugin Brings Blocks to Contact Form 7, Sarah Gooding, took a more in-depth look and shares her findings.

\n\n\n\n
\n\n\n\n

On Twitter, JR Tashjian developer at GoDaddy, introduced OmniForm, the next-generation Form Builder for your website. Sign up for early access now and be among the first to try it. The plan is to make the plugin available in the WordPress plugin directory at the end of January, with early access provided to users the week prior. Tashjian continues: “OmniForm embraces the block editor to the fullest extent and unlike any solution right now. The block editor is the future of editing in WordPress and building any kind of form will be no different from creating a post or page.” Tall order. Looking forward to doing some testing, too.

\n\n\n\n

Theme Development for Full Site Editing and Blocks

\n\n\n\n

Anne McCarthy has a new video up on YouTube: Building a site with WordPress 5.9 vs. WordPress 6.2 (in progress features) – To better show what’s changed with the Site Editor from when it was first introduced in WordPress 5.9, this video goes through both a demo of the original state and a brief look at what’s in place today and what’s to come, especially as 6.2 looks to wrap up much of the work around site editing/phase 2 of Gutenberg. Keep in mind that WordPress 6.2 is not out yet and much of what’s being shown is very much a work in progress with big opportunities to provide feedback along the way. Either way, I hope you enjoy taking a peak back and a look forward.

\n\n\n\n
\n\n\n\n\n

 “Keeping up with Gutenberg – Index 2022” 
A chronological list of the WordPress Make Blog posts from various teams involved in Gutenberg development: Design, Theme Review Team, Core Editor, Core JS, Core CSS, Test, and Meta team from Jan. 2021 on. Updated by yours truly. The index 2020 is here

\n\n\n\n\n

In this video tutorial, Jonathan Bossenger gives you an Introduction to theme.json. You will learn how the theme.js file works, and how you can control these settings and styles.

\n\n\n\n
\n\n\n\n

Daisy Olsen started a new Live Stream schedule and will show off Block Themes in WordPress every Friday at 10:30 am ET / 15:30 UTC on Twitch.

\n\n\n\n

The inaugural show took place Friday, January 6th, 2023 with the topic: Building a Block Theme. It’s a great opportunity to follow along with Daisy and ask questions along the way.

\n\n\n\n\n\n\n\n

Building Blocks and Tools for the Block editor.

\n\n\n\n

Munir Kamal takes you on a journey of From WordPress to the World: Intro to the Standalone Gutenberg Block Editor. In his new plugin, Kamal made the journey and found a few challenges along the way, overcame them and new put it all together for others to follow. Using the app ‘Isolated block editor, from the public repo, maintained by Automattic. Matt Mullenweg in the State of the Word emphasized that the block editor is also used outside of WordPress, with Tumblr, Day One app and with bbPress instance.

\n\n\n\n
\n\n\n\n

The team working on GiveWP went on a similar route on the revamp of the highly popular donation plugin. Post Status recently posted an article about that: The Future of GiveWP and the Block Editor

\n\n\n\n

GiveWP will hold a Town hall event about the new version on January 25th, 2023 at 10am PT / 18:00 UTC – in case someone is interested. Learn more Town Hall: GiveWP Design Mode and What’s Next for 3.0

\n\n\n\n


Kyle Johnson, JavaScript developer at GiveWP will present his talk: Using Gutenberg as a Development Foundation, Not Just a Block Builder at WordCamp Birmingham on February 4th, 2023. As far as we know, the talks will be recorded, but not livestreamed. So, they will show up on WordPress TV in the weeks after the WordCamp.

\n\n\n\n
\n\n\n\n

Mohammed Noufal of Hubspot wrote about How to Create Custom Blocks in WordPress, providing answers to the questions: why use a custom block, how to make Custom Block Templates and how to use custom blocks on your site.

\n\n\n\n
\n\n\n\n

Jonathan Bossenger‘s last section of his series: Let’s code: developing blocks without React!  is now also available on WordPress TV. Let’s code: developing blocks without React! – Review. If you followed along over the past few weeks, you would have learned to build a small WordPress block using plain (vanilla) JavaScript. In this session, we will review everything we’ve learned so far, by rebuilding the entire block from scratch.

\n\n\n\n

The other editions for the series are in order of broadcast/

\n\n\n\n\n\n\n\n\n

Need a plugin .zip from Gutenberg’s master branch?
Gutenberg Times provides daily build for testing and review.
Have you been using it? Hit reply and let me know.

\n\n\n\n

\"GitHub

\n\n\n\n\n

Upcoming WordPress events

\n\n\n\n

January 10, 2023 – 9:30 ET / 14:30 UTC
Hallway Hangout: Performance Considerations for Block Themes with Anne McCarthy

\n\n\n\n

January 11, 2023 – 5 pm ET / 22:00 UTC
Gutenberg Times Live Q & A: Layout, layout, layout
Panel discussion with Isabel Brison, Andrew Serong, Justin Tadlock and Birgit Pauli-Haack

\n\n\n\n

February 4 + 5, 2023
WordCamp Birmingham, AL

\n\n\n\n

February 17 – 19, 2023
WordCamp Asia 2023 

\n\n\n\n

Learn WordPress Online Meetups

\n\n\n\n

January 17, 2023 – 3pm / 20:00 UTC
Patterns, reusable blocks and block locking

\n\n\n\n

January 19, 2023 – 7 pm ET / 24:00 UTC
Let’s make custom templates in the Site Editor!

\n\n\n\n

January 31, 2023 – 3pm ET / 20:00 UTC
Creating a photography website with the block editor

\n\n\n\n
\n\n\n\n\n

Featured Image:

\n\n\n\n
\n\n\n\n

Don’t want to miss the next Weekend Edition?

\n\n\n\n

We hate spam, too and won’t give your email address to anyone except Mailchimp to send out our Weekend Edition

Thanks for subscribing.
\n\n\n\n
\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}s:7:\"pubDate\";a:1:{i:0;a:5:{s:4:\"data\";s:31:\"Sat, 07 Jan 2023 14:25:00 +0000\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}s:32:\"http://purl.org/dc/elements/1.1/\";a:1:{s:7:\"creator\";a:1:{i:0;a:5:{s:4:\"data\";s:18:\"Birgit Pauli-Haack\";s:7:\"attribs\";a:0:{}s:8:\"xml_base\";s:0:\"\";s:17:\"xml_base_explicit\";b:0;s:8:\"xml_lang\";s:0:\"\";}}}}}}}}}}}}}}}}s:4:\"type\";i:128;s:7:\"headers\";O:42:\"Requests_Utility_CaseInsensitiveDictionary\":1:{s:7:\"\0*\0data\";a:8:{s:6:\"server\";s:5:\"nginx\";s:4:\"date\";s:29:\"Wed, 25 Jan 2023 13:14:29 GMT\";s:12:\"content-type\";s:8:\"text/xml\";s:13:\"last-modified\";s:29:\"Wed, 25 Jan 2023 13:00:35 GMT\";s:4:\"vary\";s:15:\"Accept-Encoding\";s:15:\"x-frame-options\";s:10:\"SAMEORIGIN\";s:16:\"content-encoding\";s:4:\"gzip\";s:4:\"x-nc\";s:9:\"HIT ord 1\";}}s:5:\"build\";s:14:\"20211220193300\";}','no'),(143,'_transient_timeout_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1674695670','no'),(144,'_transient_feed_mod_d117b5738fbd35bd8c0391cda1f2b5d9','1674652470','no'),(145,'_transient_timeout_dash_v2_88ae138922fe95674369b1cb3d215a2b','1674695670','no'),(146,'_transient_dash_v2_88ae138922fe95674369b1cb3d215a2b','','no'),(147,'recently_activated','a:0:{}','yes'),(148,'can_compress_scripts','1','no'),(149,'_site_transient_update_plugins','O:8:\"stdClass\":5:{s:12:\"last_checked\";i:1674652484;s:8:\"response\";a:0:{}s:12:\"translations\";a:0:{}s:9:\"no_update\";a:2:{s:19:\"akismet/akismet.php\";O:8:\"stdClass\":10:{s:2:\"id\";s:21:\"w.org/plugins/akismet\";s:4:\"slug\";s:7:\"akismet\";s:6:\"plugin\";s:19:\"akismet/akismet.php\";s:11:\"new_version\";s:5:\"5.0.2\";s:3:\"url\";s:38:\"https://wordpress.org/plugins/akismet/\";s:7:\"package\";s:56:\"https://downloads.wordpress.org/plugin/akismet.5.0.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:60:\"https://ps.w.org/akismet/assets/icon-256x256.png?rev=2818463\";s:2:\"1x\";s:60:\"https://ps.w.org/akismet/assets/icon-128x128.png?rev=2818463\";}s:7:\"banners\";a:1:{s:2:\"1x\";s:61:\"https://ps.w.org/akismet/assets/banner-772x250.jpg?rev=479904\";}s:11:\"banners_rtl\";a:0:{}s:8:\"requires\";s:3:\"5.0\";}s:9:\"hello.php\";O:8:\"stdClass\":10:{s:2:\"id\";s:25:\"w.org/plugins/hello-dolly\";s:4:\"slug\";s:11:\"hello-dolly\";s:6:\"plugin\";s:9:\"hello.php\";s:11:\"new_version\";s:5:\"1.7.2\";s:3:\"url\";s:42:\"https://wordpress.org/plugins/hello-dolly/\";s:7:\"package\";s:60:\"https://downloads.wordpress.org/plugin/hello-dolly.1.7.2.zip\";s:5:\"icons\";a:2:{s:2:\"2x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-256x256.jpg?rev=2052855\";s:2:\"1x\";s:64:\"https://ps.w.org/hello-dolly/assets/icon-128x128.jpg?rev=2052855\";}s:7:\"banners\";a:2:{s:2:\"2x\";s:67:\"https://ps.w.org/hello-dolly/assets/banner-1544x500.jpg?rev=2645582\";s:2:\"1x\";s:66:\"https://ps.w.org/hello-dolly/assets/banner-772x250.jpg?rev=2052855\";}s:11:\"banners_rtl\";a:0:{}s:8:\"requires\";s:3:\"4.6\";}}s:7:\"checked\";a:3:{s:19:\"akismet/akismet.php\";s:5:\"5.0.2\";s:19:\"datadog/datadog.php\";s:5:\"0.0.0\";s:9:\"hello.php\";s:5:\"1.7.2\";}}','no'),(152,'theme_mods_twentytwentythree','a:1:{s:18:\"custom_css_post_id\";i:-1;}','yes'),(157,'finished_updating_comment_type','1','yes'),(171,'_transient_timeout_global_styles_twentytwentythree','1674661327','no'),(172,'_transient_global_styles_twentytwentythree','body{--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--base: #ffffff;--wp--preset--color--contrast: #000000;--wp--preset--color--primary: #9DFF20;--wp--preset--color--secondary: #345C00;--wp--preset--color--tertiary: #F6F6F6;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--duotone--dark-grayscale: url(\'#wp-duotone-dark-grayscale\');--wp--preset--duotone--grayscale: url(\'#wp-duotone-grayscale\');--wp--preset--duotone--purple-yellow: url(\'#wp-duotone-purple-yellow\');--wp--preset--duotone--blue-red: url(\'#wp-duotone-blue-red\');--wp--preset--duotone--midnight: url(\'#wp-duotone-midnight\');--wp--preset--duotone--magenta-yellow: url(\'#wp-duotone-magenta-yellow\');--wp--preset--duotone--purple-green: url(\'#wp-duotone-purple-green\');--wp--preset--duotone--blue-orange: url(\'#wp-duotone-blue-orange\');--wp--preset--font-size--small: clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 0.24), 1rem);--wp--preset--font-size--medium: clamp(1rem, 1rem + ((1vw - 0.48rem) * 0.24), 1.125rem);--wp--preset--font-size--large: clamp(1.75rem, 1.75rem + ((1vw - 0.48rem) * 0.24), 1.875rem);--wp--preset--font-size--x-large: 2.25rem;--wp--preset--font-size--xx-large: clamp(4rem, 4rem + ((1vw - 0.48rem) * 11.538), 10rem);--wp--preset--font-family--dm-sans: \"DM Sans\", sans-serif;--wp--preset--font-family--ibm-plex-mono: \'IBM Plex Mono\', monospace;--wp--preset--font-family--inter: \"Inter\", sans-serif;--wp--preset--font-family--system-font: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif;--wp--preset--font-family--source-serif-pro: \"Source Serif Pro\", serif;--wp--preset--spacing--30: clamp(1.5rem, 5vw, 2rem);--wp--preset--spacing--40: clamp(1.8rem, 1.8rem + ((1vw - 0.48rem) * 2.885), 3rem);--wp--preset--spacing--50: clamp(2.5rem, 8vw, 4.5rem);--wp--preset--spacing--60: clamp(3.75rem, 10vw, 7rem);--wp--preset--spacing--70: clamp(5rem, 5.25rem + ((1vw - 0.48rem) * 9.096), 8rem);--wp--preset--spacing--80: clamp(7rem, 14vw, 11rem);}body { margin: 0;--wp--style--global--content-size: 650px;--wp--style--global--wide-size: 1200px; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :where([class*=\"wp-block-\"]:not(.alignfull):not([class*=\"__\"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*=\"wp-block-\"]:not(.alignfull):not([class*=\"__\"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 1.5rem; }body { --wp--style--block-gap: 1.5rem; }body .is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-flow > * + *{margin-block-start: 1.5rem;margin-block-end: 0;}body .is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-constrained > * + *{margin-block-start: 1.5rem;margin-block-end: 0;}body .is-layout-flex{gap: 1.5rem;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body{background-color: var(--wp--preset--color--base);color: var(--wp--preset--color--contrast);font-family: var(--wp--preset--font-family--system-font);font-size: var(--wp--preset--font-size--medium);line-height: 1.6;--wp--style--root--padding-top: var(--wp--preset--spacing--40);--wp--style--root--padding-right: var(--wp--preset--spacing--30);--wp--style--root--padding-bottom: var(--wp--preset--spacing--40);--wp--style--root--padding-left: var(--wp--preset--spacing--30);}a:where(:not(.wp-element-button)){color: var(--wp--preset--color--contrast);text-decoration: underline;}a:where(:not(.wp-element-button)):hover{text-decoration: none;}a:where(:not(.wp-element-button)):focus{text-decoration: underline dashed;}a:where(:not(.wp-element-button)):active{color: var(--wp--preset--color--secondary);text-decoration: none;}h1, h2, h3, h4, h5, h6{font-weight: 400;line-height: 1.4;}h1{font-size: clamp(2.719rem, 2.719rem + ((1vw - 0.48rem) * 1.742), 3.625rem);line-height: 1.2;}h2{font-size: clamp(2.625rem, calc(2.625rem + ((1vw - 0.48rem) * 8.4135)), 3.25rem);line-height: 1.2;}h3{font-size: var(--wp--preset--font-size--x-large);}h4{font-size: var(--wp--preset--font-size--large);}h5{font-size: var(--wp--preset--font-size--medium);font-weight: 700;text-transform: uppercase;}h6{font-size: var(--wp--preset--font-size--medium);text-transform: uppercase;}.wp-element-button, .wp-block-button__link{background-color: var(--wp--preset--color--primary);border-radius: 0;border-width: 0;color: var(--wp--preset--color--contrast);font-family: inherit;font-size: inherit;line-height: inherit;padding: calc(0.667em + 2px) calc(1.333em + 2px);text-decoration: none;}.wp-element-button:visited, .wp-block-button__link:visited{color: var(--wp--preset--color--contrast);}.wp-element-button:hover, .wp-block-button__link:hover{background-color: var(--wp--preset--color--contrast);color: var(--wp--preset--color--base);}.wp-element-button:focus, .wp-block-button__link:focus{background-color: var(--wp--preset--color--contrast);color: var(--wp--preset--color--base);}.wp-element-button:active, .wp-block-button__link:active{background-color: var(--wp--preset--color--secondary);color: var(--wp--preset--color--base);}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-base-color{color: var(--wp--preset--color--base) !important;}.has-contrast-color{color: var(--wp--preset--color--contrast) !important;}.has-primary-color{color: var(--wp--preset--color--primary) !important;}.has-secondary-color{color: var(--wp--preset--color--secondary) !important;}.has-tertiary-color{color: var(--wp--preset--color--tertiary) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-background-color{background-color: var(--wp--preset--color--base) !important;}.has-contrast-background-color{background-color: var(--wp--preset--color--contrast) !important;}.has-primary-background-color{background-color: var(--wp--preset--color--primary) !important;}.has-secondary-background-color{background-color: var(--wp--preset--color--secondary) !important;}.has-tertiary-background-color{background-color: var(--wp--preset--color--tertiary) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-border-color{border-color: var(--wp--preset--color--base) !important;}.has-contrast-border-color{border-color: var(--wp--preset--color--contrast) !important;}.has-primary-border-color{border-color: var(--wp--preset--color--primary) !important;}.has-secondary-border-color{border-color: var(--wp--preset--color--secondary) !important;}.has-tertiary-border-color{border-color: var(--wp--preset--color--tertiary) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-dm-sans-font-family{font-family: var(--wp--preset--font-family--dm-sans) !important;}.has-ibm-plex-mono-font-family{font-family: var(--wp--preset--font-family--ibm-plex-mono) !important;}.has-inter-font-family{font-family: var(--wp--preset--font-family--inter) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-source-serif-pro-font-family{font-family: var(--wp--preset--font-family--source-serif-pro) !important;}','no'),(173,'_transient_timeout_global_styles_svg_filters_twentytwentythree','1674661327','no'),(174,'_transient_global_styles_svg_filters_twentytwentythree','','no'); /*!40000 ALTER TABLE `wp_options` ENABLE KEYS */; UNLOCK TABLES; @@ -632,7 +634,7 @@ CREATE TABLE `wp_posts` ( LOCK TABLES `wp_posts` WRITE; /*!40000 ALTER TABLE `wp_posts` DISABLE KEYS */; -INSERT INTO `wp_posts` VALUES (1,1,'2023-01-25 13:12:51','2023-01-25 13:12:51','\n

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

\n','Hello world!','','publish','open','open','','hello-world','','','2023-01-25 13:12:51','2023-01-25 13:12:51','',0,'http://localhost:9999/?p=1',0,'post','',1),(2,1,'2023-01-25 13:12:51','2023-01-25 13:12:51','\n

This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:

\n\n\n\n

Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)

\n\n\n\n

...or something like this:

\n\n\n\n

The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.

\n\n\n\n

As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!

\n','Sample Page','','publish','closed','open','','sample-page','','','2023-01-25 13:12:51','2023-01-25 13:12:51','',0,'http://localhost:9999/?page_id=2',0,'page','',0),(3,1,'2023-01-25 13:12:51','2023-01-25 13:12:51','

Who we are

Suggested text: Our website address is: http://localhost:9999.

Comments

Suggested text: When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.

An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.

Media

Suggested text: If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.

Cookies

Suggested text: If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.

If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.

When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.

If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.

Embedded content from other websites

Suggested text: Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.

These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.

Who we share your data with

Suggested text: If you request a password reset, your IP address will be included in the reset email.

How long we retain your data

Suggested text: If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.

For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.

What rights you have over your data

Suggested text: If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.

Where your data is sent

Suggested text: Visitor comments may be checked through an automated spam detection service.

','Privacy Policy','','draft','closed','open','','privacy-policy','','','2023-01-25 13:12:51','2023-01-25 13:12:51','',0,'http://localhost:9999/?page_id=3',0,'page','',0),(4,1,'2023-01-25 13:14:19','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2023-01-25 13:14:19','0000-00-00 00:00:00','',0,'http://localhost:9999/?p=4',0,'post','',0); +INSERT INTO `wp_posts` VALUES (1,1,'2023-01-25 13:12:51','2023-01-25 13:12:51','\n

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

\n','Hello world!','','publish','open','open','','hello-world','','','2023-01-25 13:12:51','2023-01-25 13:12:51','',0,'http://localhost/?p=1',0,'post','',1),(2,1,'2023-01-25 13:12:51','2023-01-25 13:12:51','\n

This is an example page. It\'s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:

\n\n\n\n

Hi there! I\'m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin\' caught in the rain.)

\n\n\n\n

...or something like this:

\n\n\n\n

The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.

\n\n\n\n

As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!

\n','Sample Page','','publish','closed','open','','sample-page','','','2023-01-25 13:12:51','2023-01-25 13:12:51','',0,'http://localhost/?page_id=2',0,'page','',0),(3,1,'2023-01-25 13:12:51','2023-01-25 13:12:51','

Who we are

Suggested text: Our website address is: http://localhost.

Comments

Suggested text: When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.

An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.

Media

Suggested text: If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.

Cookies

Suggested text: If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.

If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.

When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.

If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.

Embedded content from other websites

Suggested text: Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.

These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.

Who we share your data with

Suggested text: If you request a password reset, your IP address will be included in the reset email.

How long we retain your data

Suggested text: If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.

For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.

What rights you have over your data

Suggested text: If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.

Where your data is sent

Suggested text: Visitor comments may be checked through an automated spam detection service.

','Privacy Policy','','draft','closed','open','','privacy-policy','','','2023-01-25 13:12:51','2023-01-25 13:12:51','',0,'http://localhost/?page_id=3',0,'page','',0),(4,1,'2023-01-25 13:14:19','0000-00-00 00:00:00','','Auto Draft','','auto-draft','open','open','','','','','2023-01-25 13:14:19','0000-00-00 00:00:00','',0,'http://localhost/?p=4',0,'post','',0); /*!40000 ALTER TABLE `wp_posts` ENABLE KEYS */; UNLOCK TABLES; @@ -806,7 +808,7 @@ CREATE TABLE `wp_users` ( LOCK TABLES `wp_users` WRITE; /*!40000 ALTER TABLE `wp_users` DISABLE KEYS */; -INSERT INTO `wp_users` VALUES (1,'test','$P$Bj7U/Ktsr0VatG1e1O5qcdeQxCy25G0','test','test@gmail.com','http://localhost:9999','2023-01-25 13:12:51','',0,'test'); +INSERT INTO `wp_users` VALUES (1,'test','$P$Bj7U/Ktsr0VatG1e1O5qcdeQxCy25G0','test','test@gmail.com','http://localhost','2023-01-25 13:12:51','',0,'test'); /*!40000 ALTER TABLE `wp_users` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; diff --git a/tests/Frameworks/WordPress/Version_6_1/wp-config.php b/tests/Frameworks/WordPress/Version_6_1/wp-config.php index 75772b2dbd..d7b13fda40 100644 --- a/tests/Frameworks/WordPress/Version_6_1/wp-config.php +++ b/tests/Frameworks/WordPress/Version_6_1/wp-config.php @@ -20,7 +20,7 @@ // ** Database settings - You can get this info from your web host ** // /** The name of the database for WordPress */ -define( 'DB_NAME', 'test' ); +define( 'DB_NAME', 'wp61' ); /** Database username */ define( 'DB_USER', 'test' ); diff --git a/tests/Integration/ResponseStatusCodeTest.php b/tests/Integration/ResponseStatusCodeTest.php index defa5a635f..8ade570ae5 100644 --- a/tests/Integration/ResponseStatusCodeTest.php +++ b/tests/Integration/ResponseStatusCodeTest.php @@ -37,7 +37,7 @@ function () { [ SpanAssertion::build('web.request', 'web.request', 'web', 'GET /success')->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/success', + 'http.url' => 'http://localhost/success', 'http.status_code' => '200', ]), ] @@ -62,7 +62,7 @@ function () { SpanAssertion::build('web.request', 'web.request', 'web', 'GET /error')->withExactTags( [ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/error', + 'http.url' => 'http://localhost/error', 'http.status_code' => '500', ] )->setError(), diff --git a/tests/Integrations/AMQP/AMQPTest.php b/tests/Integrations/AMQP/V2/AMQPTest.php similarity index 98% rename from tests/Integrations/AMQP/AMQPTest.php rename to tests/Integrations/AMQP/V2/AMQPTest.php index d8819b7c9b..81d3f772c8 100644 --- a/tests/Integrations/AMQP/AMQPTest.php +++ b/tests/Integrations/AMQP/V2/AMQPTest.php @@ -1,6 +1,6 @@ inCli( - __DIR__ . '/scripts/send.php', + __DIR__ . '/../scripts/send.php', [ 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', 'DD_TRACE_CLI_ENABLED' => 'true', - ] + ], + [], + self::$autoloadPath ); list($receiveTraces, $output) = $this->inCli( - __DIR__ . '/scripts/receive.php', + __DIR__ . '/../scripts/receive.php', [ 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', 'DD_TRACE_CLI_ENABLED' => 'true', ], [], - '', + self::$autoloadPath, true ); @@ -924,24 +928,26 @@ public function testDistributedTracingIsNotPropagatedIfDisabled() self::putEnv('DD_TRACE_DEBUG_PRNG_SEED=42'); // Not necessary, but makes it easier to debug locally $sendTraces = $this->inCli( - __DIR__ . '/scripts/send.php', + __DIR__ . '/../scripts/send.php', [ 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', 'DD_TRACE_CLI_ENABLED' => 'true', 'DD_DISTRIBUTED_TRACING' => 'false' - ] + ], + [], + self::$autoloadPath ); list($receiveTraces, $output) = $this->inCli( - __DIR__ . '/scripts/receive.php', + __DIR__ . '/../scripts/receive.php', [ 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', 'DD_TRACE_CLI_ENABLED' => 'true' ], [], - '', + self::$autoloadPath, true ); diff --git a/tests/Integrations/AMQP/V2/composer.json b/tests/Integrations/AMQP/V2/composer.json new file mode 100644 index 0000000000..f141a1fdc5 --- /dev/null +++ b/tests/Integrations/AMQP/V2/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "php-amqplib/php-amqplib": "^v2.6.2" + } +} diff --git a/tests/Integrations/AMQP/V3_5/AMQPTest.php b/tests/Integrations/AMQP/V3_5/AMQPTest.php new file mode 100644 index 0000000000..34869f8498 --- /dev/null +++ b/tests/Integrations/AMQP/V3_5/AMQPTest.php @@ -0,0 +1,7 @@ +inCli(self::getConsoleScript(), [ + list($traces) = $this->inCli(static::getConsoleScript(), [ 'DD_TRACE_CLI_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', @@ -88,7 +88,7 @@ public function testThrowCommand() public function testCommand() { - list($traces) = $this->inCli(self::getConsoleScript(), [ + list($traces) = $this->inCli(static::getConsoleScript(), [ 'DD_TRACE_CLI_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', @@ -148,7 +148,7 @@ public function testCommand() public function testLongRunningCommandWithoutRootSpan() { - list($traces) = $this->inCli(self::getConsoleScript(), [ + list($traces) = $this->inCli(static::getConsoleScript(), [ 'DD_TRACE_CLI_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', diff --git a/tests/Integrations/CakePHP/V2_8/CommonScenariosTest.php b/tests/Integrations/CakePHP/V2_8/CommonScenariosTest.php index 1e84c4fa9a..e0003a9fe2 100644 --- a/tests/Integrations/CakePHP/V2_8/CommonScenariosTest.php +++ b/tests/Integrations/CakePHP/V2_8/CommonScenariosTest.php @@ -50,7 +50,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'simple', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', 'http.route' => '/:controller', Tag::SPAN_KIND => 'server', @@ -76,7 +76,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'simple_view', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', 'http.route' => '/:controller', Tag::SPAN_KIND => 'server', @@ -111,7 +111,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'error', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', 'http.route' => '/:controller', Tag::SPAN_KIND => 'server', @@ -153,7 +153,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Parameterized', 'cakephp.route.action' => 'customAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/parameterized/paramValue', + 'http.url' => 'http://localhost/parameterized/paramValue', 'http.status_code' => '200', 'http.route' => '/parameterized/:param', Tag::SPAN_KIND => 'server', diff --git a/tests/Integrations/CakePHP/V3_10/CommonScenariosTest.php b/tests/Integrations/CakePHP/V3_10/CommonScenariosTest.php index 48ae8f80d4..824be58538 100644 --- a/tests/Integrations/CakePHP/V3_10/CommonScenariosTest.php +++ b/tests/Integrations/CakePHP/V3_10/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Simple', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', @@ -77,7 +77,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Simple_view', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', @@ -112,7 +112,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Error', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', @@ -154,7 +154,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Parameterized', 'cakephp.route.action' => 'customAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/parameterized/paramValue', + 'http.url' => 'http://localhost/parameterized/paramValue', 'http.status_code' => '200', 'http.route' => '/parameterized/:param', Tag::SPAN_KIND => 'server', diff --git a/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php b/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php index 84fa30ac6d..0bf6523e75 100644 --- a/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php +++ b/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Simple', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', @@ -77,7 +77,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Simple_view', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', @@ -113,7 +113,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Error', 'cakephp.route.action' => 'index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', @@ -155,7 +155,7 @@ public function provideSpecs() 'cakephp.route.controller' => 'Parameterized', 'cakephp.route.action' => 'customAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/parameterized/paramValue', + 'http.url' => 'http://localhost/parameterized/paramValue', 'http.status_code' => '200', 'http.route' => '/parameterized/{param}', Tag::SPAN_KIND => 'server', diff --git a/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php b/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php index 649e479861..3beaef738f 100644 --- a/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php +++ b/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php @@ -50,7 +50,7 @@ public function provideSpecs() 'GET /simple' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/simple?key=value&', + Tag::HTTP_URL => 'http://localhost/simple?key=value&', Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'Simple::index', Tag::SPAN_KIND => 'server', @@ -75,7 +75,7 @@ public function provideSpecs() 'GET /simple_view' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/simple_view?key=value&', + Tag::HTTP_URL => 'http://localhost/simple_view?key=value&', Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'Simple_View::index', Tag::SPAN_KIND => 'server', @@ -109,7 +109,7 @@ public function provideSpecs() 'GET /error' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/error?key=value&', + Tag::HTTP_URL => 'http://localhost/error?key=value&', // CodeIgniter's error handler does not adjust the status code Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'Error_::index', @@ -138,7 +138,7 @@ public function provideSpecs() 'GET /parameterized/paramValue' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/parameterized/paramValue', + Tag::HTTP_URL => 'http://localhost/parameterized/paramValue', Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'Parameterized::customAction', Tag::SPAN_KIND => 'server', diff --git a/tests/Integrations/CodeIgniter/V2_2/ExitTest.php b/tests/Integrations/CodeIgniter/V2_2/ExitTest.php index c243b5c8ef..1c6d893032 100644 --- a/tests/Integrations/CodeIgniter/V2_2/ExitTest.php +++ b/tests/Integrations/CodeIgniter/V2_2/ExitTest.php @@ -38,7 +38,7 @@ public function testScenario() 'GET /exits' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/exits', + Tag::HTTP_URL => 'http://localhost/exits', Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'Exits::index', Tag::SPAN_KIND => 'server', diff --git a/tests/Integrations/CodeIgniter/V2_2/NoCI_ControllertTest.php b/tests/Integrations/CodeIgniter/V2_2/NoCI_ControllertTest.php index 7f57ba39f3..4951837ea5 100644 --- a/tests/Integrations/CodeIgniter/V2_2/NoCI_ControllertTest.php +++ b/tests/Integrations/CodeIgniter/V2_2/NoCI_ControllertTest.php @@ -38,7 +38,7 @@ public function testScenario() 'GET /health_check/ping' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/health_check/ping', + Tag::HTTP_URL => 'http://localhost/health_check/ping', Tag::HTTP_STATUS_CODE => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'codeigniter', diff --git a/tests/Integrations/Custom/Autoloaded/CommonScenariosTest.php b/tests/Integrations/Custom/Autoloaded/CommonScenariosTest.php index a226f5b8d1..0607a95a2f 100644 --- a/tests/Integrations/Custom/Autoloaded/CommonScenariosTest.php +++ b/tests/Integrations/Custom/Autoloaded/CommonScenariosTest.php @@ -47,7 +47,7 @@ public function provideSpecs() 'GET /simple' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', ]), ], @@ -59,7 +59,7 @@ public function provideSpecs() 'GET /simple_view' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', ]), ], @@ -71,7 +71,7 @@ public function provideSpecs() 'GET /error' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', ])->setError(), ], diff --git a/tests/Integrations/Custom/Autoloaded/FatalErrorTest.php b/tests/Integrations/Custom/Autoloaded/FatalErrorTest.php index 59cebaeac8..c500b44c88 100644 --- a/tests/Integrations/Custom/Autoloaded/FatalErrorTest.php +++ b/tests/Integrations/Custom/Autoloaded/FatalErrorTest.php @@ -44,7 +44,7 @@ public function testScenario() 'GET /fatal' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/fatal', + 'http.url' => 'http://localhost/fatal', 'http.status_code' => '200', ]) ->setError("E_ERROR", "Intentional E_ERROR") diff --git a/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php b/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php index f6fa461dc5..3bb94ee9b6 100644 --- a/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php +++ b/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php @@ -96,10 +96,16 @@ public function testInstrumentation() $this->call(GetSpec::create("autoloaded", "/pdo")); - $response = $this->retrieveDumpedData(function ($request) { - return (strpos($request["uri"] ?? "", "/telemetry/") === 0) - && (strpos($request["body"] ?? "", "spans_created") !== false) - ; + $found_telemetry = false; + $found_app_integrations_change = false; + $response = $this->retrieveDumpedData(function ($request) use (&$found_telemetry, &$found_app_integrations_change) { + if (strpos($request["uri"] ?? "", "/telemetry/") === 0 && strpos($request["body"] ?? "", "spans_created") !== false) { + $found_telemetry = true; + } + if (strpos($request["body"] ?? "", "app-integrations-change") !== false) { + $found_app_integrations_change = true; + } + return $found_telemetry && $found_app_integrations_change; }, true); $this->assertGreaterThanOrEqual(3, $response); diff --git a/tests/Integrations/Custom/Autoloaded/TraceSearchConfigTest.php b/tests/Integrations/Custom/Autoloaded/TraceSearchConfigTest.php index e1f3615e9f..e248b80380 100644 --- a/tests/Integrations/Custom/Autoloaded/TraceSearchConfigTest.php +++ b/tests/Integrations/Custom/Autoloaded/TraceSearchConfigTest.php @@ -40,7 +40,7 @@ public function testScenario() 'GET /simple' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', ])->withExactMetrics([ '_dd1.sr.eausr' => 0.3, diff --git a/tests/Integrations/Custom/NotAutoloaded/HttpHeadersConfiguredTest.php b/tests/Integrations/Custom/NotAutoloaded/HttpHeadersConfiguredTest.php index c50f725c4f..c33d5d5b98 100644 --- a/tests/Integrations/Custom/NotAutoloaded/HttpHeadersConfiguredTest.php +++ b/tests/Integrations/Custom/NotAutoloaded/HttpHeadersConfiguredTest.php @@ -38,7 +38,7 @@ public function testSelectedHeadersAreIncluded() $tags = [ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/', + 'http.url' => 'http://localhost/', 'http.status_code' => 200, 'http.request.headers.first-header' => 'some value: with colon', 'http.request.headers.forth-header' => '123', diff --git a/tests/Integrations/Custom/NotAutoloaded/HttpHeadersNotConfiguredTest.php b/tests/Integrations/Custom/NotAutoloaded/HttpHeadersNotConfiguredTest.php index 825a968d63..10e8133267 100644 --- a/tests/Integrations/Custom/NotAutoloaded/HttpHeadersNotConfiguredTest.php +++ b/tests/Integrations/Custom/NotAutoloaded/HttpHeadersNotConfiguredTest.php @@ -44,7 +44,7 @@ public function testSelectedHeadersAreIncluded() 'GET /' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/', + 'http.url' => 'http://localhost/', 'http.status_code' => 200, ]), ] diff --git a/tests/Integrations/Custom/NotAutoloaded/IncomingUserInfoTest.php b/tests/Integrations/Custom/NotAutoloaded/IncomingUserInfoTest.php index f704bef5f8..d8f145eaef 100644 --- a/tests/Integrations/Custom/NotAutoloaded/IncomingUserInfoTest.php +++ b/tests/Integrations/Custom/NotAutoloaded/IncomingUserInfoTest.php @@ -22,7 +22,7 @@ protected static function getEnvs() public function testSelectedHeadersAreIncluded() { $traces = $this->tracesFromWebRequest(function () { - $response = $this->sendRequest('GET', self::HOST_WITH_CREDENTIALS . ':' . self::PORT); + $response = $this->sendRequest('GET', self::HOST_WITH_CREDENTIALS); }); $this->assertFlameGraph( @@ -35,7 +35,7 @@ public function testSelectedHeadersAreIncluded() 'GET /' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/', + 'http.url' => 'http://localhost/', 'http.status_code' => 200, ]), ] diff --git a/tests/Integrations/DeferredLoading/index.php b/tests/Integrations/DeferredLoading/index.php index cfa39551ea..283e911a18 100644 --- a/tests/Integrations/DeferredLoading/index.php +++ b/tests/Integrations/DeferredLoading/index.php @@ -1,6 +1,6 @@ query("SHOW TABLES LIKE 'cache%'"); while ($table = $cacheTables->fetchColumn()) { //fwrite(STDERR, "Truncating table $table" . PHP_EOL); diff --git a/tests/Integrations/Drupal/V9_5/CommonScenariosTest.php b/tests/Integrations/Drupal/V9_5/CommonScenariosTest.php index 1334f0981e..2273f2b598 100644 --- a/tests/Integrations/Drupal/V9_5/CommonScenariosTest.php +++ b/tests/Integrations/Drupal/V9_5/CommonScenariosTest.php @@ -4,6 +4,8 @@ class CommonScenariosTest extends \DDTrace\Tests\Integrations\Drupal\V8_9\CommonScenariosTest { + public static $database = "drupal95"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Drupal/Version_9_5/index.php'; diff --git a/tests/Integrations/Elasticsearch/V1/ElasticSearchIntegrationTest.php b/tests/Integrations/Elasticsearch/V1/ElasticSearchIntegrationTest.php index 656239bdd0..355145b351 100644 --- a/tests/Integrations/Elasticsearch/V1/ElasticSearchIntegrationTest.php +++ b/tests/Integrations/Elasticsearch/V1/ElasticSearchIntegrationTest.php @@ -38,6 +38,8 @@ function array_filter_recursive(callable $keep_fn, array $input) */ class ElasticSearchIntegrationTest extends IntegrationTestCase { + protected static $lockedResource = "elasticsearch"; + const HOST2 = 'elasticsearch2_integration'; const HOST7 = 'elasticsearch7_integration'; @@ -104,7 +106,7 @@ public function testDelete() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -112,7 +114,7 @@ public function testDelete() $traces = $this->isolateTracer(function () use ($client) { $this->assertSame('array', gettype($client->delete([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', ]))); }); @@ -122,7 +124,7 @@ public function testDelete() 'Elasticsearch.Client.delete', 'elasticsearch', 'elasticsearch', - 'delete index:my_index type:my_type' + 'delete index:my_index7 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -143,7 +145,7 @@ public function testExists() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -151,7 +153,7 @@ public function testExists() $traces = $this->isolateTracer(function () use ($client) { $this->assertTrue($client->exists([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', ])); }); @@ -161,7 +163,7 @@ public function testExists() 'Elasticsearch.Client.exists', 'elasticsearch', 'elasticsearch', - 'exists index:my_index type:my_type' + 'exists index:my_index7 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -182,7 +184,7 @@ public function testExplain() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -193,7 +195,7 @@ public function testExplain() $traces = $this->isolateTracer(function () use ($client) { $this->assertArrayHasKey('explanation', $client->explain([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => [ 'query' => [ @@ -208,7 +210,7 @@ public function testExplain() 'Elasticsearch.Client.explain', 'elasticsearch', 'elasticsearch', - 'explain index:my_index type:my_type' + 'explain index:my_index7 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -233,7 +235,7 @@ public function testGet() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -241,7 +243,7 @@ public function testGet() $traces = $this->isolateTracer(function () use ($client) { $this->assertArrayHasKey('found', $client->get([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', ])); }); @@ -251,7 +253,7 @@ public function testGet() 'Elasticsearch.Client.get', 'elasticsearch', 'elasticsearch', - 'get index:my_index type:my_type' + 'get index:my_index7 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -274,7 +276,7 @@ public function testIndex() $traces = $this->isolateTracer(function () use ($client) { $response = $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -286,7 +288,7 @@ public function testIndex() 'Elasticsearch.Client.index', 'elasticsearch', 'elasticsearch', - 'index index:my_index type:my_type' + 'index index:my_index7 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -310,10 +312,10 @@ public function testLimitedTracer() { $client = $this->client(); $traces = $this->isolateLimitedTracer(function () use ($client) { - $client->indices()->delete(['index' => 'my_index']); + $client->indices()->delete(['index' => 'my_index7']); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -321,7 +323,7 @@ public function testLimitedTracer() $docs = $client->search([ 'scroll' => '1s', 'size' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -361,16 +363,16 @@ public function testLimitedTracer() public function testScroll() { $client = $this->client(); - $client->indices()->delete(['index' => 'my_index']); + $client->indices()->delete(['index' => 'my_index7']); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); $client->index([ 'id' => 2, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'second'], ]); @@ -381,7 +383,7 @@ public function testScroll() $docs = $client->search([ 'scroll' => '1s', 'size' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -456,14 +458,14 @@ public function testSearch() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); $client->indices()->flush(); $traces = $this->isolateTracer(function () use ($client) { $client->search([ - 'index' => 'my_index', + 'index' => 'my_index7', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -477,7 +479,7 @@ public function testSearch() 'Elasticsearch.Client.search', 'elasticsearch', 'elasticsearch', - 'search index:' . 'my_index' + 'search index:' . 'my_index7' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -503,14 +505,14 @@ public function testPerformRequest() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); $client->indices()->flush(); $traces = $this->isolateTracer(function () use ($client) { $client->search([ - 'index' => 'my_index', + 'index' => 'my_index7', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -520,7 +522,7 @@ public function testPerformRequest() }); $this->assertFlameGraph($traces, [ - SpanAssertion::exists('Elasticsearch.Client.search', 'search index:my_index') + SpanAssertion::exists('Elasticsearch.Client.search', 'search index:my_index7') ->withChildren([ SpanAssertion::build( 'Elasticsearch.Endpoint.performRequest', @@ -528,7 +530,7 @@ public function testPerformRequest() 'elasticsearch', 'performRequest' )->withExactTags([ - 'elasticsearch.url' => '/my_index/_search', + 'elasticsearch.url' => '/my_index7/_search', 'elasticsearch.method' => \PHP_VERSION_ID >= 70300 ? 'POST' : 'GET', 'elasticsearch.params' => '[]', 'elasticsearch.body' => '{"query":{"match_all":{}}}', @@ -553,7 +555,7 @@ public function testUpdate() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -561,7 +563,7 @@ public function testUpdate() $traces = $this->isolateTracer(function () use ($client) { $this->assertArrayHasKey('_type', $client->update([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index7', 'type' => 'my_type', 'body' => ['doc' => ['my' => 'body']], ])); @@ -572,7 +574,7 @@ public function testUpdate() 'Elasticsearch.Client.update', 'elasticsearch', 'elasticsearch', - 'update index:my_index type:my_type' + 'update index:my_index7 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' diff --git a/tests/Integrations/Elasticsearch/V1/composer.json b/tests/Integrations/Elasticsearch/V1/composer.json new file mode 100644 index 0000000000..d6c78aace2 --- /dev/null +++ b/tests/Integrations/Elasticsearch/V1/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "elasticsearch/elasticsearch": "1.2.*", + "symfony/event-dispatcher": "~2.7" + } +} diff --git a/tests/Integrations/Elasticsearch/V7/ElasticSearchIntegrationTest.php b/tests/Integrations/Elasticsearch/V7/ElasticSearchIntegrationTest.php new file mode 100644 index 0000000000..ad64598c2c --- /dev/null +++ b/tests/Integrations/Elasticsearch/V7/ElasticSearchIntegrationTest.php @@ -0,0 +1,7 @@ +client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -110,7 +112,7 @@ public function testDelete() $traces = $this->isolateTracer(function () use ($client) { $this->assertSame('deleted', $client->delete([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', ])["result"]); }); @@ -120,7 +122,7 @@ public function testDelete() 'Elasticsearch.Client.delete', 'elasticsearch', 'elasticsearch', - 'delete index:my_index type:my_type' + 'delete index:my_index8 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -138,7 +140,7 @@ public function testExists() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -146,7 +148,7 @@ public function testExists() $traces = $this->isolateTracer(function () use ($client) { $this->assertTrue($client->exists([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', ])->asBool()); }); @@ -156,7 +158,7 @@ public function testExists() 'Elasticsearch.Client.exists', 'elasticsearch', 'elasticsearch', - 'exists index:my_index type:my_type' + 'exists index:my_index8 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -173,7 +175,7 @@ public function testExplain() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -184,7 +186,7 @@ public function testExplain() $traces = $this->isolateTracer(function () use ($client) { $this->assertArrayHasKey('explanation', $client->explain([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => [ 'query' => [ @@ -199,7 +201,7 @@ public function testExplain() 'Elasticsearch.Client.explain', 'elasticsearch', 'elasticsearch', - 'explain index:my_index type:my_type' + 'explain index:my_index8 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -218,7 +220,7 @@ public function testGet() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -226,7 +228,7 @@ public function testGet() $traces = $this->isolateTracer(function () use ($client) { $this->assertArrayHasKey('found', $client->get([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', ])); }); @@ -236,7 +238,7 @@ public function testGet() 'Elasticsearch.Client.get', 'elasticsearch', 'elasticsearch', - 'get index:my_index type:my_type' + 'get index:my_index8 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -256,7 +258,7 @@ public function testIndex() $traces = $this->isolateTracer(function () use ($client) { $response = $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -268,7 +270,7 @@ public function testIndex() 'Elasticsearch.Client.index', 'elasticsearch', 'elasticsearch', - 'index index:my_index type:my_type' + 'index index:my_index8 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -286,10 +288,10 @@ public function testLimitedTracer() { $client = $this->client(); $traces = $this->isolateLimitedTracer(function () use ($client) { - $client->indices()->delete(['index' => 'my_index']); + $client->indices()->delete(['index' => 'my_index8']); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -297,7 +299,7 @@ public function testLimitedTracer() $docs = $client->search([ 'scroll' => '1s', 'size' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -337,16 +339,16 @@ public function testLimitedTracer() public function testScroll() { $client = $this->client(); - $client->indices()->delete(['index' => 'my_index']); + $client->indices()->delete(['index' => 'my_index8']); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); $client->index([ 'id' => 2, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'second'], ]); @@ -357,7 +359,7 @@ public function testScroll() $docs = $client->search([ 'scroll' => '1s', 'size' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -436,14 +438,14 @@ public function testSearch() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); $client->indices()->flush(); $traces = $this->isolateTracer(function () use ($client) { $client->search([ - 'index' => 'my_index', + 'index' => 'my_index8', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -457,7 +459,7 @@ public function testSearch() 'Elasticsearch.Client.search', 'elasticsearch', 'elasticsearch', - 'search index:' . 'my_index' + 'search index:' . 'my_index8' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' @@ -476,14 +478,14 @@ public function testPerformRequest() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); $client->indices()->flush(); $traces = $this->isolateTracer(function () use ($client) { $client->search([ - 'index' => 'my_index', + 'index' => 'my_index8', 'body' => [ 'query' => [ 'match_all' => new \stdClass(), @@ -493,7 +495,7 @@ public function testPerformRequest() }); $this->assertFlameGraph($traces, [ - SpanAssertion::exists('Elasticsearch.Client.search', 'search index:my_index') + SpanAssertion::exists('Elasticsearch.Client.search', 'search index:my_index8') ->withChildren([ SpanAssertion::exists('Elastic.Transport.Serializer.JsonSerializer.serialize', 'Elastic.Transport.Serializer.JsonSerializer.serialize'), SpanAssertion::build( @@ -502,7 +504,7 @@ public function testPerformRequest() 'elasticsearch', 'performRequest' )->withExactTags([ - 'elasticsearch.url' => '/my_index/_search', + 'elasticsearch.url' => '/my_index8/_search', 'elasticsearch.method' => 'POST', 'elasticsearch.body' => '{"query":{"match_all":{}}}', Tag::SPAN_KIND => 'client', @@ -519,7 +521,7 @@ public function testUpdate() $client = $this->client(); $client->index([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['my' => 'body'], ]); @@ -527,7 +529,7 @@ public function testUpdate() $traces = $this->isolateTracer(function () use ($client) { $this->assertArrayHasKey('_type', $client->update([ 'id' => 1, - 'index' => 'my_index', + 'index' => 'my_index8', 'type' => 'my_type', 'body' => ['doc' => ['my' => 'body']], ])); @@ -538,7 +540,7 @@ public function testUpdate() 'Elasticsearch.Client.update', 'elasticsearch', 'elasticsearch', - 'update index:my_index type:my_type' + 'update index:my_index8 type:my_type' )->withExactTags([ Tag::SPAN_KIND => 'client', Tag::COMPONENT => 'elasticsearch' diff --git a/tests/Integrations/Elasticsearch/V8/composer.json b/tests/Integrations/Elasticsearch/V8/composer.json new file mode 100644 index 0000000000..d248e533b2 --- /dev/null +++ b/tests/Integrations/Elasticsearch/V8/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "elasticsearch/elasticsearch": "~8.5" + } +} diff --git a/tests/Integrations/Frankenphp/CommonScenariosTest.php b/tests/Integrations/Frankenphp/CommonScenariosTest.php index 64bebda421..c87894fff8 100644 --- a/tests/Integrations/Frankenphp/CommonScenariosTest.php +++ b/tests/Integrations/Frankenphp/CommonScenariosTest.php @@ -46,7 +46,7 @@ public function provideSpecs() 'GET /simple' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'frankenphp' @@ -60,7 +60,7 @@ public function provideSpecs() 'GET /error' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'frankenphp' diff --git a/tests/Integrations/Guzzle/V5/composer.json b/tests/Integrations/Guzzle/V5/composer.json new file mode 100644 index 0000000000..255565722b --- /dev/null +++ b/tests/Integrations/Guzzle/V5/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "guzzlehttp/guzzle": "~5.0" + } +} diff --git a/tests/Integrations/Guzzle/V5/guzzle_in_distributed_web_request.php b/tests/Integrations/Guzzle/V5/guzzle_in_distributed_web_request.php index 6b6f1e949c..082e0b3334 100644 --- a/tests/Integrations/Guzzle/V5/guzzle_in_distributed_web_request.php +++ b/tests/Integrations/Guzzle/V5/guzzle_in_distributed_web_request.php @@ -1,6 +1,7 @@ inWebServer( function ($execute) { - $execute(GetSpec::create('GET', '/guzzle_in_web_request.php')); + $execute(GetSpec::create('GET', '/guzzle_in_web_request.php?version=' . basename(dirname(str_replace("\\", "/", static::class))))); }, __DIR__ . '/guzzle_in_web_request.php', [ diff --git a/tests/Integrations/Guzzle/V6/composer.json b/tests/Integrations/Guzzle/V6/composer.json new file mode 100644 index 0000000000..3913318017 --- /dev/null +++ b/tests/Integrations/Guzzle/V6/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "guzzlehttp/guzzle": "~6.0" + } +} diff --git a/tests/Integrations/Guzzle/V6/guzzle_in_distributed_web_request.php b/tests/Integrations/Guzzle/V6/guzzle_in_distributed_web_request.php index 7dabf8f65d..dea0e9363c 100644 --- a/tests/Integrations/Guzzle/V6/guzzle_in_distributed_web_request.php +++ b/tests/Integrations/Guzzle/V6/guzzle_in_distributed_web_request.php @@ -1,6 +1,7 @@ 'swoole_test_app', 'DD_TRACE_CLI_ENABLED' => 'true', - 'DD_TRACE_DEBUG' => 'true' + 'DD_TRACE_DEBUG' => 'true', + 'PHP_INI_SCAN_DIR' => ':' . dirname(self::getAppIndexScript()), ]); } @@ -105,7 +98,7 @@ public function testScenarioGetReturnString() 'App\Http\Controllers\CommonSpecsController@simple simple_route' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/simple?key=value&', + Tag::HTTP_URL => 'http://localhost/simple?key=value&', Tag::HTTP_ROUTE => 'simple', Tag::HTTP_STATUS_CODE => '200', Tag::SPAN_KIND => 'server', @@ -172,7 +165,7 @@ public function testScenarioGetWithView() 'App\Http\Controllers\CommonSpecsController@simple_view unnamed_route' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/simple_view?key=value&', + Tag::HTTP_URL => 'http://localhost/simple_view?key=value&', Tag::HTTP_ROUTE => 'simple_view', Tag::HTTP_STATUS_CODE => '200', Tag::SPAN_KIND => 'server', @@ -256,7 +249,7 @@ public function testScenarioGetWithException() 'App\Http\Controllers\CommonSpecsController@error unnamed_route' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/error?key=value&', + Tag::HTTP_URL => 'http://localhost/error?key=value&', Tag::HTTP_ROUTE => 'error', Tag::HTTP_STATUS_CODE => '500', Tag::SPAN_KIND => 'server', @@ -322,7 +315,7 @@ public function testScenarioGetToMissingRoute() 'GET /does_not_exist' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/does_not_exist?key=value&', + Tag::HTTP_URL => 'http://localhost/does_not_exist?key=value&', Tag::HTTP_STATUS_CODE => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'laravel', diff --git a/tests/Integrations/Laravel/V10_x/AutomatedLoginEventsTest.php b/tests/Integrations/Laravel/V10_x/AutomatedLoginEventsTest.php index 31e6fbde5c..c616c67b47 100644 --- a/tests/Integrations/Laravel/V10_x/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Laravel/V10_x/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "laravel10"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_10_x/public/index.php'; diff --git a/tests/Integrations/Laravel/V10_x/CommonScenariosTest.php b/tests/Integrations/Laravel/V10_x/CommonScenariosTest.php index 6221d289c7..d626ca2f8e 100644 --- a/tests/Integrations/Laravel/V10_x/CommonScenariosTest.php +++ b/tests/Integrations/Laravel/V10_x/CommonScenariosTest.php @@ -4,6 +4,8 @@ class CommonScenariosTest extends \DDTrace\Tests\Integrations\Laravel\V9_x\CommonScenariosTest { + public static $database = "laravel10"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_10_x/public/index.php'; diff --git a/tests/Integrations/Laravel/V4/AutomatedLoginEventsTest.php b/tests/Integrations/Laravel/V4/AutomatedLoginEventsTest.php index 6ba787cca0..cc2e76bf25 100644 --- a/tests/Integrations/Laravel/V4/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Laravel/V4/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "laravel42"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_4_2/public/index.php'; diff --git a/tests/Integrations/Laravel/V4/CommonScenariosTest.php b/tests/Integrations/Laravel/V4/CommonScenariosTest.php index 1cda1b25c6..825f249773 100644 --- a/tests/Integrations/Laravel/V4/CommonScenariosTest.php +++ b/tests/Integrations/Laravel/V4/CommonScenariosTest.php @@ -9,6 +9,8 @@ class CommonScenariosTest extends WebFrameworkTestCase { + public static $database = "laravel42"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_4_2/public/index.php'; @@ -47,7 +49,7 @@ public function provideSpecs() 'laravel.route.name' => 'simple_route', 'laravel.route.action' => 'HomeController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', 'http.route' => 'simple', 'some.key1' => 'value', @@ -136,7 +138,7 @@ public function provideSpecs() 'laravel.route.name' => 'error', 'laravel.route.action' => 'HomeController@error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', 'http.route' => 'error', 'some.key1' => 'value', @@ -184,7 +186,7 @@ public function provideSpecs() 'laravel.route.name' => 'unnamed_route', 'laravel.route.action' => 'HomeController@dynamicRoute', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/dynamic_route/dynamic01/static/dynamic02', + 'http.url' => 'http://localhost/dynamic_route/dynamic01/static/dynamic02', 'http.status_code' => '200', 'http.route' => 'dynamic_route/{param01}/static/{param02?}', 'some.key1' => 'value', diff --git a/tests/Integrations/Laravel/V4/EloquentTest.php b/tests/Integrations/Laravel/V4/EloquentTest.php index 03476d4e94..e4b9698a33 100644 --- a/tests/Integrations/Laravel/V4/EloquentTest.php +++ b/tests/Integrations/Laravel/V4/EloquentTest.php @@ -10,6 +10,8 @@ class EloquentTest extends WebFrameworkTestCase { + public static $database = "laravel42"; + use SpanAssertionTrait; protected static function getAppIndexScript() @@ -119,6 +121,6 @@ public function testDelete() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + return new \PDO('mysql:host=mysql_integration;dbname=laravel42', 'test', 'test'); } } diff --git a/tests/Integrations/Laravel/V4/PathParamsTest.php b/tests/Integrations/Laravel/V4/PathParamsTest.php index bb47e3c409..ff9b25c868 100644 --- a/tests/Integrations/Laravel/V4/PathParamsTest.php +++ b/tests/Integrations/Laravel/V4/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "laravel42"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_4_2/public/index.php'; diff --git a/tests/Integrations/Laravel/V4/TraceSearchConfigTest.php b/tests/Integrations/Laravel/V4/TraceSearchConfigTest.php index c8eb8ddc46..3139466297 100644 --- a/tests/Integrations/Laravel/V4/TraceSearchConfigTest.php +++ b/tests/Integrations/Laravel/V4/TraceSearchConfigTest.php @@ -9,6 +9,8 @@ class TraceSearchConfigTest extends WebFrameworkTestCase { + public static $database = "laravel42"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_4_2/public/index.php'; @@ -39,7 +41,7 @@ public function testScenario() 'laravel.route.name' => 'simple_route', 'laravel.route.action' => 'HomeController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', 'http.route' => 'simple', TAG::SPAN_KIND => 'server', diff --git a/tests/Integrations/Laravel/V5_7/AutomatedLoginEventsTest.php b/tests/Integrations/Laravel/V5_7/AutomatedLoginEventsTest.php index 9145056a95..32d2f3dc1e 100644 --- a/tests/Integrations/Laravel/V5_7/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Laravel/V5_7/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "laravel57"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_7/public/index.php'; diff --git a/tests/Integrations/Laravel/V5_7/CommonScenariosTest.php b/tests/Integrations/Laravel/V5_7/CommonScenariosTest.php index 2ef0269557..d638ab09ec 100644 --- a/tests/Integrations/Laravel/V5_7/CommonScenariosTest.php +++ b/tests/Integrations/Laravel/V5_7/CommonScenariosTest.php @@ -7,6 +7,8 @@ class CommonScenariosTest extends WebFrameworkTestCase { + public static $database = "laravel57"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_7/public/index.php'; diff --git a/tests/Integrations/Laravel/V5_7/EloquentTest.php b/tests/Integrations/Laravel/V5_7/EloquentTest.php index 1f5ad77bf6..6c7de63ae2 100644 --- a/tests/Integrations/Laravel/V5_7/EloquentTest.php +++ b/tests/Integrations/Laravel/V5_7/EloquentTest.php @@ -10,6 +10,8 @@ class EloquentTest extends WebFrameworkTestCase { + public static $database = "laravel57"; + use SpanAssertionTrait; protected static function getAppIndexScript() @@ -138,6 +140,6 @@ public function testDelete() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + return new \PDO('mysql:host=mysql_integration;dbname=laravel57', 'test', 'test'); } } diff --git a/tests/Integrations/Laravel/V5_7/PathParamsTest.php b/tests/Integrations/Laravel/V5_7/PathParamsTest.php index 7c1eb922ab..414cdf9eab 100644 --- a/tests/Integrations/Laravel/V5_7/PathParamsTest.php +++ b/tests/Integrations/Laravel/V5_7/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "laravel57"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_7/public/index.php'; diff --git a/tests/Integrations/Laravel/V5_7/PipelineTracingTest.php b/tests/Integrations/Laravel/V5_7/PipelineTracingTest.php index 6781709b0d..40eb25037b 100644 --- a/tests/Integrations/Laravel/V5_7/PipelineTracingTest.php +++ b/tests/Integrations/Laravel/V5_7/PipelineTracingTest.php @@ -4,15 +4,12 @@ use DDTrace\Tag; use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\SpanAssertionTrait; -use DDTrace\Tests\Common\TracerTestTrait; use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; class PipelineTracingTest extends WebFrameworkTestCase { - use TracerTestTrait; - use SpanAssertionTrait; + public static $database = "laravel57"; protected static function getAppIndexScript() { diff --git a/tests/Integrations/Laravel/V5_7/QueueTest.php b/tests/Integrations/Laravel/V5_7/QueueTest.php index c3c83d03f1..d0c5314012 100644 --- a/tests/Integrations/Laravel/V5_7/QueueTest.php +++ b/tests/Integrations/Laravel/V5_7/QueueTest.php @@ -4,6 +4,8 @@ class QueueTest extends \DDTrace\Tests\Integrations\Laravel\V5_8\QueueTest { + public static $database = "laravel57"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_7/public/index.php'; diff --git a/tests/Integrations/Laravel/V5_7/TraceSearchConfigTest.php b/tests/Integrations/Laravel/V5_7/TraceSearchConfigTest.php index d0da6140c1..db975b80f6 100644 --- a/tests/Integrations/Laravel/V5_7/TraceSearchConfigTest.php +++ b/tests/Integrations/Laravel/V5_7/TraceSearchConfigTest.php @@ -9,6 +9,8 @@ class TraceSearchConfigTest extends WebFrameworkTestCase { + public static $database = "laravel57"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_7/public/index.php'; @@ -44,7 +46,7 @@ public function testScenario() 'laravel.route.name' => 'simple_route', 'laravel.route.action' => 'App\Http\Controllers\CommonSpecsController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', 'http.route' => 'simple', TAG::SPAN_KIND => 'server', diff --git a/tests/Integrations/Laravel/V5_8/AutomatedLoginEventsTest.php b/tests/Integrations/Laravel/V5_8/AutomatedLoginEventsTest.php index 2753cbf9bf..0d8ea732e6 100644 --- a/tests/Integrations/Laravel/V5_8/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Laravel/V5_8/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "laravel58"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_8/public/index.php'; diff --git a/tests/Integrations/Laravel/V5_8/CommonScenariosTest.php b/tests/Integrations/Laravel/V5_8/CommonScenariosTest.php index 9efb7c6386..042c7c42a3 100644 --- a/tests/Integrations/Laravel/V5_8/CommonScenariosTest.php +++ b/tests/Integrations/Laravel/V5_8/CommonScenariosTest.php @@ -2,13 +2,10 @@ namespace DDTrace\Tests\Integrations\Laravel\V5_8; -use DDTrace\Tag; -use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\WebFrameworkTestCase; -use DDTrace\Tests\Frameworks\Util\Request\RequestSpec; - class CommonScenariosTest extends \DDTrace\Tests\Integrations\Laravel\V5_7\CommonScenariosTest { + public static $database = "laravel58"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_8/public/index.php'; diff --git a/tests/Integrations/Laravel/V5_8/EloquentTest.php b/tests/Integrations/Laravel/V5_8/EloquentTest.php index d3105283cb..47a2799c02 100644 --- a/tests/Integrations/Laravel/V5_8/EloquentTest.php +++ b/tests/Integrations/Laravel/V5_8/EloquentTest.php @@ -4,15 +4,12 @@ use DDTrace\Tag; use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\SpanAssertionTrait; -use DDTrace\Tests\Common\TracerTestTrait; use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; class EloquentTest extends WebFrameworkTestCase { - use TracerTestTrait; - use SpanAssertionTrait; + public static $database = "laravel58"; protected static function getAppIndexScript() { @@ -140,6 +137,6 @@ public function testDelete() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + return new \PDO('mysql:host=mysql_integration;dbname=laravel58', 'test', 'test'); } } diff --git a/tests/Integrations/Laravel/V5_8/PathParamsTest.php b/tests/Integrations/Laravel/V5_8/PathParamsTest.php index 419ee976eb..358d2a7453 100644 --- a/tests/Integrations/Laravel/V5_8/PathParamsTest.php +++ b/tests/Integrations/Laravel/V5_8/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "laravel58"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_8/public/index.php'; diff --git a/tests/Integrations/Laravel/V5_8/QueueTest.php b/tests/Integrations/Laravel/V5_8/QueueTest.php index 46da9e80e6..b09b790716 100644 --- a/tests/Integrations/Laravel/V5_8/QueueTest.php +++ b/tests/Integrations/Laravel/V5_8/QueueTest.php @@ -4,15 +4,12 @@ use DDTrace\Tag; use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\SpanAssertionTrait; -use DDTrace\Tests\Common\TracerTestTrait; use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; class QueueTest extends WebFrameworkTestCase { - use TracerTestTrait; - use SpanAssertionTrait; + public static $database = "laravel58"; protected static function getAppIndexScript() { @@ -234,7 +231,7 @@ protected function resetQueue() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + return new \PDO('mysql:host=mysql_integration;dbname=' . static::$database, 'test', 'test'); } protected function spanEventJobProcessing() diff --git a/tests/Integrations/Laravel/V5_8/TraceSearchConfigTest.php b/tests/Integrations/Laravel/V5_8/TraceSearchConfigTest.php index 13582b3645..a3bfa259e9 100644 --- a/tests/Integrations/Laravel/V5_8/TraceSearchConfigTest.php +++ b/tests/Integrations/Laravel/V5_8/TraceSearchConfigTest.php @@ -9,6 +9,8 @@ class TraceSearchConfigTest extends WebFrameworkTestCase { + public static $database = "laravel58"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_5_8/public/index.php'; @@ -44,7 +46,7 @@ public function testScenario() 'laravel.route.name' => 'simple_route', 'laravel.route.action' => 'App\Http\Controllers\CommonSpecsController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', 'http.route' => 'simple', TAG::SPAN_KIND => 'server', diff --git a/tests/Integrations/Laravel/V8_x/AutomatedLoginEventsTest.php b/tests/Integrations/Laravel/V8_x/AutomatedLoginEventsTest.php index 580018f9c7..7d5db973ca 100644 --- a/tests/Integrations/Laravel/V8_x/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Laravel/V8_x/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "laravel8"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_8_x/public/index.php'; diff --git a/tests/Integrations/Laravel/V8_x/CommonScenariosTest.php b/tests/Integrations/Laravel/V8_x/CommonScenariosTest.php index 25c224ad23..10c8cb17cd 100644 --- a/tests/Integrations/Laravel/V8_x/CommonScenariosTest.php +++ b/tests/Integrations/Laravel/V8_x/CommonScenariosTest.php @@ -10,6 +10,8 @@ class CommonScenariosTest extends \DDTrace\Tests\Integrations\Laravel\V5_7\CommonScenariosTest { + public static $database = "laravel8"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_8_x/public/index.php'; diff --git a/tests/Integrations/Laravel/V8_x/EloquentTest.php b/tests/Integrations/Laravel/V8_x/EloquentTest.php index 0e6098b647..e6462ff79c 100644 --- a/tests/Integrations/Laravel/V8_x/EloquentTest.php +++ b/tests/Integrations/Laravel/V8_x/EloquentTest.php @@ -4,15 +4,12 @@ use DDTrace\Tag; use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\SpanAssertionTrait; -use DDTrace\Tests\Common\TracerTestTrait; use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; class EloquentTest extends WebFrameworkTestCase { - use TracerTestTrait; - use SpanAssertionTrait; + public static $database = "laravel8"; protected static function getAppIndexScript() { @@ -140,6 +137,6 @@ public function testDelete() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + return new \PDO('mysql:host=mysql_integration;dbname=laravel8', 'test', 'test'); } } diff --git a/tests/Integrations/Laravel/V8_x/HttpHideRouteTest.php b/tests/Integrations/Laravel/V8_x/HttpHideRouteTest.php index 1a1cfa645b..e629b94e0f 100644 --- a/tests/Integrations/Laravel/V8_x/HttpHideRouteTest.php +++ b/tests/Integrations/Laravel/V8_x/HttpHideRouteTest.php @@ -10,6 +10,8 @@ class HttpHideRouteTest extends WebFrameworkTestCase { + public static $database = "laravel8"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_8_x/public/index.php'; @@ -35,4 +37,4 @@ public function testDefaultPath() { } } } -} \ No newline at end of file +} diff --git a/tests/Integrations/Laravel/V8_x/InternalExceptionsTest.php b/tests/Integrations/Laravel/V8_x/InternalExceptionsTest.php index e393fde9e2..b27527c8b6 100644 --- a/tests/Integrations/Laravel/V8_x/InternalExceptionsTest.php +++ b/tests/Integrations/Laravel/V8_x/InternalExceptionsTest.php @@ -4,15 +4,12 @@ use DDTrace\Tag; use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\SpanAssertionTrait; -use DDTrace\Tests\Common\TracerTestTrait; use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; class InternalExceptionsTest extends WebFrameworkTestCase { - use TracerTestTrait; - use SpanAssertionTrait; + public static $database = "laravel8"; protected static function getAppIndexScript() { @@ -46,7 +43,7 @@ public function testNotImplemented() 'laravel.route.name' => 'not-implemented', 'laravel.route.action' => 'App\Http\Controllers\InternalErrorController@notImplemented', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/not-implemented', + 'http.url' => 'http://localhost/not-implemented', 'http.status_code' => '501', 'http.route' => 'not-implemented', TAG::SPAN_KIND => 'server', @@ -106,7 +103,7 @@ public function testUnauthorized() 'laravel.route.name' => 'unauthorized', 'laravel.route.action' => 'App\Http\Controllers\InternalErrorController@unauthorized', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/unauthorized', + 'http.url' => 'http://localhost/unauthorized', 'http.status_code' => '403', 'http.route' => 'unauthorized', TAG::SPAN_KIND => 'server', diff --git a/tests/Integrations/Laravel/V8_x/PathParamsTest.php b/tests/Integrations/Laravel/V8_x/PathParamsTest.php index c9d1183386..1a6f81bc2c 100644 --- a/tests/Integrations/Laravel/V8_x/PathParamsTest.php +++ b/tests/Integrations/Laravel/V8_x/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "laravel8"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_8_x/public/index.php'; diff --git a/tests/Integrations/Laravel/V8_x/QueueTest.php b/tests/Integrations/Laravel/V8_x/QueueTest.php index a8e34083e7..e0c66ed88c 100644 --- a/tests/Integrations/Laravel/V8_x/QueueTest.php +++ b/tests/Integrations/Laravel/V8_x/QueueTest.php @@ -2,22 +2,14 @@ namespace DDTrace\Tests\Integrations\Laravel\V8_x; -use DDTrace\Log\Logger; use DDTrace\Tag; -use DDTrace\Tests\Common\SnapshotTestTrait; use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\SpanAssertionTrait; -use DDTrace\Tests\Common\SpanChecker; -use DDTrace\Tests\Common\TracerTestTrait; use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; -use Illuminate\Support\Facades\Artisan; class QueueTest extends WebFrameworkTestCase { - use TracerTestTrait; - use SpanAssertionTrait; - use SnapshotTestTrait; + public static $database = "laravel8"; protected static function getAppIndexScript() { @@ -349,7 +341,7 @@ protected function resetQueue() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + return new \PDO('mysql:host=mysql_integration;dbname=laravel8', 'test', 'test'); } protected function spanEventJobProcessing() diff --git a/tests/Integrations/Laravel/V8_x/QueueTestNotDistributed.php b/tests/Integrations/Laravel/V8_x/QueueTestNotDistributed.php index cc901923a5..72a1a3f751 100644 --- a/tests/Integrations/Laravel/V8_x/QueueTestNotDistributed.php +++ b/tests/Integrations/Laravel/V8_x/QueueTestNotDistributed.php @@ -4,15 +4,12 @@ use DDTrace\Tag; use DDTrace\Tests\Common\SpanAssertion; -use DDTrace\Tests\Common\SpanAssertionTrait; -use DDTrace\Tests\Common\TracerTestTrait; use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; class QueueTestNotDistributed extends WebFrameworkTestCase { - use TracerTestTrait; - use SpanAssertionTrait; + public static $database = "laravel8"; protected static function getAppIndexScript() { @@ -91,7 +88,7 @@ protected function resetQueue() protected function connection() { - return new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + return new \PDO('mysql:host=mysql_integration;dbname=laravel8', 'test', 'test'); } protected function getCommonTags( diff --git a/tests/Integrations/Laravel/V8_x/RouteCachingTest.php b/tests/Integrations/Laravel/V8_x/RouteCachingTest.php index 269936d567..1a357f8a4c 100644 --- a/tests/Integrations/Laravel/V8_x/RouteCachingTest.php +++ b/tests/Integrations/Laravel/V8_x/RouteCachingTest.php @@ -10,6 +10,8 @@ class RouteCachingTest extends WebFrameworkTestCase { + public static $database = "laravel8"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_8_x/public/index.php'; @@ -40,7 +42,7 @@ public function testNotCached() 'laravel.route.name' => 'unnamed_route', 'laravel.route.action' => 'App\Http\Controllers\RouteCachingController@unnamed', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/unnamed-route', + 'http.url' => 'http://localhost/unnamed-route', 'http.status_code' => '200', 'http.route' => 'unnamed-route', TAG::SPAN_KIND => 'server', @@ -88,7 +90,7 @@ public function testCached() 'laravel.route.name' => 'unnamed_route', 'laravel.route.action' => 'App\Http\Controllers\RouteCachingController@unnamed', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/unnamed-route', + 'http.url' => 'http://localhost/unnamed-route', 'http.status_code' => '200', 'http.route' => 'unnamed-route', TAG::SPAN_KIND => 'server', diff --git a/tests/Integrations/Laravel/V8_x/TraceSearchConfigTest.php b/tests/Integrations/Laravel/V8_x/TraceSearchConfigTest.php index 1ea5d475ab..4c98522fc8 100644 --- a/tests/Integrations/Laravel/V8_x/TraceSearchConfigTest.php +++ b/tests/Integrations/Laravel/V8_x/TraceSearchConfigTest.php @@ -9,6 +9,8 @@ class TraceSearchConfigTest extends WebFrameworkTestCase { + public static $database = "laravel8"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_8_x/public/index.php'; @@ -44,7 +46,7 @@ public function testScenario() 'laravel.route.name' => 'simple_route', 'laravel.route.action' => 'App\Http\Controllers\CommonSpecsController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', 'http.route' => 'simple', TAG::SPAN_KIND => 'server', diff --git a/tests/Integrations/Laravel/V9_x/AutomatedLoginEventsTest.php b/tests/Integrations/Laravel/V9_x/AutomatedLoginEventsTest.php index 7b9407ce15..89fafef9d8 100644 --- a/tests/Integrations/Laravel/V9_x/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Laravel/V9_x/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "laravel9"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_9_x/public/index.php'; diff --git a/tests/Integrations/Laravel/V9_x/CommonScenariosTest.php b/tests/Integrations/Laravel/V9_x/CommonScenariosTest.php index 682bcc0d37..aa15066f69 100644 --- a/tests/Integrations/Laravel/V9_x/CommonScenariosTest.php +++ b/tests/Integrations/Laravel/V9_x/CommonScenariosTest.php @@ -7,6 +7,8 @@ class CommonScenariosTest extends \DDTrace\Tests\Integrations\Laravel\V5_7\CommonScenariosTest { + public static $database = "laravel9"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Laravel/Version_9_x/public/index.php'; diff --git a/tests/Integrations/Logs/BaseLogsTest.php b/tests/Integrations/Logs/BaseLogsTest.php index b42485a8fa..3e72b215db 100644 --- a/tests/Integrations/Logs/BaseLogsTest.php +++ b/tests/Integrations/Logs/BaseLogsTest.php @@ -8,15 +8,20 @@ class BaseLogsTest extends \DDTrace\Tests\Common\IntegrationTestCase { + protected static function logFile() + { + return "/tmp/test-" . substr(static::class, strrpos(static::class, '\\') + 1) . '.log'; + } + protected function ddSetUp() { parent::ddSetUp(); - shell_exec('rm -f /tmp/test.log'); + shell_exec('rm -f ' . static::logFile()); } protected function getTestFileContents(): string { - $filename = '/tmp/test.log'; + $filename = static::logFile(); $handle = fopen($filename, 'r'); $contents = fread($handle, filesize($filename)); fclose($handle); diff --git a/tests/Integrations/Logs/LaminasLogV2/LaminasLogV2Test.php b/tests/Integrations/Logs/LaminasLogV2/LaminasLogV2Test.php index e6deb9c4b1..bfb5963137 100644 --- a/tests/Integrations/Logs/LaminasLogV2/LaminasLogV2Test.php +++ b/tests/Integrations/Logs/LaminasLogV2/LaminasLogV2Test.php @@ -23,7 +23,7 @@ protected function ddSetUp() protected function getLogger($jsonFormatter = false) { $logger = new Logger(); - $writer = new Stream('/tmp/test.log'); + $writer = new Stream(static::logFile()); if ($jsonFormatter) { $writer->setFormatter(new Json()); diff --git a/tests/Integrations/Logs/LaminasLogV2/composer.json b/tests/Integrations/Logs/LaminasLogV2/composer.json new file mode 100644 index 0000000000..f16f36b9ef --- /dev/null +++ b/tests/Integrations/Logs/LaminasLogV2/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "laminas/laminas-log": "~2.0" + } +} diff --git a/tests/Integrations/Logs/MonologV1/MonologV1Test.php b/tests/Integrations/Logs/MonologV1/MonologV1Test.php index c84cd24d7a..0c17cd9311 100644 --- a/tests/Integrations/Logs/MonologV1/MonologV1Test.php +++ b/tests/Integrations/Logs/MonologV1/MonologV1Test.php @@ -12,7 +12,7 @@ class MonologV1Test extends BaseLogsTest protected function getLogger($jsonFormatter = false) { $logger = new Logger('test'); - $streamHandler = new StreamHandler('/tmp/test.log'); + $streamHandler = new StreamHandler(static::logFile()); if ($jsonFormatter) { $streamHandler->setFormatter(new JsonFormatter()); diff --git a/tests/Integrations/Logs/MonologV1/composer.json b/tests/Integrations/Logs/MonologV1/composer.json new file mode 100644 index 0000000000..4928c06f8b --- /dev/null +++ b/tests/Integrations/Logs/MonologV1/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "monolog/monolog": "~1.0" + } +} diff --git a/tests/Integrations/Logs/MonologV2/composer.json b/tests/Integrations/Logs/MonologV2/composer.json new file mode 100644 index 0000000000..fa7c066e21 --- /dev/null +++ b/tests/Integrations/Logs/MonologV2/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "monolog/monolog": "~2.0" + } +} diff --git a/tests/Integrations/Logs/MonologV3/composer.json b/tests/Integrations/Logs/MonologV3/composer.json new file mode 100644 index 0000000000..5156e664e6 --- /dev/null +++ b/tests/Integrations/Logs/MonologV3/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "monolog/monolog": "~3.0" + } +} diff --git a/tests/Integrations/Lumen/V10_0/TraceSearchConfigTest.php b/tests/Integrations/Lumen/V10_0/TraceSearchConfigTest.php index 57f6771713..26ed1d4328 100644 --- a/tests/Integrations/Lumen/V10_0/TraceSearchConfigTest.php +++ b/tests/Integrations/Lumen/V10_0/TraceSearchConfigTest.php @@ -44,7 +44,7 @@ public function testScenario() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'lumen', diff --git a/tests/Integrations/Lumen/V5_2/CommonScenariosTest.php b/tests/Integrations/Lumen/V5_2/CommonScenariosTest.php index a0ddf044af..b3bcfc9a1c 100644 --- a/tests/Integrations/Lumen/V5_2/CommonScenariosTest.php +++ b/tests/Integrations/Lumen/V5_2/CommonScenariosTest.php @@ -50,7 +50,7 @@ public function provideSpecs() )->withExactTags([ 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simpleView', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen' @@ -110,7 +110,7 @@ public function provideSpecs() )->withExactTags([ 'lumen.route.action' => 'App\Http\Controllers\ExampleController@error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen' @@ -159,7 +159,7 @@ protected function getSimpleTrace() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen' @@ -188,7 +188,7 @@ protected function getErrorTrace() )->withExactTags([ 'lumen.route.action' => 'App\Http\Controllers\ExampleController@error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen' diff --git a/tests/Integrations/Lumen/V5_2/TraceSearchConfigTest.php b/tests/Integrations/Lumen/V5_2/TraceSearchConfigTest.php index fc20a908f0..1245798370 100644 --- a/tests/Integrations/Lumen/V5_2/TraceSearchConfigTest.php +++ b/tests/Integrations/Lumen/V5_2/TraceSearchConfigTest.php @@ -44,7 +44,7 @@ public function testScenario() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen' diff --git a/tests/Integrations/Lumen/V5_6/CommonScenariosTest.php b/tests/Integrations/Lumen/V5_6/CommonScenariosTest.php index bee117face..87d5afcba1 100644 --- a/tests/Integrations/Lumen/V5_6/CommonScenariosTest.php +++ b/tests/Integrations/Lumen/V5_6/CommonScenariosTest.php @@ -50,7 +50,7 @@ public function provideSpecs() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen', @@ -75,7 +75,7 @@ public function provideSpecs() )->withExactTags([ 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simpleView', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen', @@ -120,7 +120,7 @@ public function provideSpecs() )->withExactTags([ 'lumen.route.action' => 'App\Http\Controllers\ExampleController@error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', TAG::COMPONENT => 'lumen' diff --git a/tests/Integrations/Lumen/V5_6/DeprecatedResourceNameTest.php b/tests/Integrations/Lumen/V5_6/DeprecatedResourceNameTest.php index 55e89c648f..4fdcf2dd03 100644 --- a/tests/Integrations/Lumen/V5_6/DeprecatedResourceNameTest.php +++ b/tests/Integrations/Lumen/V5_6/DeprecatedResourceNameTest.php @@ -43,7 +43,7 @@ public function testScenario() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'lumen', diff --git a/tests/Integrations/Lumen/V5_6/TraceSearchConfigTest.php b/tests/Integrations/Lumen/V5_6/TraceSearchConfigTest.php index cf690079c4..864c7eb320 100644 --- a/tests/Integrations/Lumen/V5_6/TraceSearchConfigTest.php +++ b/tests/Integrations/Lumen/V5_6/TraceSearchConfigTest.php @@ -44,7 +44,7 @@ public function testScenario() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'lumen', diff --git a/tests/Integrations/Lumen/V5_8/TraceSearchConfigTest.php b/tests/Integrations/Lumen/V5_8/TraceSearchConfigTest.php index b635b4dd35..b02f5039e6 100644 --- a/tests/Integrations/Lumen/V5_8/TraceSearchConfigTest.php +++ b/tests/Integrations/Lumen/V5_8/TraceSearchConfigTest.php @@ -44,7 +44,7 @@ public function testScenario() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'lumen', diff --git a/tests/Integrations/Lumen/V8_1/TraceSearchConfigTest.php b/tests/Integrations/Lumen/V8_1/TraceSearchConfigTest.php index 49d8820d2b..170e013f37 100644 --- a/tests/Integrations/Lumen/V8_1/TraceSearchConfigTest.php +++ b/tests/Integrations/Lumen/V8_1/TraceSearchConfigTest.php @@ -44,7 +44,7 @@ public function testScenario() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'lumen', diff --git a/tests/Integrations/Lumen/V9_0/TraceSearchConfigTest.php b/tests/Integrations/Lumen/V9_0/TraceSearchConfigTest.php index 01e856ac9c..8819068029 100644 --- a/tests/Integrations/Lumen/V9_0/TraceSearchConfigTest.php +++ b/tests/Integrations/Lumen/V9_0/TraceSearchConfigTest.php @@ -44,7 +44,7 @@ public function testScenario() 'lumen.route.name' => 'simple_route', 'lumen.route.action' => 'App\Http\Controllers\ExampleController@simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'lumen', diff --git a/tests/Integrations/Magento/V2_3/CommonScenariosTest.php b/tests/Integrations/Magento/V2_3/CommonScenariosTest.php index c1c34c740c..bfd39174eb 100644 --- a/tests/Integrations/Magento/V2_3/CommonScenariosTest.php +++ b/tests/Integrations/Magento/V2_3/CommonScenariosTest.php @@ -4,6 +4,8 @@ class CommonScenariosTest extends \DDTrace\Tests\Integrations\Magento\V2_4\CommonScenariosTest { + public static $database = "magento23"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Magento/Version_2_3/pub/index.php'; diff --git a/tests/Integrations/Magento/V2_4/CommonScenariosTest.php b/tests/Integrations/Magento/V2_4/CommonScenariosTest.php index 7a543ba61a..14eea63e32 100644 --- a/tests/Integrations/Magento/V2_4/CommonScenariosTest.php +++ b/tests/Integrations/Magento/V2_4/CommonScenariosTest.php @@ -7,6 +7,8 @@ class CommonScenariosTest extends WebFrameworkTestCase { + public static $database = "magento24"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Magento/Version_2_4/pub/index.php'; diff --git a/tests/Integrations/Memcache/MemcacheTest.php b/tests/Integrations/Memcache/MemcacheTest.php index ef0244a087..06289a79fb 100644 --- a/tests/Integrations/Memcache/MemcacheTest.php +++ b/tests/Integrations/Memcache/MemcacheTest.php @@ -10,6 +10,8 @@ final class MemcacheTest extends IntegrationTestCase { + protected static $lockedResource = "memcache"; + /** * @var \Memcache */ diff --git a/tests/Integrations/Memcached/MemcachedTest.php b/tests/Integrations/Memcached/MemcachedTest.php index 80a2470d43..7c07ff96cf 100644 --- a/tests/Integrations/Memcached/MemcachedTest.php +++ b/tests/Integrations/Memcached/MemcachedTest.php @@ -10,6 +10,8 @@ final class MemcachedTest extends IntegrationTestCase { + protected static $lockedResource = "memcache"; + /** * @var \Memcached */ diff --git a/tests/Integrations/Mongo/MongoTest.php b/tests/Integrations/Mongo/MongoTest.php index 1d616f9c00..ae1e83398e 100644 --- a/tests/Integrations/Mongo/MongoTest.php +++ b/tests/Integrations/Mongo/MongoTest.php @@ -12,6 +12,8 @@ class MongoTest extends IntegrationTestCase { + protected static $lockedResource = "mongodb"; + const HOST = 'mongodb_integration'; const PORT = '27017'; const USER = 'test'; diff --git a/tests/Integrations/MongoDB/MongoDBTest.php b/tests/Integrations/MongoDB/MongoDBTest.php index dc14ca2d9c..8e467c03d2 100644 --- a/tests/Integrations/MongoDB/MongoDBTest.php +++ b/tests/Integrations/MongoDB/MongoDBTest.php @@ -26,6 +26,8 @@ class AnObject class MongoDBTest extends IntegrationTestCase { + protected static $lockedResource = "mongodb"; + const HOST = 'mongodb_integration'; const PORT = '27017'; const USER = 'test'; diff --git a/tests/Integrations/MongoDB/composer.json b/tests/Integrations/MongoDB/composer.json new file mode 100644 index 0000000000..14ff1e3bda --- /dev/null +++ b/tests/Integrations/MongoDB/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "mongodb/mongodb": "1.*" + } +} diff --git a/tests/Integrations/Mysqli/MysqliTest.php b/tests/Integrations/Mysqli/MysqliTest.php index 705faa6b5b..62be833d17 100644 --- a/tests/Integrations/Mysqli/MysqliTest.php +++ b/tests/Integrations/Mysqli/MysqliTest.php @@ -9,7 +9,7 @@ class MysqliTest extends IntegrationTestCase { private static $host = 'mysql_integration'; - private static $db = 'test'; + public static $database = 'mysqlitest'; private static $port = '3306'; private static $user = 'test'; private static $password = 'test'; @@ -39,7 +39,7 @@ protected function envsToCleanUpAtTearDown() public function testProceduralConnect() { $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); $mysqli->close(); }); @@ -96,7 +96,7 @@ public function testProceduralConnectDontReportError() public function testConstructorConnect() { $traces = $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->close(); }); @@ -109,7 +109,7 @@ public function testConstructorConnect() public function testProceduralQuery() { $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); \mysqli_query($mysqli, 'SELECT * from tests'); $mysqli->close(); }); @@ -130,7 +130,7 @@ public function testProceduralQueryPeerServiceEnabled() { $this->putEnvAndReloadConfig(['DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED=true']); $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); \mysqli_query($mysqli, 'SELECT * from tests'); $mysqli->close(); }); @@ -154,7 +154,7 @@ public function testProceduralExecuteQuery() } $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); \mysqli_execute_query($mysqli, 'SELECT * from tests WHERE 1 = ?', [1]); $mysqli->close(); }); @@ -175,7 +175,7 @@ public function testProceduralExecuteQueryPeerServiceEnabled() $this->putEnvAndReloadConfig(['DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED=true']); $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); \mysqli_execute_query($mysqli, 'SELECT * from tests WHERE 1 = ?', [1]); $mysqli->close(); }); @@ -191,7 +191,7 @@ public function testProceduralQueryRealConnect() { $traces = $this->isolateTracer(function () { $mysqli = \mysqli_init(); - \mysqli_real_connect($mysqli, self::$host, self::$user, self::$password, self::$db); + \mysqli_real_connect($mysqli, self::$host, self::$user, self::$password, self::$database); \mysqli_query($mysqli, 'SELECT * from tests'); $mysqli->close(); }); @@ -215,7 +215,7 @@ public function testProceduralQueryRealConnectPeerServiceEnabled() $traces = $this->isolateTracer(function () { $mysqli = \mysqli_init(); - \mysqli_real_connect($mysqli, self::$host, self::$user, self::$password, self::$db); + \mysqli_real_connect($mysqli, self::$host, self::$user, self::$password, self::$database); \mysqli_query($mysqli, 'SELECT * from tests'); $mysqli->close(); }); @@ -236,7 +236,7 @@ public function testProceduralQueryRealConnectPeerServiceEnabled() public function testConstructorQuery() { $traces = $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->query('SELECT * from tests'); $mysqli->close(); }); @@ -258,7 +258,7 @@ public function testConstructorQueryPeerServiceEnabled() $this->putEnvAndReloadConfig(['DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED=true']); $traces = $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->query('SELECT * from tests'); $mysqli->close(); }); @@ -279,7 +279,7 @@ public function testEmptyConstructorQuery() { $traces = $this->isolateTracer(function () { $mysqli = new \mysqli(); - $mysqli->real_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli->real_connect(self::$host, self::$user, self::$password, self::$database); $mysqli->query('SELECT * from tests'); $mysqli->close(); }); @@ -304,7 +304,7 @@ public function testEmptyConstructorQueryPeerServiceEnabled() $traces = $this->isolateTracer(function () { $mysqli = new \mysqli(); - $mysqli->real_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli->real_connect(self::$host, self::$user, self::$password, self::$database); $mysqli->query('SELECT * from tests'); $mysqli->close(); }); @@ -327,7 +327,7 @@ public function testProceduralCommit() { $query = "INSERT INTO tests (id, name) VALUES (100, 'Tom')"; $traces = $this->isolateTracer(function () use ($query) { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); \mysqli_query($mysqli, $query); \mysqli_commit($mysqli); $mysqli->close(); @@ -345,7 +345,7 @@ public function testProceduralCommit() public function testConstructorPreparedStatement() { $traces = $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $stmt = $mysqli->prepare("INSERT INTO tests (id, name) VALUES (?, ?)"); $id = 100; $name = 100; @@ -369,7 +369,7 @@ public function testConstructorPreparedStatementPeerServiceEnabled() $this->putEnvAndReloadConfig(['DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED=true']); $traces = $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $stmt = $mysqli->prepare("INSERT INTO tests (id, name) VALUES (?, ?)"); $id = 100; $name = 100; @@ -393,7 +393,7 @@ public function testProceduralSelectDbPeerServiceEnabled() $this->putEnvAndReloadConfig(['DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED=true', 'DD_TRACE_GENERATE_ROOT_SPAN=true']); $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); \mysqli_select_db($mysqli, 'information_schema'); \mysqli_query($mysqli, 'SELECT * from columns limit 1'); $mysqli->close(); @@ -422,7 +422,7 @@ public function testConstructorSelectDbPeerServiceEnabled() $this->putEnvAndReloadConfig(['DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED=true', 'DD_TRACE_GENERATE_ROOT_SPAN=true']); $traces = $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->select_db('information_schema'); $mysqli->query('SELECT * from columns limit 1'); $mysqli->close(); @@ -449,7 +449,7 @@ public function testConstructorSelectDbPeerServiceEnabled() public function testLimitedTracerConstructorQuery() { $traces = $this->isolateLimitedTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->query('SELECT * from tests'); $mysqli->close(); }); @@ -460,7 +460,7 @@ public function testLimitedTracerConstructorQuery() public function testLimitedTracerProceduralCommit() { $traces = $this->isolateLimitedTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); \mysqli_query($mysqli, "INSERT INTO tests (id, name) VALUES (100, 'From Test')"); \mysqli_commit($mysqli); $mysqli->close(); @@ -473,7 +473,7 @@ public function testLimitedTracerProceduralCommit() public function testLimitedTracerConstructorPreparedStatement() { $traces = $this->isolateLimitedTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $stmt = $mysqli->prepare("INSERT INTO tests (id, name) VALUES (?, ?)"); $id = 100; $name = 100; @@ -489,7 +489,7 @@ public function testLimitedTracerConstructorPreparedStatement() public function testProceduralPreparedStatement() { $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); $stmt = \mysqli_prepare($mysqli, "INSERT INTO tests (id, name) VALUES (?, ?)"); $id = 100; $name = 100; @@ -503,13 +503,9 @@ public function testProceduralPreparedStatement() $this->assertFlameGraph($traces, [ SpanAssertion::exists('mysqli_connect', 'mysqli_connect'), SpanAssertion::build('mysqli_prepare', 'mysqli', 'sql', 'INSERT INTO tests (id, name) VALUES (?, ?)') - ->withExactTags(array_merge(self::baseTags(), [ - '_dd.base_service' => 'phpunit', - ])), + ->withExactTags(self::baseTags()), SpanAssertion::build('mysqli_stmt_execute', 'mysqli', 'sql', 'INSERT INTO tests (id, name) VALUES (?, ?)') - ->withExactTags(array_merge(self::baseTags(), [ - '_dd.base_service' => 'phpunit', - ])), + ->withExactTags(self::baseTags()), ], true, false); } @@ -518,7 +514,7 @@ public function testProceduralPreparedStatementPeerServiceEnabled() $this->putEnvAndReloadConfig(['DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED=true']); $traces = $this->isolateTracer(function () { - $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$db); + $mysqli = \mysqli_connect(self::$host, self::$user, self::$password, self::$database); $stmt = \mysqli_prepare($mysqli, "INSERT INTO tests (id, name) VALUES (?, ?)"); $id = 100; $name = 100; @@ -571,7 +567,7 @@ public function testNoFakeServices() ]); $traces = $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->query('SELECT * from tests'); $mysqli->close(); }); @@ -591,7 +587,7 @@ public function testServiceMappedSplitByDomain() self::putEnv('DD_TRACE_DB_CLIENT_SPLIT_BY_INSTANCE=true'); self::putEnv('DD_SERVICE_MAPPING=mysqli:my-mysqli'); $traces = $this->isolateTracer(function () { - new \mysqli(self::$host, self::$user, self::$password, self::$db); + new \mysqli(self::$host, self::$user, self::$password, self::$database); }); $this->assertSpans($traces, [ @@ -611,11 +607,11 @@ private function baseTags($expectDbName = true, $expectPeerService = false) ]; if ($expectDbName) { - $tags['db.name'] = 'test'; + $tags['db.name'] = self::$database; } if ($expectPeerService) { - $tags['peer.service'] = 'test'; + $tags['peer.service'] = self::$database; $tags['_dd.peer.service.source'] = 'db.name'; } @@ -625,7 +621,7 @@ private function baseTags($expectDbName = true, $expectPeerService = false) private function setUpDatabase() { $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->query(" CREATE TABLE tests ( id integer not null primary key, @@ -641,7 +637,7 @@ private function setUpDatabase() private function clearDatabase() { $this->isolateTracer(function () { - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $mysqli->query("DROP TABLE IF EXISTS tests"); $mysqli->commit(); $mysqli->close(); @@ -661,7 +657,7 @@ private function queryDatabaseAllAssociative($table, $wheres) $conditions[] = "$key = '$value'"; } $inlineWhere = $conditions ? 'WHERE ' . implode('AND', $conditions) : ''; - $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$db); + $mysqli = new \mysqli(self::$host, self::$user, self::$password, self::$database); $result = $mysqli->query("SELECT * FROM $table $inlineWhere"); if (false === $result) { $message = mysqli_error($mysqli); diff --git a/tests/Integrations/Nette/V2_4/NetteTest.php b/tests/Integrations/Nette/V2_4/NetteTest.php index ee4e82802b..6d1d565389 100644 --- a/tests/Integrations/Nette/V2_4/NetteTest.php +++ b/tests/Integrations/Nette/V2_4/NetteTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'nette.route.presenter' => 'Homepage', 'nette.route.action' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'nette' @@ -93,7 +93,7 @@ public function provideSpecs() 'nette.route.presenter' => 'Homepage', 'nette.route.action' => 'simpleView', 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'nette' @@ -147,7 +147,7 @@ public function provideSpecs() 'nette.route.presenter' => 'Homepage', 'nette.route.action' => 'errorView', 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'nette' diff --git a/tests/Integrations/Nette/V3_0/NetteTest.php b/tests/Integrations/Nette/V3_0/NetteTest.php index 678516411b..81edcdd1fe 100644 --- a/tests/Integrations/Nette/V3_0/NetteTest.php +++ b/tests/Integrations/Nette/V3_0/NetteTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'nette.route.presenter' => 'Homepage', 'nette.route.action' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'nette' @@ -93,7 +93,7 @@ public function provideSpecs() 'nette.route.presenter' => 'Homepage', 'nette.route.action' => 'simpleView', 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'nette' @@ -147,7 +147,7 @@ public function provideSpecs() 'nette.route.presenter' => 'Homepage', 'nette.route.action' => 'errorView', 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'nette' diff --git a/tests/Integrations/OpenAI/composer.json b/tests/Integrations/OpenAI/composer.json new file mode 100644 index 0000000000..6bbc027b03 --- /dev/null +++ b/tests/Integrations/OpenAI/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "openai-php/client": "@stable", + "guzzlehttp/guzzle": "^7.8.1", + "guzzlehttp/psr7": "^2.6.2" + } +} diff --git a/tests/Integrations/PDO/PDOTest.php b/tests/Integrations/PDO/PDOTest.php index 31ef5357e1..690acb8f01 100644 --- a/tests/Integrations/PDO/PDOTest.php +++ b/tests/Integrations/PDO/PDOTest.php @@ -8,7 +8,7 @@ final class PDOTest extends IntegrationTestCase { - const MYSQL_DATABASE = 'test'; + public static $database = 'pdotest'; const MYSQL_USER = 'test'; const MYSQL_PASSWORD = 'test'; const MYSQL_HOST = 'mysql_integration'; @@ -733,45 +733,39 @@ private function ensureActiveQueriesErrorCanHappen() private function setUpDatabase() { - $this->isolateTracer(function () { - $pdo = $this->pdoInstance(); + $pdo = $this->pdoInstance(); + $pdo->beginTransaction(); + $pdo->exec(" + CREATE TABLE tests ( + id integer not null primary key AUTO_INCREMENT, + name varchar(100) + ) + "); + if (PHP_VERSION_ID >= 80000 && !$pdo->inTransaction()) { + // CREATE TABLE causes an implicit commit on PHP 8 + // @see https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html $pdo->beginTransaction(); - $pdo->exec(" - CREATE TABLE tests ( - id integer not null primary key AUTO_INCREMENT, - name varchar(100) - ) - "); - if (PHP_VERSION_ID >= 80000 && !$pdo->inTransaction()) { - // CREATE TABLE causes an implicit commit on PHP 8 - // @see https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html - $pdo->beginTransaction(); - } - $pdo->exec("INSERT INTO tests (id, name) VALUES (1, 'Tom')"); + } + $pdo->exec("INSERT INTO tests (id, name) VALUES (1, 'Tom')"); - $pdo->commit(); - $pdo = null; - }); + $pdo->commit(); } private function clearDatabase() { - $this->isolateTracer(function () { - $pdo = $this->pdoInstance(); - $pdo->beginTransaction(); - $pdo->exec("DROP TABLE tests"); - if (PHP_VERSION_ID < 80000) { - // DROP TABLE causes an implicit commit on PHP 8 - // @see https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html - $pdo->commit(); - } - $pdo = null; - }); + $pdo = $this->pdoInstance(); + $pdo->beginTransaction(); + $pdo->exec("DROP TABLE tests"); + if (PHP_VERSION_ID < 80000) { + // DROP TABLE causes an implicit commit on PHP 8 + // @see https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html + $pdo->commit(); + } } public function mysqlDns() { - return "mysql:host=" . self::MYSQL_HOST . ";dbname=" . self::MYSQL_DATABASE; + return "mysql:host=" . self::MYSQL_HOST . ";dbname=" . self::$database; } protected function baseTags($expectPeerService = false) @@ -779,7 +773,7 @@ protected function baseTags($expectPeerService = false) $tags = [ 'db.engine' => 'mysql', 'out.host' => self::MYSQL_HOST, - 'db.name' => self::MYSQL_DATABASE, + 'db.name' => self::$database, 'db.user' => self::MYSQL_USER, 'span.kind' => 'client', Tag::COMPONENT => 'pdo', @@ -787,7 +781,7 @@ protected function baseTags($expectPeerService = false) ]; if ($expectPeerService) { - $tags['peer.service'] = self::MYSQL_DATABASE; + $tags['peer.service'] = self::$database; $tags['_dd.peer.service.source'] = 'db.name'; } diff --git a/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php b/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php index 1b1c3ae3a6..52da1ea320 100644 --- a/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php +++ b/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php @@ -10,6 +10,8 @@ class PHPRedisClusterTest extends IntegrationTestCase { + protected static $lockedResource = "redis"; + const CONNECTION_1 = 'CONNECTION_1'; const CONNECTION_1_AS_ARG = 'CONNECTION_1_AS_ARG'; const A_STRING = 'A_STRING'; diff --git a/tests/Integrations/PHPRedis/V3/PHPRedisTest.php b/tests/Integrations/PHPRedis/V3/PHPRedisTest.php index 34c413547f..d182c6ed1a 100644 --- a/tests/Integrations/PHPRedis/V3/PHPRedisTest.php +++ b/tests/Integrations/PHPRedis/V3/PHPRedisTest.php @@ -10,6 +10,8 @@ class PHPRedisTest extends IntegrationTestCase { + protected static $lockedResource = "redis"; + const A_STRING = 'A_STRING'; const ARRAY_COUNT_1 = 'ARRAY_COUNT_1'; const ARRAY_COUNT_2 = 'ARRAY_COUNT_2'; diff --git a/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php b/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php index 906ba3e22f..8b52230423 100644 --- a/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php +++ b/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php @@ -10,6 +10,8 @@ class PHPRedisClusterTest extends IntegrationTestCase { + protected static $lockedResource = "redis"; + const CONNECTION_1 = 'CONNECTION_1'; const CONNECTION_1_AS_ARG = 'CONNECTION_1_AS_ARG'; const A_STRING = 'A_STRING'; diff --git a/tests/Integrations/PHPRedis/V4/PHPRedisTest.php b/tests/Integrations/PHPRedis/V4/PHPRedisTest.php index 8162c9f76e..54142c087a 100644 --- a/tests/Integrations/PHPRedis/V4/PHPRedisTest.php +++ b/tests/Integrations/PHPRedis/V4/PHPRedisTest.php @@ -10,6 +10,8 @@ class PHPRedisTest extends IntegrationTestCase { + protected static $lockedResource = "redis"; + const A_STRING = 'A_STRING'; const A_FLOAT = 'A_FLOAT'; const ARRAY_COUNT_1 = 'ARRAY_COUNT_1'; diff --git a/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php b/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php index ac7eee8d0b..498b922fd5 100644 --- a/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php +++ b/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php @@ -10,6 +10,8 @@ class PHPRedisClusterTest extends IntegrationTestCase { + protected static $lockedResource = "redis"; + const CONNECTION_1 = 'CONNECTION_1'; const CONNECTION_1_AS_ARG = 'CONNECTION_1_AS_ARG'; const A_STRING = 'A_STRING'; diff --git a/tests/Integrations/PHPRedis/V5/PHPRedisTest.php b/tests/Integrations/PHPRedis/V5/PHPRedisTest.php index f8e1d043be..68271da19d 100644 --- a/tests/Integrations/PHPRedis/V5/PHPRedisTest.php +++ b/tests/Integrations/PHPRedis/V5/PHPRedisTest.php @@ -17,6 +17,8 @@ class CustomRedisClass extends \Redis class PHPRedisTest extends IntegrationTestCase { + protected static $lockedResource = "redis"; + const A_STRING = 'A_STRING'; const A_FLOAT = 'A_FLOAT'; const ARRAY_COUNT_1 = 'ARRAY_COUNT_1'; diff --git a/tests/Integrations/Predis/PredisTest.php b/tests/Integrations/Predis/PredisTest.php index 8bc7f9ed4c..f7e5d0e703 100644 --- a/tests/Integrations/Predis/PredisTest.php +++ b/tests/Integrations/Predis/PredisTest.php @@ -9,6 +9,8 @@ final class PredisTest extends IntegrationTestCase { + protected static $lockedResource = "redis"; + private $host = 'redis_integration'; private $port = '6379'; diff --git a/tests/Integrations/Predis/composer.json b/tests/Integrations/Predis/composer.json new file mode 100644 index 0000000000..5b58b88b4d --- /dev/null +++ b/tests/Integrations/Predis/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "predis/predis": "^1.1" + } +} diff --git a/tests/Integrations/Roadrunner/V2/CommonScenariosTest.php b/tests/Integrations/Roadrunner/V2/CommonScenariosTest.php index d7f888da37..efe303b35d 100644 --- a/tests/Integrations/Roadrunner/V2/CommonScenariosTest.php +++ b/tests/Integrations/Roadrunner/V2/CommonScenariosTest.php @@ -46,7 +46,7 @@ public function provideSpecs() 'GET /simple' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'roadrunner' @@ -60,7 +60,7 @@ public function provideSpecs() 'GET /simple_view' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'roadrunner' @@ -74,7 +74,7 @@ public function provideSpecs() 'GET /error' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', 'error.stack' => '#0 {main}', Tag::SPAN_KIND => 'server', diff --git a/tests/Integrations/SQLSRV/SQLSRVTest.php b/tests/Integrations/SQLSRV/SQLSRVTest.php index a52857bb57..2410e00913 100644 --- a/tests/Integrations/SQLSRV/SQLSRVTest.php +++ b/tests/Integrations/SQLSRV/SQLSRVTest.php @@ -11,7 +11,7 @@ class SQLSRVTest extends IntegrationTestCase { private static $host = 'sqlsrv_integration'; private static $port = '1433'; - private static $database = 'master'; + private static $db = 'master'; private static $user = 'sa'; private static $password = 'Password12!'; @@ -397,17 +397,14 @@ public function testConnectPrepareStatement() $this->assertFlameGraph($traces, [ SpanAssertion::exists('sqlsrv_connect'), SpanAssertion::build('sqlsrv_prepare', 'sqlsrv', 'sql', $query) - ->withExactTags( - array_merge(self::baseTags($query), [ - '_dd.base_service' => 'phpunit', - ])), + ->withExactTags(self::baseTags($query)), SpanAssertion::build('sqlsrv_execute', 'sqlsrv', 'sql', $query) - ->withExactTags(array_merge(self::baseTags($query), [ - '_dd.base_service' => 'phpunit', - ])) + ->withExactTags(self::baseTags($query)) ->withExactMetrics([ Tag::DB_ROW_COUNT => 1.0, Tag::ANALYTICS_KEY => 1.0, + '_dd.agent_psr' => 1.0, + '_sampling_priority_v1' => 1.0, ]) ], true, false); } @@ -445,7 +442,7 @@ private function createConnection() self::$host . ', ' . self::$port, [ 'PWD' => self::$password, - 'Database' => self::$database, + 'Database' => self::$db, 'UID' => self::$user, 'TrustServerCertificate' => true ] @@ -484,7 +481,7 @@ private static function baseTags($query = null, $expectPeerService = false) Tag::SPAN_KIND => 'client', Tag::COMPONENT => SQLSRVIntegration::NAME, Tag::DB_SYSTEM => SQLSRVIntegration::SYSTEM, - Tag::DB_INSTANCE => self::$database, + Tag::DB_INSTANCE => self::$db, Tag::DB_USER => self::$user, Tag::TARGET_HOST => self::$host, Tag::TARGET_PORT => self::$port, diff --git a/tests/Integrations/Slim/V3_12/CommonScenariosTest.php b/tests/Integrations/Slim/V3_12/CommonScenariosTest.php index 0525516853..712538bc1f 100644 --- a/tests/Integrations/Slim/V3_12/CommonScenariosTest.php +++ b/tests/Integrations/Slim/V3_12/CommonScenariosTest.php @@ -49,7 +49,7 @@ public function provideSpecs() )->withExactTags([ 'slim.route.controller' => 'Closure::__invoke', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', @@ -74,7 +74,7 @@ public function provideSpecs() )->withExactTags([ 'slim.route.controller' => 'App\SimpleViewController::index', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', @@ -109,7 +109,7 @@ public function provideSpecs() )->withExactTags([ 'slim.route.controller' => 'Closure::__invoke', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', @@ -137,7 +137,7 @@ public function provideSpecs() )->withExactTags([ 'slim.route.controller' => 'Closure::__invoke', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/parameterized/paramValue', + 'http.url' => 'http://localhost/parameterized/paramValue', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', diff --git a/tests/Integrations/Slim/V4/CommonScenariosTest.php b/tests/Integrations/Slim/V4/CommonScenariosTest.php index 22c14b1f56..1a248bafbe 100644 --- a/tests/Integrations/Slim/V4/CommonScenariosTest.php +++ b/tests/Integrations/Slim/V4/CommonScenariosTest.php @@ -112,7 +112,7 @@ public function provideSpecs() 'slim.route.name' => 'simple-route', 'slim.route.handler' => 'Closure::__invoke', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', @@ -140,7 +140,7 @@ public function provideSpecs() )->withExactTags([ 'slim.route.handler' => 'Closure::__invoke', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', @@ -177,7 +177,7 @@ public function provideSpecs() )->withExactTags([ 'slim.route.handler' => 'Closure::__invoke', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', @@ -211,7 +211,7 @@ public function provideSpecs() )->withExactTags([ 'slim.route.handler' => 'Closure::__invoke', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/parameterized/paramValue', + 'http.url' => 'http://localhost/parameterized/paramValue', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', diff --git a/tests/Integrations/Swoole/CommonScenariosTest.php b/tests/Integrations/Swoole/CommonScenariosTest.php index 684c393da9..04e23cf013 100644 --- a/tests/Integrations/Swoole/CommonScenariosTest.php +++ b/tests/Integrations/Swoole/CommonScenariosTest.php @@ -65,7 +65,7 @@ public function provideSpecs() 'GET /simple' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'swoole' @@ -79,7 +79,7 @@ public function provideSpecs() 'GET /simple_view' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'swoole' @@ -93,7 +93,7 @@ public function provideSpecs() 'GET /error' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:' . self::PORT . '/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', 'error.stack' => "#0 [internal function]: {closure}()\n#1 {main}", Tag::SPAN_KIND => 'server', diff --git a/tests/Integrations/Symfony/V3_0/CommonScenariosTest.php b/tests/Integrations/Symfony/V3_0/CommonScenariosTest.php index 3874d14d77..4f9d14068d 100644 --- a/tests/Integrations/Symfony/V3_0/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V3_0/CommonScenariosTest.php @@ -43,7 +43,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -80,7 +80,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -126,7 +126,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -192,7 +192,7 @@ public function provideSpecs() )->withExactTags([ 'symfony.route.action' => 'Symfony\Bundle\TwigBundle\Controller\ExceptionController@showAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V3_0/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V3_0/TraceSearchConfigTest.php index 360eb17d57..82be10bfa6 100644 --- a/tests/Integrations/Symfony/V3_0/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V3_0/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V3_3/AutomatedLoginEventsTest.php b/tests/Integrations/Symfony/V3_3/AutomatedLoginEventsTest.php index 73d2fb36e8..3ce67daa57 100644 --- a/tests/Integrations/Symfony/V3_3/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Symfony/V3_3/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "symfony33"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_3_3/web/index.php'; diff --git a/tests/Integrations/Symfony/V3_3/CommonScenariosTest.php b/tests/Integrations/Symfony/V3_3/CommonScenariosTest.php index 8e8135cfe3..7e6d402ec9 100644 --- a/tests/Integrations/Symfony/V3_3/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V3_3/CommonScenariosTest.php @@ -43,7 +43,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -81,7 +81,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -128,7 +128,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -196,7 +196,7 @@ public function provideSpecs() )->withExactTags([ 'symfony.route.action' => 'Symfony\Bundle\TwigBundle\Controller\ExceptionController@showAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V3_3/PathParamsTest.php b/tests/Integrations/Symfony/V3_3/PathParamsTest.php index f1b82a2576..d0c2d1228f 100644 --- a/tests/Integrations/Symfony/V3_3/PathParamsTest.php +++ b/tests/Integrations/Symfony/V3_3/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "symfony33"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_3_3/web/index.php'; diff --git a/tests/Integrations/Symfony/V3_3/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V3_3/TraceSearchConfigTest.php index 7a7d09fa5c..2c9478e5a3 100644 --- a/tests/Integrations/Symfony/V3_3/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V3_3/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V3_4/AutofinishedTracesSymfony34Test.php b/tests/Integrations/Symfony/V3_4/AutofinishedTracesSymfony34Test.php index dc12f68b93..5e69751943 100644 --- a/tests/Integrations/Symfony/V3_4/AutofinishedTracesSymfony34Test.php +++ b/tests/Integrations/Symfony/V3_4/AutofinishedTracesSymfony34Test.php @@ -37,7 +37,7 @@ public function testEndpointThatExitsWithNoProcess() 'symfony.route.action' => 'AppBundle\Controller\HomeController@actionBeingTerminatedByExit', 'symfony.route.name' => 'terminated_by_exit', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/terminated_by_exit', + 'http.url' => 'http://localhost/terminated_by_exit', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V3_4/CommonScenariosTest.php b/tests/Integrations/Symfony/V3_4/CommonScenariosTest.php index 478e0f340d..4973645565 100644 --- a/tests/Integrations/Symfony/V3_4/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V3_4/CommonScenariosTest.php @@ -50,7 +50,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -88,7 +88,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -137,7 +137,7 @@ public function provideSpecs() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -192,7 +192,7 @@ public function provideSpecs() ->withExactTags([ 'symfony.route.action' => 'Symfony\Bundle\TwigBundle\Controller\ExceptionController@showAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V3_4/TemplateEnginesTest.php b/tests/Integrations/Symfony/V3_4/TemplateEnginesTest.php index 91fd087a37..910c479081 100644 --- a/tests/Integrations/Symfony/V3_4/TemplateEnginesTest.php +++ b/tests/Integrations/Symfony/V3_4/TemplateEnginesTest.php @@ -30,7 +30,7 @@ public function testAlternateTemplatingEngine() 'symfony.route.action' => 'AppBundle\Controller\HomeController@indexAction', 'symfony.route.name' => 'alternate_templating', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/alternate_templating', + 'http.url' => 'http://localhost/alternate_templating', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V3_4/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V3_4/TraceSearchConfigTest.php index 37664d4627..b7ffd3eba3 100644 --- a/tests/Integrations/Symfony/V3_4/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V3_4/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'AppBundle\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V4_0/CommonScenariosTest.php b/tests/Integrations/Symfony/V4_0/CommonScenariosTest.php index 4586d2344b..e93bc42506 100644 --- a/tests/Integrations/Symfony/V4_0/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V4_0/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -89,7 +89,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -136,7 +136,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -204,7 +204,7 @@ public function provideSpecs() )->withExactTags([ 'symfony.route.action' => 'Symfony\Bundle\TwigBundle\Controller\ExceptionController@showAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V4_0/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V4_0/TraceSearchConfigTest.php index e5d213e98f..1cacc0f82a 100644 --- a/tests/Integrations/Symfony/V4_0/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V4_0/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V4_2/CommonScenariosTest.php b/tests/Integrations/Symfony/V4_2/CommonScenariosTest.php index c67f0a7563..f2a703c53d 100644 --- a/tests/Integrations/Symfony/V4_2/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V4_2/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -89,7 +89,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -136,7 +136,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -202,7 +202,7 @@ public function provideSpecs() )->withExactTags([ 'symfony.route.action' => 'Symfony\Bundle\TwigBundle\Controller\ExceptionController@showAction', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V4_2/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V4_2/TraceSearchConfigTest.php index 53cfc7923d..5e7d684b29 100644 --- a/tests/Integrations/Symfony/V4_2/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V4_2/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V4_4/AutomatedLoginEventsTest.php b/tests/Integrations/Symfony/V4_4/AutomatedLoginEventsTest.php index 6d103b407c..c54ff95387 100644 --- a/tests/Integrations/Symfony/V4_4/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Symfony/V4_4/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "symfony44"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_4_4/public/index.php'; diff --git a/tests/Integrations/Symfony/V4_4/CommonScenariosTest.php b/tests/Integrations/Symfony/V4_4/CommonScenariosTest.php index 94ececacf8..5ef29b708f 100644 --- a/tests/Integrations/Symfony/V4_4/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V4_4/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -91,7 +91,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -138,7 +138,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -192,7 +192,7 @@ public function provideSpecs() 'GET /does_not_exist' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V4_4/PathParamsTest.php b/tests/Integrations/Symfony/V4_4/PathParamsTest.php index a32171670c..0b0ba229ce 100644 --- a/tests/Integrations/Symfony/V4_4/PathParamsTest.php +++ b/tests/Integrations/Symfony/V4_4/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "symfony44"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_4_4/public/index.php'; diff --git a/tests/Integrations/Symfony/V4_4/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V4_4/TraceSearchConfigTest.php index 9183677fd5..a751f93854 100644 --- a/tests/Integrations/Symfony/V4_4/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V4_4/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V5_0/CommonScenariosTest.php b/tests/Integrations/Symfony/V5_0/CommonScenariosTest.php index 4116ee1725..4877a3df41 100644 --- a/tests/Integrations/Symfony/V5_0/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V5_0/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -91,7 +91,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -138,7 +138,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -192,7 +192,7 @@ public function provideSpecs() 'GET /does_not_exist' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V5_0/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V5_0/TraceSearchConfigTest.php index 88945977d5..58e5e95683 100644 --- a/tests/Integrations/Symfony/V5_0/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V5_0/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V5_1/CommonScenariosTest.php b/tests/Integrations/Symfony/V5_1/CommonScenariosTest.php index 119786964d..4007c20974 100644 --- a/tests/Integrations/Symfony/V5_1/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V5_1/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -91,7 +91,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -138,7 +138,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -192,7 +192,7 @@ public function provideSpecs() 'GET /does_not_exist' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V5_1/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V5_1/TraceSearchConfigTest.php index 2f6f832c0e..9a6022439a 100644 --- a/tests/Integrations/Symfony/V5_1/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V5_1/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V5_2/AutomatedLoginEventsTest.php b/tests/Integrations/Symfony/V5_2/AutomatedLoginEventsTest.php index edc72a4272..00972cd43d 100644 --- a/tests/Integrations/Symfony/V5_2/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Symfony/V5_2/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "symfony52"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_5_2/public/index.php'; diff --git a/tests/Integrations/Symfony/V5_2/CommonScenariosTest.php b/tests/Integrations/Symfony/V5_2/CommonScenariosTest.php index abd905e649..26e2a39be0 100644 --- a/tests/Integrations/Symfony/V5_2/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V5_2/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -89,7 +89,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -134,7 +134,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -184,7 +184,7 @@ public function provideSpecs() 'GET /does_not_exist' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V5_2/PathParamsTest.php b/tests/Integrations/Symfony/V5_2/PathParamsTest.php index d5a4de6a5b..2be773101d 100644 --- a/tests/Integrations/Symfony/V5_2/PathParamsTest.php +++ b/tests/Integrations/Symfony/V5_2/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "symfony52"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_5_2/public/index.php'; diff --git a/tests/Integrations/Symfony/V5_2/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V5_2/TraceSearchConfigTest.php index c86903991a..2e391cbb73 100644 --- a/tests/Integrations/Symfony/V5_2/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V5_2/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V6_2/AutomatedLoginEventsTest.php b/tests/Integrations/Symfony/V6_2/AutomatedLoginEventsTest.php index e9d2bafa19..78f77e16ac 100644 --- a/tests/Integrations/Symfony/V6_2/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Symfony/V6_2/AutomatedLoginEventsTest.php @@ -9,6 +9,7 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "symfony62"; public function createUser($email) { $this->connection()->exec('insert into user (roles, email, password) VALUES ("{}", "'.$email.'", "$2y$13$WNnAxSuifzgXGx9kYfFr.eMaXzE50MmrMnXxmrlZqxSa21oiMyy0i")'); diff --git a/tests/Integrations/Symfony/V6_2/CommonScenariosTest.php b/tests/Integrations/Symfony/V6_2/CommonScenariosTest.php index 0aaa11a617..ec2400813c 100644 --- a/tests/Integrations/Symfony/V6_2/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V6_2/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -89,7 +89,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -134,7 +134,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -184,7 +184,7 @@ public function provideSpecs() 'GET /does_not_exist' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V6_2/ConsoleCommandTest.php b/tests/Integrations/Symfony/V6_2/ConsoleCommandTest.php index 5c812014d1..d43c4afd04 100644 --- a/tests/Integrations/Symfony/V6_2/ConsoleCommandTest.php +++ b/tests/Integrations/Symfony/V6_2/ConsoleCommandTest.php @@ -15,7 +15,7 @@ protected static function getConsoleScript() public function testScenario() { - list($traces) = $this->inCli(self::getConsoleScript(), [ + list($traces) = $this->inCli(static::getConsoleScript(), [ 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', 'DD_TRACE_EXEC_ENABLED' => 'false', ], [], 'about'); diff --git a/tests/Integrations/Symfony/V6_2/PathParamsTest.php b/tests/Integrations/Symfony/V6_2/PathParamsTest.php index 3175d34218..659a5426c1 100644 --- a/tests/Integrations/Symfony/V6_2/PathParamsTest.php +++ b/tests/Integrations/Symfony/V6_2/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "symfony62"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_6_2/public/index.php'; diff --git a/tests/Integrations/Symfony/V6_2/TraceSearchConfigTest.php b/tests/Integrations/Symfony/V6_2/TraceSearchConfigTest.php index 85716ce0de..a48c7aafbd 100644 --- a/tests/Integrations/Symfony/V6_2/TraceSearchConfigTest.php +++ b/tests/Integrations/Symfony/V6_2/TraceSearchConfigTest.php @@ -43,7 +43,7 @@ public function testScenario() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/Symfony/V7_0/AutomatedLoginEventsTest.php b/tests/Integrations/Symfony/V7_0/AutomatedLoginEventsTest.php index 98e777fcf5..f84e3b7b50 100644 --- a/tests/Integrations/Symfony/V7_0/AutomatedLoginEventsTest.php +++ b/tests/Integrations/Symfony/V7_0/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "symfony70"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/Symfony/Version_7_0/public/index.php'; diff --git a/tests/Integrations/Symfony/V7_0/CommonScenariosTest.php b/tests/Integrations/Symfony/V7_0/CommonScenariosTest.php index 641326f2ec..220c89f012 100644 --- a/tests/Integrations/Symfony/V7_0/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/V7_0/CommonScenariosTest.php @@ -51,7 +51,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleAction', 'symfony.route.name' => 'simple', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -89,7 +89,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@simpleViewAction', 'symfony.route.name' => 'simple_view', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -134,7 +134,7 @@ public function provideSpecs() 'symfony.route.action' => 'App\Controller\CommonScenariosController@errorAction', 'symfony.route.name' => 'error', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', @@ -184,7 +184,7 @@ public function provideSpecs() 'GET /does_not_exist' )->withExactTags([ 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/does_not_exist?key=value&', + 'http.url' => 'http://localhost/does_not_exist?key=value&', 'http.status_code' => '404', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', diff --git a/tests/Integrations/WordPress/V4_8/AutomatedLoginEventsTest.php b/tests/Integrations/WordPress/V4_8/AutomatedLoginEventsTest.php index 89b60e8978..f17cd82ce6 100644 --- a/tests/Integrations/WordPress/V4_8/AutomatedLoginEventsTest.php +++ b/tests/Integrations/WordPress/V4_8/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "wp48"; + protected $users_table = 'wp_users'; protected static function getAppIndexScript() diff --git a/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php b/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php index 99ec806edb..393dfb33d4 100644 --- a/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php +++ b/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php @@ -10,6 +10,8 @@ class CommonScenariosTest extends WebFrameworkTestCase { + public static $database = "wp48"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_4_8/index.php'; @@ -18,7 +20,7 @@ protected static function getAppIndexScript() public static function ddSetUpBeforeClass() { parent::ddSetUpBeforeClass(); - $pdo = new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + $pdo = new \PDO('mysql:host=mysql_integration;dbname=wp48', 'test', 'test'); $pdo->exec(file_get_contents(__DIR__ . '/../../../Frameworks/WordPress/Version_4_8/wp_2019-10-01.sql')); } diff --git a/tests/Integrations/WordPress/V4_8/PathParamsTest.php b/tests/Integrations/WordPress/V4_8/PathParamsTest.php index f7f4fc5327..01ab216279 100644 --- a/tests/Integrations/WordPress/V4_8/PathParamsTest.php +++ b/tests/Integrations/WordPress/V4_8/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "wp48"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_4_8/index.php'; diff --git a/tests/Integrations/WordPress/V5_5/AutomatedLoginEventsTest.php b/tests/Integrations/WordPress/V5_5/AutomatedLoginEventsTest.php index bea6d652d7..595227131f 100644 --- a/tests/Integrations/WordPress/V5_5/AutomatedLoginEventsTest.php +++ b/tests/Integrations/WordPress/V5_5/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "wp55"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_5_5/index.php'; diff --git a/tests/Integrations/WordPress/V5_5/CommonScenariosTest.php b/tests/Integrations/WordPress/V5_5/CommonScenariosTest.php index 7dddd3b840..86093271c4 100644 --- a/tests/Integrations/WordPress/V5_5/CommonScenariosTest.php +++ b/tests/Integrations/WordPress/V5_5/CommonScenariosTest.php @@ -11,6 +11,8 @@ class CommonScenariosTest extends WebFrameworkTestCase { + public static $database = "wp55"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_5_5/index.php'; @@ -19,7 +21,7 @@ protected static function getAppIndexScript() public static function ddSetUpBeforeClass() { parent::ddSetUpBeforeClass(); - $pdo = new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + $pdo = new \PDO('mysql:host=mysql_integration;dbname=wp55', 'test', 'test'); $pdo->exec(file_get_contents(__DIR__ . '/../../../Frameworks/WordPress/Version_5_5/wp_2020-10-21.sql')); } diff --git a/tests/Integrations/WordPress/V5_5/PathParamsTest.php b/tests/Integrations/WordPress/V5_5/PathParamsTest.php index 22878ce4c9..9081e57492 100644 --- a/tests/Integrations/WordPress/V5_5/PathParamsTest.php +++ b/tests/Integrations/WordPress/V5_5/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "wp55"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_5_5/index.php'; diff --git a/tests/Integrations/WordPress/V5_9/AutomatedLoginEventsTest.php b/tests/Integrations/WordPress/V5_9/AutomatedLoginEventsTest.php index f21b212d1e..aee17e961a 100644 --- a/tests/Integrations/WordPress/V5_9/AutomatedLoginEventsTest.php +++ b/tests/Integrations/WordPress/V5_9/AutomatedLoginEventsTest.php @@ -9,6 +9,8 @@ */ class AutomatedLoginEventsTest extends AutomatedLoginEventsTestSuite { + public static $database = "wp59"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_5_9/index.php'; diff --git a/tests/Integrations/WordPress/V5_9/CommonScenariosTest.php b/tests/Integrations/WordPress/V5_9/CommonScenariosTest.php index 767dea9a05..19a50fb1de 100644 --- a/tests/Integrations/WordPress/V5_9/CommonScenariosTest.php +++ b/tests/Integrations/WordPress/V5_9/CommonScenariosTest.php @@ -11,6 +11,8 @@ class CommonScenariosTest extends WebFrameworkTestCase { + public static $database = "wp59"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_5_9/index.php'; @@ -19,7 +21,7 @@ protected static function getAppIndexScript() public function ddSetUp() { parent::ddSetUp(); - $pdo = new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + $pdo = new \PDO('mysql:host=mysql_integration;dbname=wp59', 'test', 'test'); $pdo->exec(file_get_contents(__DIR__ . '/../../../Frameworks/WordPress/Version_5_5/wp_2020-10-21.sql')); } diff --git a/tests/Integrations/WordPress/V5_9/PathParamsTest.php b/tests/Integrations/WordPress/V5_9/PathParamsTest.php index 1dbe1200bd..b7421ec643 100644 --- a/tests/Integrations/WordPress/V5_9/PathParamsTest.php +++ b/tests/Integrations/WordPress/V5_9/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "wp59"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_5_9/index.php'; diff --git a/tests/Integrations/WordPress/V6_1/CommonScenariosTest.php b/tests/Integrations/WordPress/V6_1/CommonScenariosTest.php index ef6b06b61f..793b74eb2c 100644 --- a/tests/Integrations/WordPress/V6_1/CommonScenariosTest.php +++ b/tests/Integrations/WordPress/V6_1/CommonScenariosTest.php @@ -7,6 +7,8 @@ class CommonScenariosTest extends WebFrameworkTestCase { + public static $database = "wp61"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_6_1/index.php'; @@ -15,7 +17,7 @@ protected static function getAppIndexScript() public function ddSetUp() { parent::ddSetUp(); - $pdo = new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + $pdo = new \PDO('mysql:host=mysql_integration;dbname=wp61', 'test', 'test'); $pdo->exec(file_get_contents(__DIR__ . '/../../../Frameworks/WordPress/Version_6_1/scripts/wp_initdb.sql')); } diff --git a/tests/Integrations/WordPress/V6_1/PathParamsTest.php b/tests/Integrations/WordPress/V6_1/PathParamsTest.php index 9dd5f30c46..e39bc5774a 100644 --- a/tests/Integrations/WordPress/V6_1/PathParamsTest.php +++ b/tests/Integrations/WordPress/V6_1/PathParamsTest.php @@ -9,6 +9,8 @@ */ class PathParamsTest extends PathParamsTestSuite { + public static $database = "wp61"; + protected static function getAppIndexScript() { return __DIR__ . '/../../../Frameworks/WordPress/Version_6_1/index.php'; diff --git a/tests/Integrations/Yii/V2_0/CommonScenariosTest.php b/tests/Integrations/Yii/V2_0/CommonScenariosTest.php index d9a3399364..d1f9bcafc5 100644 --- a/tests/Integrations/Yii/V2_0/CommonScenariosTest.php +++ b/tests/Integrations/Yii/V2_0/CommonScenariosTest.php @@ -49,7 +49,7 @@ public function provideSpecs() 'GET /simple' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/simple?key=value&', + Tag::HTTP_URL => 'http://localhost/simple?key=value&', Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'app\controllers\SimpleController::actionIndex', 'app.route.path' => '/simple', @@ -93,7 +93,7 @@ public function provideSpecs() 'GET /simple_view' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/simple_view?key=value&', + Tag::HTTP_URL => 'http://localhost/simple_view?key=value&', Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'app\controllers\SimpleController::actionView', 'app.route.path' => '/simple_view', @@ -140,7 +140,7 @@ public function provideSpecs() 'GET /error' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/error?key=value&', + Tag::HTTP_URL => 'http://localhost/error?key=value&', Tag::HTTP_STATUS_CODE => '500', 'app.endpoint' => 'app\controllers\SimpleController::actionError', 'app.route.path' => '/error', @@ -212,7 +212,7 @@ public function provideSpecs() 'GET /parameterized/?' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/parameterized/paramValue', + Tag::HTTP_URL => 'http://localhost/parameterized/paramValue', Tag::HTTP_STATUS_CODE => '200', 'app.endpoint' => 'app\controllers\SimpleController::actionParameterized', 'app.route.path' => '/parameterized/:value', diff --git a/tests/Integrations/Yii/V2_0/ModuleTest.php b/tests/Integrations/Yii/V2_0/ModuleTest.php index 69fc8e42ce..6b1a52b85b 100644 --- a/tests/Integrations/Yii/V2_0/ModuleTest.php +++ b/tests/Integrations/Yii/V2_0/ModuleTest.php @@ -39,7 +39,7 @@ public function testGet() 'GET /forum/?/?/?' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/forum/new-york/new-york/manhattan?key=value&', + Tag::HTTP_URL => 'http://localhost/forum/new-york/new-york/manhattan?key=value&', Tag::HTTP_STATUS_CODE => '200', 'app.route.path' => '/forum/:state/:city/:neighborhood', Tag::HTTP_ROUTE => '/forum/:state/:city/:neighborhood', diff --git a/tests/Integrations/Yii/V2_0/ParameterizedRouteTest.php b/tests/Integrations/Yii/V2_0/ParameterizedRouteTest.php index d91d67c221..915f3a698e 100644 --- a/tests/Integrations/Yii/V2_0/ParameterizedRouteTest.php +++ b/tests/Integrations/Yii/V2_0/ParameterizedRouteTest.php @@ -39,7 +39,7 @@ public function testGet() 'GET /homes/?/?/?' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/homes/new-york/new-york/manhattan?key=value&', + Tag::HTTP_URL => 'http://localhost/homes/new-york/new-york/manhattan?key=value&', Tag::HTTP_STATUS_CODE => '200', 'app.route.path' => '/homes/:state/:city/:neighborhood', Tag::HTTP_ROUTE => '/homes/:state/:city/:neighborhood', diff --git a/tests/Integrations/Yii/V2_0/YiiDetailsTest.php b/tests/Integrations/Yii/V2_0/YiiDetailsTest.php index 84d737236a..05532f12cd 100644 --- a/tests/Integrations/Yii/V2_0/YiiDetailsTest.php +++ b/tests/Integrations/Yii/V2_0/YiiDetailsTest.php @@ -40,7 +40,7 @@ public function testRootIndexRoute() 'GET /site/index' )->withExactTags([ Tag::HTTP_METHOD => 'GET', - Tag::HTTP_URL => 'http://localhost:9999/site/index', + Tag::HTTP_URL => 'http://localhost/site/index', Tag::HTTP_STATUS_CODE => '200', 'app.route.path' => '/site/index', Tag::HTTP_ROUTE => '/site/index', diff --git a/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php b/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php index cf21f2d3ff..77a8660b67 100644 --- a/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php +++ b/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php @@ -39,7 +39,7 @@ public function provideSpecs() 'zf1.action' => 'index', 'zf1.route_name' => 'default', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => "server", Tag::COMPONENT => "zendframework", @@ -52,7 +52,7 @@ public function provideSpecs() 'zf1.action' => 'view', 'zf1.route_name' => 'my_simple_view_route', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => "server", Tag::COMPONENT => "zendframework", @@ -65,7 +65,7 @@ public function provideSpecs() 'zf1.action' => 'error', 'zf1.route_name' => 'default', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => "server", Tag::COMPONENT => "zendframework", diff --git a/tests/Integrations/ZendFramework/V1/TraceSearchConfigTest.php b/tests/Integrations/ZendFramework/V1/TraceSearchConfigTest.php index 6626a13db9..67c1b4ea11 100644 --- a/tests/Integrations/ZendFramework/V1/TraceSearchConfigTest.php +++ b/tests/Integrations/ZendFramework/V1/TraceSearchConfigTest.php @@ -40,7 +40,7 @@ public function testScenario() 'zf1.action' => 'index', 'zf1.route_name' => 'default', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'zendframework', diff --git a/tests/Integrations/ZendFramework/V1_21/CommonScenariosTest.php b/tests/Integrations/ZendFramework/V1_21/CommonScenariosTest.php index cb4e7e22d6..729d1a6137 100644 --- a/tests/Integrations/ZendFramework/V1_21/CommonScenariosTest.php +++ b/tests/Integrations/ZendFramework/V1_21/CommonScenariosTest.php @@ -39,7 +39,7 @@ public function provideSpecs() 'zf1.action' => 'index', 'zf1.route_name' => 'default', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple?key=value&', + 'http.url' => 'http://localhost/simple?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => "server", Tag::COMPONENT => "zendframework", @@ -52,7 +52,7 @@ public function provideSpecs() 'zf1.action' => 'view', 'zf1.route_name' => 'my_simple_view_route', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple_view?key=value&', + 'http.url' => 'http://localhost/simple_view?key=value&', 'http.status_code' => '200', Tag::SPAN_KIND => "server", Tag::COMPONENT => "zendframework", @@ -65,7 +65,7 @@ public function provideSpecs() 'zf1.action' => 'error', 'zf1.route_name' => 'default', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/error?key=value&', + 'http.url' => 'http://localhost/error?key=value&', 'http.status_code' => '500', Tag::SPAN_KIND => "server", Tag::COMPONENT => "zendframework", diff --git a/tests/Integrations/ZendFramework/V1_21/TraceSearchConfigTest.php b/tests/Integrations/ZendFramework/V1_21/TraceSearchConfigTest.php index a86cfcf267..88040937ec 100644 --- a/tests/Integrations/ZendFramework/V1_21/TraceSearchConfigTest.php +++ b/tests/Integrations/ZendFramework/V1_21/TraceSearchConfigTest.php @@ -40,7 +40,7 @@ public function testScenario() 'zf1.action' => 'index', 'zf1.route_name' => 'default', 'http.method' => 'GET', - 'http.url' => 'http://localhost:9999/simple', + 'http.url' => 'http://localhost/simple', 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'zendframework', diff --git a/tests/OpenTelemetry/composer.json b/tests/OpenTelemetry/composer.json new file mode 100644 index 0000000000..607a2888d4 --- /dev/null +++ b/tests/OpenTelemetry/composer.json @@ -0,0 +1,8 @@ +{ + "name": "datadog/dd-trace-tests", + "require": { + "open-telemetry/sdk": "@stable", + "open-telemetry/extension-propagator-b3": "@stable", + "open-telemetry/opentelemetry-logger-monolog": "@stable" + } +} diff --git a/tests/OpenTracer1Unit/composer.json b/tests/OpenTracer1Unit/composer.json new file mode 100644 index 0000000000..f80f627d48 --- /dev/null +++ b/tests/OpenTracer1Unit/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "opentracing/opentracing": "^1.0" + } +} diff --git a/tests/OpenTracerUnit/GlobalTracerTest.php b/tests/OpenTracerUnit/GlobalTracerTest.php deleted file mode 100644 index 59fd6c6f94..0000000000 --- a/tests/OpenTracerUnit/GlobalTracerTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertInstanceOf( - '\DDTrace\OpenTracer\Tracer', - \OpenTracing\GlobalTracer::get() - ); - } -} diff --git a/tests/OpenTracerUnit/ScopeManagerTest.php b/tests/OpenTracerUnit/ScopeManagerTest.php deleted file mode 100644 index 178fa819df..0000000000 --- a/tests/OpenTracerUnit/ScopeManagerTest.php +++ /dev/null @@ -1,63 +0,0 @@ -assertNull($scopeManager->getActive()); - } - - public function testActivateSuccess() - { - $tracer = Tracer::make(new NoopTransport()); - $span = $tracer->startSpan(self::OPERATION_NAME); - $scopeManager = new ScopeManager(new DDScopeManager()); - $scopeManager->activate($span, false); - $this->assertSame( - $span->unwrapped(), - $scopeManager->getActive()->getSpan()->unwrapped() - ); - } - - public function testGetScopeReturnsNull() - { - $tracer = Tracer::make(new NoopTransport()); - $tracer->startSpan(self::OPERATION_NAME); - $this->assertNull($tracer->getScopeManager()->getActive()); - } - - public function testGetScopeSuccess() - { - $tracer = Tracer::make(new NoopTransport()); - $span = $tracer->startSpan(self::OPERATION_NAME); - $scope = $tracer->getScopeManager()->activate($span, false); - $this->assertSame( - $scope->unwrapped(), - $tracer->getScopeManager()->getActive()->unwrapped() - ); - } -} diff --git a/tests/OpenTracerUnit/ScopeTest.php b/tests/OpenTracerUnit/ScopeTest.php deleted file mode 100644 index 13867f5531..0000000000 --- a/tests/OpenTracerUnit/ScopeTest.php +++ /dev/null @@ -1,29 +0,0 @@ -prophesize('DDTrace\Contracts\Span'); - $span->finish()->shouldBeCalled(); - $ddScope = new DDScope(new ScopeManager(), $span->reveal(), true); - $scope = new Scope($ddScope); - $scope->close(); - } - - public function testScopeDoesNotFinishesSpanOnClose() - { - $span = $this->prophesize('DDTrace\Contracts\Span'); - $span->finish()->shouldNotBeCalled(); - $ddScope = new DDScope(new ScopeManager(), $span->reveal(), false); - $scope = new Scope($ddScope); - $scope->close(); - } -} diff --git a/tests/OpenTracerUnit/SpanContextTest.php b/tests/OpenTracerUnit/SpanContextTest.php deleted file mode 100644 index 58f4321d72..0000000000 --- a/tests/OpenTracerUnit/SpanContextTest.php +++ /dev/null @@ -1,65 +0,0 @@ -assertNull($context->unwrapped()->getParentId()); - } - - public function testCreateChildSpanContext() - { - $parentContext = new SpanContext( - DDSpanContext::createAsRoot([ - self::BAGGAGE_ITEM_KEY => self::BAGGAGE_ITEM_VALUE, - ]) - ); - $childContext = new SpanContext( - DDSpanContext::createAsChild($parentContext->unwrapped()) - ); - - $this->assertSame($childContext->unwrapped()->getTraceId(), $parentContext->unwrapped()->getTraceId()); - $this->assertSame($childContext->unwrapped()->getParentId(), $parentContext->unwrapped()->getSpanId()); - $this->assertSame(iterator_to_array($childContext), iterator_to_array($parentContext)); - } - - public function testGetBaggageItemsReturnsExpectedValues() - { - $context = new SpanContext( - DDSpanContext::createAsRoot([ - self::BAGGAGE_ITEM_KEY => self::BAGGAGE_ITEM_VALUE, - ]) - ); - - $this->assertSame(self::BAGGAGE_ITEM_VALUE, $context->getBaggageItem(self::BAGGAGE_ITEM_KEY)); - $this->assertNull($context->getBaggageItem(self::BAGGAGE_ITEM_KEY2)); - } - - public function testAddBaggageItemsReturnsExpectedContext() - { - $context = new SpanContext( - DDSpanContext::createAsRoot([ - self::BAGGAGE_ITEM_KEY => self::BAGGAGE_ITEM_VALUE, - ]) - ); - $newContext = $context->withBaggageItem(self::BAGGAGE_ITEM_KEY2, self::BAGGAGE_ITEM_VALUE2); - - $this->assertSame(self::BAGGAGE_ITEM_VALUE, $newContext->getBaggageItem(self::BAGGAGE_ITEM_KEY)); - $this->assertNull($context->getBaggageItem(self::BAGGAGE_ITEM_KEY2)); - $this->assertSame(self::BAGGAGE_ITEM_VALUE2, $newContext->getBaggageItem(self::BAGGAGE_ITEM_KEY2)); - } -} diff --git a/tests/OpenTracerUnit/SpanTest.php b/tests/OpenTracerUnit/SpanTest.php deleted file mode 100644 index 83ce510036..0000000000 --- a/tests/OpenTracerUnit/SpanTest.php +++ /dev/null @@ -1,165 +0,0 @@ -createSpan(); - $span->setTag(self::TAG_KEY, self::TAG_VALUE); - - $this->assertSame(self::OPERATION_NAME, $span->getOperationName()); - $this->assertSame(self::SERVICE, $span->unwrapped()->getService()); - $this->assertSame(self::RESOURCE, $span->unwrapped()->getResource()); - $this->assertSame(self::TAG_VALUE, $span->unwrapped()->getTag(self::TAG_KEY)); - } - - public function testOverwriteOperationNameSuccess() - { - $span = $this->createSpan(); - $span->overwriteOperationName(self::ANOTHER_NAME); - $this->assertSame(self::ANOTHER_NAME, $span->getOperationName()); - } - - public function testSpanTagsRemainImmutableAfterFinishing() - { - $span = $this->createSpan(); - $span->finish(); - - $span->setTag(self::TAG_KEY, self::TAG_VALUE); - $this->assertNull($span->unwrapped()->getTag(self::TAG_KEY)); - } - - public function testSpanTagWithErrorCreatesExpectedTags() - { - $span = $this->createSpan(); - $span->setTag(Tag::ERROR, new Exception(self::EXCEPTION_MESSAGE)); - - $this->assertTrue($span->unwrapped()->hasError()); - $this->assertEquals($span->unwrapped()->getTag(Tag::ERROR_MSG), self::EXCEPTION_MESSAGE); - $this->assertEquals($span->unwrapped()->getTag(Tag::ERROR_TYPE), 'Exception'); - } - - public function testSpanTagWithErrorBoolProperlyMarksError() - { - $span = $this->createSpan(); - - $span->setTag(Tag::ERROR, true); - $this->assertTrue($span->unwrapped()->hasError()); - - $span->setTag(Tag::ERROR, false); - $this->assertFalse($span->unwrapped()->hasError()); - } - - public function testLogWithErrorBoolProperlyMarksError() - { - $span = $this->createSpan(); - - $span->log([Tag::LOG_ERROR => true]); - $this->assertTrue($span->unwrapped()->hasError()); - - $span->log([Tag::LOG_ERROR => false]); - $this->assertFalse($span->unwrapped()->hasError()); - } - - public function testLogWithEventErrorMarksSpanWithError() - { - $span = $this->createSpan(); - - $span->log([Tag::LOG_EVENT => 'error']); - $this->assertTrue($span->unwrapped()->hasError()); - } - - public function testLogWithOtherEventDoesNotMarkSpanWithError() - { - $span = $this->createSpan(); - - $span->log([Tag::LOG_EVENT => 'some other event']); - $this->assertFalse($span->unwrapped()->hasError()); - - $span->log([Tag::LOG_ERROR => false]); - $this->assertFalse($span->unwrapped()->hasError()); - } - - public function testSpanLogWithErrorCreatesExpectedTags() - { - foreach ([Tag::LOG_ERROR, Tag::LOG_ERROR_OBJECT] as $key) { - $span = $this->createSpan(); - $span->log([$key => new Exception(self::EXCEPTION_MESSAGE)]); - - $this->assertTrue($span->unwrapped()->hasError()); - $this->assertEquals($span->unwrapped()->getTag(Tag::ERROR_MSG), self::EXCEPTION_MESSAGE); - $this->assertEquals($span->unwrapped()->getTag(Tag::ERROR_TYPE), 'Exception'); - } - } - - public function testSpanLogStackAddsExpectedTag() - { - $span = $this->createSpan(); - $span->log([Tag::LOG_STACK => self::DUMMY_STACK_TRACE]); - - $this->assertFalse($span->unwrapped()->hasError()); - $this->assertEquals($span->unwrapped()->getTag(Tag::ERROR_STACK), self::DUMMY_STACK_TRACE); - } - - public function testSpanLogMessageAddsExpectedTag() - { - $span = $this->createSpan(); - $span->log([Tag::LOG_MESSAGE => self::EXCEPTION_MESSAGE]); - - $this->assertFalse($span->unwrapped()->hasError()); - $this->assertEquals($span->unwrapped()->getTag(Tag::ERROR_MSG), self::EXCEPTION_MESSAGE); - } - - public function testAddCustomTagsSuccess() - { - $span = $this->createSpan(); - $span->setTag(Tag::SERVICE_NAME, self::ANOTHER_SERVICE); - $span->setTag(Tag::RESOURCE_NAME, self::ANOTHER_RESOURCE); - $span->setTag(Tag::SPAN_TYPE, self::ANOTHER_TYPE); - - $this->assertEquals(self::ANOTHER_SERVICE, $span->unwrapped()->getService()); - $this->assertEquals(self::ANOTHER_RESOURCE, $span->unwrapped()->getResource()); - $this->assertEquals(self::ANOTHER_TYPE, $span->unwrapped()->getType()); - } - - public function testAddTagsFailsForInvalidTagKey() - { - $this->setExpectedException( - '\DDTrace\Exceptions\InvalidSpanArgument', - 'Invalid key type in given span tags. Expected string, got integer.' - ); - $span = $this->createSpan(); - $span->setTag(1, self::TAG_VALUE); - } - - private function createSpan() - { - $spanData = \DDTrace\start_span(); - $spanData->name = self::OPERATION_NAME; - $spanData->service = self::SERVICE; - $spanData->resource = self::RESOURCE; - $span = new DDSpan($spanData, DDSpanContext::createAsRoot()); - return new Span($span); - } -} diff --git a/tests/OpenTracerUnit/TracerTest.php b/tests/OpenTracerUnit/TracerTest.php deleted file mode 100644 index 08997452ba..0000000000 --- a/tests/OpenTracerUnit/TracerTest.php +++ /dev/null @@ -1,315 +0,0 @@ -startSpan(self::OPERATION_NAME)->unwrapped(); - $this->assertSame("", $span->getTag(Tag::ENV)); - $this->assertSame("", $span->getTag(Tag::VERSION)); - } - - public function testTracerWithConstructorArg() - { - $tracer = new Tracer(\DDTrace\GlobalTracer::get()); - - $span = $tracer->startSpan(self::OPERATION_NAME)->unwrapped(); - $this->assertSame("", $span->getTag(Tag::ENV)); - $this->assertSame("", $span->getTag(Tag::VERSION)); - } - - public function testCreateSpanWithDefaultTags() - { - $tracer = Tracer::make(new NoopTransport()); - - $span = $tracer->startSpan(self::OPERATION_NAME)->unwrapped(); - $this->assertSame("", $span->getTag(Tag::ENV)); - $this->assertSame("", $span->getTag(Tag::VERSION)); - } - - public function testCreateSpanWithEnvAndVersionConfigured() - { - $this->putEnvAndReloadConfig(['DD_ENV=' . self::ENVIRONMENT, 'DD_VERSION=' . self::VERSION]); - $tracer = Tracer::make(new NoopTransport()); - - $span = $tracer->startSpan(self::OPERATION_NAME)->unwrapped(); - $this->assertSame(self::ENVIRONMENT, $span->getTag(Tag::ENV)); - $this->assertSame(self::VERSION, $span->getTag(Tag::VERSION)); - } - - public function testCreateSpanWithExpectedValues() - { - $tracer = Tracer::make(new NoopTransport()); - $startTime = 12345; - $span = $tracer - ->startSpan(self::OPERATION_NAME, [ - 'tags' => [ - self::TAG_KEY => self::TAG_VALUE - ], - 'start_time' => $startTime, - ]) - ->unwrapped(); - - $this->assertSame(self::OPERATION_NAME, $span->getOperationName()); - $this->assertSame(self::TAG_VALUE, $span->getTag(self::TAG_KEY)); - $this->assertSame($startTime, $span->getStartTime()); - } - - public function testStartSpanAsChild() - { - $context = DDSpanContext::createAsRoot(); - $tracer = Tracer::make(new NoopTransport()); - $span = $tracer - ->startSpan(self::OPERATION_NAME, [ - 'child_of' => $context, - ]) - ->unwrapped(); - $this->assertSame($context->getSpanId(), $span->getParentId()); - $this->assertNull($span->getTag(Tag::PID)); - } - - public function testStartSpanAsRootWithPid() - { - $tracer = Tracer::make(new NoopTransport()); - $span = $tracer->startSpan(self::OPERATION_NAME)->unwrapped(); - $metrics = $span->getMetrics(); - $this->assertArrayHasKey(Tag::PID, $metrics); - $this->assertEquals(getmypid(), $metrics[Tag::PID]); - } - - public function testStartActiveSpan() - { - $tracer = Tracer::make(new NoopTransport()); - $scope = $tracer->startActiveSpan(self::OPERATION_NAME); - $this->assertSame( - $scope->unwrapped(), - $tracer->getScopeManager()->getActive()->unwrapped() - ); - } - - public function testStartActiveSpanAsChild() - { - $tracer = Tracer::make(new NoopTransport()); - $parentScope = $tracer->startActiveSpan(self::OPERATION_NAME); - $parentSpan = $parentScope->getSpan(); - $parentSpan->setTag(Tag::SERVICE_NAME, 'parent_service'); - $childScope = $tracer->startActiveSpan(self::ANOTHER_OPERATION_NAME); - $this->assertSame( - $childScope->unwrapped(), - $tracer->getScopeManager()->getActive()->unwrapped() - ); - $this->assertSame( - $parentScope->getSpan()->unwrapped()->getSpanId(), - $childScope->getSpan()->unwrapped()->getParentId() - ); - $this->assertSame( - $parentScope->getSpan()->unwrapped()->getService(), - $childScope->getSpan()->unwrapped()->getService() - ); - } - - public function testInjectThrowsUnsupportedFormatException() - { - $this->setExpectedException('\DDTrace\Exceptions\UnsupportedFormat'); - $carrier = []; - - $tracer = Tracer::make(new NoopTransport()); - $tracer->inject( - new SpanContext(DDSpanContext::createAsRoot()), - self::FORMAT, - $carrier - ); - } - - public function testInjectCallsTheRightInjector() - { - $context = new SpanContext(DDSpanContext::createAsRoot()); - $carrier = []; - - $propagator = $this->prophesize('DDTrace\Propagator'); - $propagator->inject($context->unwrapped(), $carrier)->shouldBeCalled(); - $tracer = Tracer::make(new NoopTransport(), [self::FORMAT => $propagator->reveal()]); - $tracer->inject($context, self::FORMAT, $carrier); - } - - public function testExtractThrowsUnsupportedFormatException() - { - $this->setExpectedException('\DDTrace\Exceptions\UnsupportedFormat'); - $carrier = []; - $tracer = Tracer::make(new NoopTransport()); - $tracer->extract(self::FORMAT, $carrier); - } - - public function testExtractCallsTheRightExtractor() - { - $expectedContext = DDSpanContext::createAsRoot(); - $carrier = []; - - $propagator = $this->prophesize('DDTrace\Propagator'); - $propagator->extract($carrier)->shouldBeCalled()->willReturn($expectedContext); - $tracer = Tracer::make(new NoopTransport(), [self::FORMAT => $propagator->reveal()]); - $actualContext = $tracer->extract(self::FORMAT, $carrier); - $this->assertSame($expectedContext, $actualContext->unwrapped()); - } - - public function testOTSpanContextAsParent() - { - GlobalTracer::set(Tracer::make()); - - $tracer = GlobalTracer::get(); - - $header = <<extract(Formats\TEXT_MAP, $carrier); - $B = $tracer->startActiveSpan('B', ['child_of' => $context]); - - $otcontext = $B->getSpan()->getContext(); - self::assertInstanceOf('DDTrace\OpenTracer\SpanContext', $otcontext); - self::assertEquals('2409624703365403319', $otcontext->unwrapped()->getParentId()); - } - - public function testOTStartSpanOptions() - { - GlobalTracer::set(Tracer::make()); - $tracer = GlobalTracer::get(); - - $now = time(); - $scope = $tracer->startActiveSpan( - self::OPERATION_NAME, - \OpenTracing\StartSpanOptions::create([ - 'tags' => [ - \OpenTracing\Tags\SPAN_KIND => \OpenTracing\Tags\SPAN_KIND_MESSAGE_BUS_PRODUCER, - 'message_id' => 'some id' - ], - 'start_time' => $now, - ]) - ); - self::assertInstanceOf('DDTrace\OpenTracer\Scope', $scope); - $scope = $scope->unwrapped(); - $span = $scope->getSpan(); - self::assertSame(\OpenTracing\Tags\SPAN_KIND_MESSAGE_BUS_PRODUCER, $span->getTag(\OpenTracing\Tags\SPAN_KIND)); - self::assertSame($now, $span->getStartTime()); - } - - public function testOnlyFinishedTracesAreBeingSent() - { - self::markTestIncomplete(); - $transport = $this->prophesize('DDTrace\Transport'); - $tracer = Tracer::make($transport->reveal()); - $span = $tracer->startSpan(self::OPERATION_NAME); - $tracer->startSpan(self::ANOTHER_OPERATION_NAME, [ - 'child_of' => $span->unwrapped(), - ]); - $span->finish(); - - $span2 = $tracer->startSpan(self::OPERATION_NAME); - $span3 = $tracer->startSpan(self::ANOTHER_OPERATION_NAME, [ - 'child_of' => $span2->unwrapped(), - ]); - $span2->finish(); - $span3->finish(); - - $transport->send([ - [$span2->unwrapped(), $span3->unwrapped()], - ])->shouldBeCalled(); - - $tracer->flush(); - } - - public function testPrioritySamplingIsAssigned() - { - self::markTestIncomplete(); - $tracer = Tracer::make(new DebugTransport()); - $tracer->startSpan(self::OPERATION_NAME); - $this->assertSame( - PrioritySampling::AUTO_KEEP, - $tracer->unwrapped()->getPrioritySampling() - ); - } - - public function testPrioritySamplingInheritedFromDistributedTracingContext() - { - self::markTestIncomplete(); - $distributedTracingContext = new DDSpanContext('', '', '', [], true); - $distributedTracingContext->setPropagatedPrioritySampling(PrioritySampling::USER_REJECT); - $tracer = Tracer::make(new DebugTransport()); - $tracer->startSpan(self::OPERATION_NAME, [ - 'child_of' => $distributedTracingContext, - ]); - $this->assertSame( - PrioritySampling::USER_REJECT, - $tracer->unwrapped()->getPrioritySampling() - ); - } - - public function testUnfinishedSpansAreNotSentOnFlush() - { - $transport = new DebugTransport(); - $tracer = Tracer::make($transport); - $tracer->startActiveSpan('root'); - $tracer->startActiveSpan('child'); - - $tracer->flush(); - - $this->assertEmpty($transport->getTraces()); - } - - public function testUnfinishedSpansCanBeFinishedOnFlush() - { - self::markTestIncomplete(); - Configuration::replace(\Mockery::mock('\DDTrace\Configuration', [ - 'isAutofinishSpansEnabled' => true, - 'isPrioritySamplingEnabled' => false, - 'isDebugModeEnabled' => false, - ])); - - $transport = new DebugTransport(); - $tracer = Tracer::make($transport); - $tracer->startActiveSpan('root'); - $tracer->startActiveSpan('child'); - - $tracer->flush(); - $sent = $transport->getTraces(); - $this->assertSame('root', $sent[0][0]->getOperationName()); - $this->assertSame('child', $sent[0][1]->getOperationName()); - } -} diff --git a/tests/Sapi/CliServer/CliServer.php b/tests/Sapi/CliServer/CliServer.php index 7a97858ff1..9ce308b554 100644 --- a/tests/Sapi/CliServer/CliServer.php +++ b/tests/Sapi/CliServer/CliServer.php @@ -58,8 +58,6 @@ public function __construct($indexFile, $host, $port, array $envs = [], array $i public function start() { if (getenv('PHPUNIT_COVERAGE')) { - $this->inis['auto_prepend_file'] = __DIR__ . '/../../save_code_coverage.php'; - $xdebugExtension = glob(PHP_EXTENSION_DIR . '/xdebug*.so'); $xdebugExtension = end($xdebugExtension); $this->inis['zend_extension'] = $xdebugExtension; diff --git a/tests/Sapi/Frankenphp/FrankenphpServer.php b/tests/Sapi/Frankenphp/FrankenphpServer.php index 3ba424b75a..201ffe5100 100644 --- a/tests/Sapi/Frankenphp/FrankenphpServer.php +++ b/tests/Sapi/Frankenphp/FrankenphpServer.php @@ -52,8 +52,6 @@ public function __construct($indexFile, $host, $port, array $envs = [], array $i $this->envs = $envs; if (getenv('PHPUNIT_COVERAGE')) { - $inis['auto_prepend_file'] = __DIR__ . '/../../save_code_coverage.php'; - $xdebugExtension = glob(PHP_EXTENSION_DIR . '/xdebug*.so'); $xdebugExtension = end($xdebugExtension); $inis['zend_extension'] = $xdebugExtension; diff --git a/tests/Sapi/OctaneServer/OctaneServer.php b/tests/Sapi/OctaneServer/OctaneServer.php index a5c98c66f5..b1543f9d9a 100644 --- a/tests/Sapi/OctaneServer/OctaneServer.php +++ b/tests/Sapi/OctaneServer/OctaneServer.php @@ -58,13 +58,15 @@ public function __construct($artisanFile, $host, $port, array $envs = [], array public function start() { if (getenv('PHPUNIT_COVERAGE')) { - $this->inis['auto_prepend_file'] = __DIR__ . '/../../save_code_coverage.php'; - $xdebugExtension = glob(PHP_EXTENSION_DIR . '/xdebug*.so'); $xdebugExtension = end($xdebugExtension); $this->inis['zend_extension'] = $xdebugExtension; $this->inis['xdebug.mode'] = 'coverage'; } + $token = ini_get('datadog.trace.agent_test_session_token'); + if ($token != "") { + $this->envs["DD_TRACE_AGENT_TEST_SESSION_TOKEN"] = $token; + } $cmd = sprintf( PHP_BINARY . ' %s %s octane:start --server=swoole --host=%s --port=%d', @@ -94,7 +96,7 @@ public function stop() $this->process->stop(0); $cmd = sprintf( - PHP_BINARY . ' %s octane:stop', + PHP_BINARY . ' -d extension=swoole %s octane:stop', $this->artisanFile ); $process = new Process($cmd); diff --git a/tests/Sapi/Roadrunner/RoadrunnerServer.php b/tests/Sapi/Roadrunner/RoadrunnerServer.php index 1715ad8de3..e113fff07d 100644 --- a/tests/Sapi/Roadrunner/RoadrunnerServer.php +++ b/tests/Sapi/Roadrunner/RoadrunnerServer.php @@ -54,6 +54,11 @@ public function __construct($version, $workerFile, $host, $port, array $envs = [ $this->version = $version; $this->workerFile = $workerFile; + $token = ini_get('datadog.trace.agent_test_session_token'); + if ($token != "") { + $envs["DD_TRACE_AGENT_TEST_SESSION_TOKEN"] = $token; + } + $logPath = dirname($workerFile) . '/' . self::ERROR_LOG; switch (php_uname('m')) { @@ -67,8 +72,6 @@ public function __construct($version, $workerFile, $host, $port, array $envs = [ } if (getenv('PHPUNIT_COVERAGE')) { - $inis['auto_prepend_file'] = __DIR__ . '/../../save_code_coverage.php'; - $xdebugExtension = glob(PHP_EXTENSION_DIR . '/xdebug*.so'); $xdebugExtension = end($xdebugExtension); $inis['zend_extension'] = $xdebugExtension; diff --git a/tests/Sapi/SwooleServer/SwooleServer.php b/tests/Sapi/SwooleServer/SwooleServer.php index 15e0c29a45..5db3e79479 100644 --- a/tests/Sapi/SwooleServer/SwooleServer.php +++ b/tests/Sapi/SwooleServer/SwooleServer.php @@ -19,6 +19,11 @@ final class SwooleServer implements Sapi */ private $indexFile; + /** + * @var int + */ + private $port; + /** * @var array */ @@ -31,12 +36,14 @@ final class SwooleServer implements Sapi /** * @param string $indexFile + * @param int $port * @param array $envs * @param array $inis */ - public function __construct($indexFile, array $envs = [], array $inis = []) + public function __construct($indexFile, $port, array $envs = [], array $inis = []) { $this->indexFile = $indexFile; + $this->port = $port; $this->envs = $envs; $this->inis = $inis; } @@ -44,8 +51,6 @@ public function __construct($indexFile, array $envs = [], array $inis = []) public function start() { if (getenv('PHPUNIT_COVERAGE')) { - $this->inis['auto_prepend_file'] = __DIR__ . '/../../save_code_coverage.php'; - $xdebugExtension = glob(PHP_EXTENSION_DIR . '/xdebug*.so'); $xdebugExtension = end($xdebugExtension); $this->inis['zend_extension'] = $xdebugExtension; @@ -53,9 +58,10 @@ public function start() } $cmd = sprintf( - PHP_BINARY . ' %s %s', + PHP_BINARY . ' %s %s %d', new IniSerializer($this->inis), - $this->indexFile + $this->indexFile, + $this->port ); $envs = new EnvSerializer($this->envs); $processCmd = "$envs exec $cmd"; diff --git a/tests/Unit/Util/OrphansTest.php b/tests/Unit/Util/OrphansTest.php index fdddae81fb..58c61555f1 100644 --- a/tests/Unit/Util/OrphansTest.php +++ b/tests/Unit/Util/OrphansTest.php @@ -8,8 +8,6 @@ final class OrphansTest extends IntegrationTestCase { - use TracerTestTrait; - static function foo() { // no-op diff --git a/tests/WebServer.php b/tests/WebServer.php index 227d52bb3f..667c55d118 100644 --- a/tests/WebServer.php +++ b/tests/WebServer.php @@ -20,7 +20,7 @@ final class WebServer { const FCGI_HOST = '0.0.0.0'; - const FCGI_PORT = 9797; + const FCGI_PORT = 9797 - GLOBAL_PORT_OFFSET; const ERROR_LOG_NAME = 'dd_php_error.log'; @@ -131,6 +131,9 @@ public function start() if (!isset($this->envs['DD_TRACE_DEBUG'])) { $this->inis['datadog.trace.debug'] = 'true'; } + if (GLOBAL_AUTO_PREPEND_FILE) { + $this->inis['auto_prepend_file'] = GLOBAL_AUTO_PREPEND_FILE; + } $this->errorLogSize = (int)@filesize($this->defaultInis['error_log']); @@ -154,6 +157,7 @@ public function start() } elseif ($this->isSwoole) { $this->sapi = new SwooleServer( $this->indexFile, + $this->port, $this->envs, $this->inis ); diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index d3153bc92a..0000000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,26 +0,0 @@ -= 8) { - require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_typed.php'; -} else { - require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_untyped.php'; -} diff --git a/tests/bootstrap_utils.php b/tests/bootstrap_common.php similarity index 68% rename from tests/bootstrap_utils.php rename to tests/bootstrap_common.php index ee33d680bf..bb63230d53 100644 --- a/tests/bootstrap_utils.php +++ b/tests/bootstrap_common.php @@ -2,6 +2,18 @@ namespace DDTrace\Tests; +if (getenv('DD_AUTOLOAD_NO_COMPILE') == 'true' && (false !== getenv('CI') || false !== getenv('CIRCLECI'))) { + throw new Exception('Tests must run using the _generated.php script in CI'); +} + +// Setting an environment variable to signal we are in a tests run +putenv('DD_TEST_EXECUTION=1'); + +if (function_exists("dd_trace_env_config") && \dd_trace_env_config("DD_TRACE_SIDECAR_TRACE_SENDER")) { + // Only explicit flushes with sidecar + putenv("DD_TRACE_AGENT_FLUSH_INTERVAL=3000000"); +} + function missing_ddtrace_class_fatal_autoloader($class) { // project-specific namespace prefix @@ -36,12 +48,6 @@ function missing_ddtrace_class_fatal_autoloader($class) throw new \Exception("add " . $class . " to bridge/_files.php or bridge/dd_register_optional_deps_autoloader.php"); } -function prepend_test_autoloaders() -{ - spl_autoload_register('\DDTrace\Tests\missing_ddtrace_class_fatal_autoloader', true); - require_once __DIR__ . '/vendor/autoload.php'; -} -prepend_test_autoloaders(); +spl_autoload_register('\DDTrace\Tests\missing_ddtrace_class_fatal_autoloader', true); -require_once __DIR__ . '/bootstrap.php'; diff --git a/tests/bootstrap_phpbench.php b/tests/bootstrap_phpbench.php new file mode 100644 index 0000000000..749a900f41 --- /dev/null +++ b/tests/bootstrap_phpbench.php @@ -0,0 +1,5 @@ += 8) { + require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_typed.php'; +} else { + require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_untyped.php'; +} + +require_once __DIR__ . '/Appsec/Mock.php'; + +function update_test_agent_session_token($token) { + if (defined('GLOBAL_AUTO_PREPEND_RSRC')) { + ini_set("datadog.trace.agent_test_session_token", $token); + ftruncate(GLOBAL_AUTO_PREPEND_RSRC, 0); + fseek(GLOBAL_AUTO_PREPEND_RSRC, 0); + fwrite(GLOBAL_AUTO_PREPEND_RSRC, " strlen(__DIR__)) { + if (file_exists("$path/vendor/autoload.php")) { + putenv("COMPOSER_ROOT_VERSION=1.0.0"); // silence composer + \DDTrace\Tests\Common\IntegrationTestCase::$autoloadPath = "$path/vendor/autoload.php"; + require_once \DDTrace\Tests\Common\IntegrationTestCase::$autoloadPath; + return; + } elseif (file_exists("$path/composer.json")) { + \DDTrace\Testing\trigger_error("Found $path/composer.json, but seems not installed", E_USER_ERROR); + } + $path = dirname($path); + } +}; +if (class_exists('PHPUnit\Runner\StandardTestSuiteLoader')) { + \DDTrace\hook_method('PHPUnit\Util\FileLoader', 'load', $hook); + \DDTrace\hook_method('PHPUnit\Runner\StandardTestSuiteLoader', 'load', $hook); +} elseif (method_exists('PHPUnit\Runner\TestSuiteLoader', 'loadSuiteClassFile')) { + \DDTrace\hook_method('PHPUnit\Runner\TestSuiteLoader', 'loadSuiteClassFile', $hook); +} else { + \DDTrace\hook_method('PHPUnit\Runner\TestSuiteLoader', 'load', $hook); +} diff --git a/tests/clean-composer-scenario-locks.sh b/tests/clean-composer-scenario-locks.sh deleted file mode 100755 index 6870138391..0000000000 --- a/tests/clean-composer-scenario-locks.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p .scenarios.lock -rm -rf .scenarios.lock/**/*.lock diff --git a/tests/composer.json b/tests/composer.json index 5bf7a45c51..f714b921d3 100644 --- a/tests/composer.json +++ b/tests/composer.json @@ -17,7 +17,6 @@ "phpunit/phpunit": "<10", "phpspec/prophecy": "*", "symfony/process": "<5", - "g1a/composer-test-scenarios": "~3.0", "ext-sockets": "*" }, "config": { @@ -26,230 +25,5 @@ "php-http/discovery": true, "cakephp/plugin-installer": true } - }, - "extra": { - "scenarios": { - "elasticsearch1": { - "require": { - "elasticsearch/elasticsearch": "1.2.*", - "symfony/event-dispatcher": "~2.7" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "elasticsearch7": { - "require": { - "elasticsearch/elasticsearch": "~7.16", - "symfony/event-dispatcher": "~2.7" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "elasticsearch8": { - "require": { - "elasticsearch/elasticsearch": "~8.5" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "guzzle5": { - "require": { - "guzzlehttp/guzzle": "~5.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "guzzle6": { - "require": { - "guzzlehttp/guzzle": "~6.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "guzzle7": { - "require": { - "guzzlehttp/guzzle": "~7.0", - "guzzlehttp/promises": "~2.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "laminaslog2": { - "require": { - "laminas/laminas-log": "~2.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "mongodb1": { - "require": { - "mongodb/mongodb": "1.*" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "monolog1": { - "require": { - "monolog/monolog": "~1.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "monolog2": { - "require": { - "monolog/monolog": "~2.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "monolog3": { - "require": { - "monolog/monolog": "~3.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "openai": { - "require": { - "openai-php/client": "@stable", - "guzzlehttp/guzzle": "^7.8.1", - "guzzlehttp/psr7": "^2.6.2" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "opentelemetry1": { - "require": { - "open-telemetry/sdk": "@stable", - "open-telemetry/extension-propagator-b3": "@stable", - "open-telemetry/opentelemetry-logger-monolog": "@stable" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "opentracing_beta5": { - "require": { - "opentracing/opentracing": "1.0.0-beta5" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "opentracing_beta6": { - "require": { - "opentracing/opentracing": "1.0.0-beta6" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "opentracing10": { - "require": { - "opentracing/opentracing": "^1.0" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "predis1": { - "require": { - "predis/predis": "^1.1" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "phpredis3": { - "scenario-options": { - "create-lockfile": false - }, - "scripts": { - "pre-autoload-dump": [ - "sudo pecl uninstall redis", - "printf 'no' | sudo pecl install -f redis-3.1.6" - ] - } - }, - "phpredis4": { - "scenario-options": { - "create-lockfile": false - }, - "scripts": { - "pre-autoload-dump": [ - "sudo pecl uninstall redis", - "printf 'no' | sudo pecl install -f redis-4.3.0" - ] - } - }, - "phpredis5": { - "scenario-options": { - "create-lockfile": false - }, - "scripts": { - "pre-autoload-dump": [ - "sudo pecl uninstall redis", - "printf 'no' | sudo pecl install -f redis-5.3.4" - ] - } - }, - "swoole5": { - "scenario-options": { - "create-lockfile": false - }, - "scripts": { - "pre-autoload-dump": [ - "sudo pecl uninstall swoole", - "printf 'no' | sudo pecl install -f swoole-5.1.2" - ] - } - }, - "amqp2": { - "require": { - "php-amqplib/php-amqplib": "^v2.6.2" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "amqp35": { - "require": { - "php-amqplib/php-amqplib": "^3.5" - }, - "scenario-options": { - "create-lockfile": false - } - }, - "benchmarks": { - "require": { - "monolog/monolog": "~2.0", - "open-telemetry/sdk": "@stable", - "phpbench/phpbench": "^1.0" - }, - "scenario-options": { - "create-lockfile": false - },"scripts": { - "pre-autoload-dump": [ - "sudo pecl uninstall redis", - "printf 'no' | sudo pecl install -f redis-5.3.4" - ] - } - } - }, - "scenario-options": { - "dependency-licenses": false - } } } diff --git a/tests/ext/appsec/sca_flag_is_sent_01.phpt b/tests/ext/appsec/sca_flag_is_sent_01.phpt index 028f483e5d..14362cff42 100644 --- a/tests/ext/appsec/sca_flag_is_sent_01.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_01.phpt @@ -6,6 +6,7 @@ This configuration is used by the backend to display/charge customers --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/appsec/sca_flag_is_sent_02.phpt b/tests/ext/appsec/sca_flag_is_sent_02.phpt index c74dfeb528..03b8cc1c44 100644 --- a/tests/ext/appsec/sca_flag_is_sent_02.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_02.phpt @@ -6,6 +6,7 @@ This configuration is used by the backend to display/charge customers --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/appsec/sca_flag_is_sent_03.phpt b/tests/ext/appsec/sca_flag_is_sent_03.phpt index 251dde5602..2b91959d75 100644 --- a/tests/ext/appsec/sca_flag_is_sent_03.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_03.phpt @@ -6,6 +6,7 @@ This configuration is used by the backend to display/charge customers --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/appsec/sca_flag_is_sent_04.phpt b/tests/ext/appsec/sca_flag_is_sent_04.phpt index 0b581833af..f6259abe9a 100644 --- a/tests/ext/appsec/sca_flag_is_sent_04.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_04.phpt @@ -6,6 +6,7 @@ This configuration is used by the backend to display/charge customers --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/appsec/sca_flag_is_sent_05.phpt b/tests/ext/appsec/sca_flag_is_sent_05.phpt index a8dfdaae9f..cf39574103 100644 --- a/tests/ext/appsec/sca_flag_is_sent_05.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_05.phpt @@ -6,6 +6,7 @@ This configuration is used by the backend to display/charge customers --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/appsec/sca_test.inc b/tests/ext/appsec/sca_test.inc index e8e45c0527..a64ae8e474 100644 --- a/tests/ext/appsec/sca_test.inc +++ b/tests/ext/appsec/sca_test.inc @@ -21,7 +21,10 @@ for ($i = 0; $i < 100; ++$i) { foreach (file(__DIR__ . '/'.$id.'-telemetry.out') as $l) { if ($l) { $json = json_decode($l, true); - if ($json && ($json["application"]["service_name"] ?? "") != "background_sender-php-service") { + if ($json) { + if ($json["application"]["service_name"] == "background_sender-php-service" || $json["application"]["service_name"] == "datadog-ipc-helper") { + continue; + } array_push($batches, ...($json["request_type"] == "message-batch" ? $json["payload"] : [$json])); } } diff --git a/tests/ext/background-sender/CONFLICTS b/tests/ext/background-sender/CONFLICTS deleted file mode 100644 index 0702cb5bfb..0000000000 --- a/tests/ext/background-sender/CONFLICTS +++ /dev/null @@ -1 +0,0 @@ -all diff --git a/tests/ext/background-sender/agent_headers.phpt b/tests/ext/background-sender/agent_headers.phpt index 24485667b1..d52471ceeb 100644 --- a/tests/ext/background-sender/agent_headers.phpt +++ b/tests/ext/background-sender/agent_headers.phpt @@ -11,6 +11,8 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=666 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_AUTO_FLUSH_ENABLED=1 +--INI-- +datadog.trace.agent_test_session_token=background-sender/agent_headers --FILE-- ---INI-- -ddtrace.cgroup_file={PWD}/stubs/cgroup.docker --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_BGS_ENABLED=1 @@ -15,6 +13,9 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=666 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_AUTO_FLUSH_ENABLED=1 +--INI-- +ddtrace.cgroup_file={PWD}/stubs/cgroup.docker +datadog.trace.agent_test_session_token=background-sender/agent_headers_container_id --FILE-- ---INI-- -ddtrace.cgroup_file={PWD}/stubs/cgroup.empty --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_BGS_ENABLED=1 @@ -14,6 +12,9 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_AUTO_FLUSH_ENABLED=1 +--INI-- +ddtrace.cgroup_file={PWD}/stubs/cgroup.empty +datadog.trace.agent_test_session_token=background-sender/agent_headers_container_id_empty --FILE-- ---INI-- -ddtrace.cgroup_file={PWD}/stubs/cgroup.fargate.1.4 --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_BGS_ENABLED=1 @@ -15,6 +13,9 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_AUTO_FLUSH_ENABLED=1 +--INI-- +ddtrace.cgroup_file={PWD}/stubs/cgroup.fargate.1.4 +datadog.trace.agent_test_session_token=background-sender/agent_headers_container_id_fargate --FILE-- setResponse(["rate_by_service" => ["service:,env:" => 0]]); echo "Initial sampling: {$get_sampling()}\n"; $rr->setResponse(["rate_by_service" => ["service:,env:" => 0, "service:foo,env:none" => 1]]); +dd_trace_internal_fn("synchronous_flush"); \DDTrace\start_span(); \DDTrace\close_span(); @@ -39,6 +42,7 @@ echo "Generic sampling: {$get_sampling()}\n"; // reset it for other tests $rr->setResponse(["rate_by_service" => []]); +dd_trace_internal_fn("synchronous_flush"); $s = \DDTrace\start_span(); $s->service = "foo"; diff --git a/tests/ext/background-sender/agent_sampling_sidecar.phpt b/tests/ext/background-sender/agent_sampling_sidecar.phpt index 2f7dbb5d63..e8c32b40bb 100644 --- a/tests/ext/background-sender/agent_sampling_sidecar.phpt +++ b/tests/ext/background-sender/agent_sampling_sidecar.phpt @@ -12,42 +12,97 @@ DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_SIDECAR_TRACE_SENDER=1 DD_TRACE_AUTO_FLUSH_ENABLED=1 +--INI-- +datadog.trace.agent_test_session_token=background-sender/agent_sampling_sidecar --FILE-- replayRequest(); // cleanup possible leftover -$get_sampling = function() use ($rr) { +$expected = [1,0,1]; +$error = false; +$get_sampling = function() use ($rr, &$expected, &$error) { $root = json_decode($rr->waitForDataAndReplay()["body"], true); $spans = $root["chunks"][0]["spans"] ?? $root[0]; - return $spans[0]["metrics"]["_sampling_priority_v1"]; + $priority = $spans[0]["metrics"]["_sampling_priority_v1"]; + if ($priority != array_shift($expected)) { + $error = true; + } + return $priority; }; -$rr->setResponse(["rate_by_service" => ["service:,env:" => 0]]); +$rr->setResponse(["rate_by_service" => ["service:,env:" => 0, "service:agent_sampling_sidecar_test,env:first" => 1]]); \DDTrace\start_span(); \DDTrace\close_span(); echo "Initial sampling: {$get_sampling()}\n"; -$rr->setResponse(["rate_by_service" => ["service:,env:" => 0, "service:foo,env:none" => 1]]); +checkUpdated("service:agent_sampling_sidecar_test,env:first"); +$rr->setResponse(["rate_by_service" => ["service:,env:" => 0, "service:foo,env:none" => 1, "service:agent_sampling_sidecar_test,env:second" => 0]]); + +recordContents(); \DDTrace\start_span(); \DDTrace\close_span(); +recordContents(); + +checkUpdated("service:agent_sampling_sidecar_test,env:second"); echo "Generic sampling: {$get_sampling()}\n"; // reset it for other tests $rr->setResponse(["rate_by_service" => []]); +recordContents(); $s = \DDTrace\start_span(); $s->service = "foo"; $s->env = "none"; \DDTrace\close_span(); +recordContents(); echo "Specific sampling: {$get_sampling()}\n"; +if ($error && PHP_OS === "Linux") { + var_dump($contents); +} + ?> --EXPECTF-- [ddtrace] [info] Flushing trace of size 1 to send-queue for http://request-replayer:80 diff --git a/tests/ext/background-sender/background_sender_survives_setuid.phpt b/tests/ext/background-sender/background_sender_survives_setuid.phpt index 9f4d458494..9ebd6e70bb 100644 --- a/tests/ext/background-sender/background_sender_survives_setuid.phpt +++ b/tests/ext/background-sender/background_sender_survives_setuid.phpt @@ -8,6 +8,7 @@ To test this we will issue a setgroups() via the libc wrapper (which distributes + --ENV-- diff --git a/tests/ext/background-sender/sidecar_fallback.phpt b/tests/ext/background-sender/sidecar_fallback.phpt index 896605c7b5..045d1784a0 100644 --- a/tests/ext/background-sender/sidecar_fallback.phpt +++ b/tests/ext/background-sender/sidecar_fallback.phpt @@ -13,6 +13,8 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_SERVICE=service +--INI-- +datadog.trace.agent_test_session_token=background-sender/sidecar_fallback --FILE-- endpoint . '/replay'), true); + $allRequests = json_decode(file_get_contents($this->endpoint . '/replay', false, stream_context_create([ + "http" => [ + "header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ], + ])), true); if ($allRequests && $ignoreTelemetry) { $allRequests = array_values(array_filter($allRequests, function ($v) { return $v["uri"] != '/telemetry/proxy/api/v2/apmtelemetry'; })); } @@ -78,7 +82,10 @@ class RequestReplayer "http" => [ "method" => "POST", "content" => json_encode($array), - "header" => "Content-Type: application/json" + "header" => [ + "Content-Type: application/json", + "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ] ], ])); } diff --git a/tests/ext/integrations/source_code/001-git_metadata_injection_from_valid_files.phpt b/tests/ext/integrations/source_code/001/git_metadata_injection_from_valid_files.phpt similarity index 96% rename from tests/ext/integrations/source_code/001-git_metadata_injection_from_valid_files.phpt rename to tests/ext/integrations/source_code/001/git_metadata_injection_from_valid_files.phpt index 1cec81010f..43cc2d33ba 100644 --- a/tests/ext/integrations/source_code/001-git_metadata_injection_from_valid_files.phpt +++ b/tests/ext/integrations/source_code/001/git_metadata_injection_from_valid_files.phpt @@ -10,7 +10,7 @@ if (getenv('PHP_PEAR_RUNTESTS') === '1') die("skip: The pecl run-tests path is n --FILE-- --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/telemetry/config.phpt b/tests/ext/telemetry/config.phpt index 846b776520..9f033ced61 100644 --- a/tests/ext/telemetry/config.phpt +++ b/tests/ext/telemetry/config.phpt @@ -4,6 +4,7 @@ Report user config telemetry --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 @@ -30,7 +31,7 @@ for ($i = 0; $i < 100; ++$i) { foreach (file(__DIR__ . '/config-telemetry.out') as $l) { if ($l) { $json = json_decode($l, true); - if ($json["request_type"] == "app-started") { + if ($json && $json["request_type"] == "app-started" && $json["application"]["service_name"] != "background_sender-php-service" && $json["application"]["service_name"] != "datadog-ipc-helper") { $cfg = $json["payload"]["configuration"]; print_r(array_values(array_filter($cfg, function($c) { return $c["origin"] == "EnvVar" && $c["name"] != "trace.sources_path" && $c["name"] != "trace.sidecar_trace_sender"; diff --git a/tests/ext/telemetry/integration.phpt b/tests/ext/telemetry/integration.phpt index 5b5ace0727..d8001a3f8c 100644 --- a/tests/ext/telemetry/integration.phpt +++ b/tests/ext/telemetry/integration.phpt @@ -4,6 +4,7 @@ Signal integration telemetry --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/telemetry/metrics_logs_created.phpt b/tests/ext/telemetry/metrics_logs_created.phpt index 042a4df218..34f23f1107 100644 --- a/tests/ext/telemetry/metrics_logs_created.phpt +++ b/tests/ext/telemetry/metrics_logs_created.phpt @@ -4,6 +4,7 @@ --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/telemetry/metrics_spans_created.phpt b/tests/ext/telemetry/metrics_spans_created.phpt index d8cd82f60d..5b637b6479 100644 --- a/tests/ext/telemetry/metrics_spans_created.phpt +++ b/tests/ext/telemetry/metrics_spans_created.phpt @@ -4,6 +4,7 @@ --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/telemetry/simple.phpt b/tests/ext/telemetry/simple.phpt index 5c05d8a8bb..72d6322900 100644 --- a/tests/ext/telemetry/simple.phpt +++ b/tests/ext/telemetry/simple.phpt @@ -4,6 +4,7 @@ Simple telemetry test --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 @@ -33,6 +34,9 @@ for ($i = 0; $i < 100; ++$i) { foreach (file(__DIR__ . '/simple-telemetry.out') as $l) { if ($l) { $json = json_decode($l, true); + if ($json["application"]["service_name"] == "background_sender-php-service" || $json["application"]["service_name"] == "datadog-ipc-helper") { + continue; + } array_push($batches, ...($json["request_type"] == "message-batch" ? $json["payload"] : [$json])); } } diff --git a/tests/phpbench-opcache.json b/tests/phpbench-opcache.json index c80674e3f7..da07cee8c6 100644 --- a/tests/phpbench-opcache.json +++ b/tests/phpbench-opcache.json @@ -1,6 +1,6 @@ { "$schema":"vendor/phpbench/phpbench/phpbench.schema.json", - "runner.bootstrap": "./bootstrap_utils.php", + "runner.bootstrap": "./bootstrap_phpbench.php", "runner.path": "Benchmarks", "runner.file_pattern": "*Bench.php", "runner.php_env": { diff --git a/tests/phpbench.json b/tests/phpbench.json index 2fc223ab07..579e4e5ab7 100644 --- a/tests/phpbench.json +++ b/tests/phpbench.json @@ -1,6 +1,6 @@ { "$schema":"vendor/phpbench/phpbench/phpbench.schema.json", - "runner.bootstrap": "./bootstrap_utils.php", + "runner.bootstrap": "./bootstrap_phpbench.php", "runner.path": "Benchmarks", "runner.file_pattern": "*Bench.php", "runner.php_env": { diff --git a/tests/phpunit.xml b/tests/phpunit.xml index af8482b1e3..fd3902de3a 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -7,7 +7,7 @@ beStrictAboutResourceUsageDuringSmallTests="true" beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutTodoAnnotatedTests="true" - bootstrap="./bootstrap.php" + bootstrap="./bootstrap_phpunit.php" colors="true" columns="max" verbose="true" diff --git a/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json b/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json index 80b3573821..94f4baa203 100644 --- a/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json +++ b/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "health_check/ping", "http.status_code": "200", - "http.url": "http://localhost:9999/health_check/ping", + "http.url": "http://localhost/health_check/ping", "runtime-id": "ca127e85-a20d-46aa-b510-07e9077a14c9", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json index 369c5a37f1..1da3301fe8 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json @@ -15,7 +15,7 @@ "http.method": "GET", "http.route": "parameterized/(:any)", "http.status_code": "200", - "http.url": "http://localhost:9999/parameterized/paramValue", + "http.url": "http://localhost/parameterized/paramValue", "runtime-id": "26ab24e6-051f-4e1f-9adf-3d609bef6946", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json index 5801d38892..6f87498651 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json @@ -15,7 +15,7 @@ "http.method": "GET", "http.route": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "26ab24e6-051f-4e1f-9adf-3d609bef6946", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json index 8ab53c9245..5170c9dbe3 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "does_not_exist", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "232799b0-3a18-4d64-9c97-4f6b441bb91e", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json index 63eeaac51b..c50a1bd1bf 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "does_not_exist", "http.status_code": "200", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "0caac976-2232-42e0-a186-9f73b4c27f50", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json index 20d5d5b608..93f7dfef97 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json @@ -13,13 +13,13 @@ "_dd.p.tid": "660bead900000000", "app.endpoint": "Error_::index", "component": "codeigniter", - "error.message": "Uncaught Exception (500): datadog in /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 {main}", + "error.message": "Uncaught Exception (500): datadog in /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "91489364-94f6-4030-b3ed-5df886a158a0", "span.kind": "server" }, @@ -38,8 +38,8 @@ "error": 1, "meta": { "component": "codeigniter", - "error.message": "Thrown Exception (500): datadog in /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 {main}", + "error.message": "Thrown Exception (500): datadog in /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 {main}", "error.type": "Exception" } }]] diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json index 8345b56767..fc4999eca5 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json @@ -13,13 +13,13 @@ "_dd.p.tid": "660beb1100000000", "app.endpoint": "Error_::index", "component": "codeigniter", - "error.message": "Uncaught Exception: datadog in /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/ddshim.php(8): require()\n#3 {main}", + "error.message": "Uncaught Exception: datadog in /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/ddshim.php(8): require()\n#3 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "0caac976-2232-42e0-a186-9f73b4c27f50", "span.kind": "server" }, @@ -38,8 +38,8 @@ "error": 1, "meta": { "component": "codeigniter", - "error.message": "Thrown Exception: datadog in /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 /home/circleci/datadog/tests/Frameworks/CodeIgniter/Version_3_1/ddshim.php(8): require()\n#3 {main}", + "error.message": "Thrown Exception: datadog in /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/application/controllers/Error_.php:5", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/system/core/CodeIgniter.php(533): Error_->index()\n#1 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/index.php(315): require_once()\n#2 /home/circleci/app/tests/Frameworks/CodeIgniter/Version_3_1/ddshim.php(8): require()\n#3 {main}", "error.type": "Exception" } }]] diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json index b896206aff..f1c4002d99 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json @@ -15,7 +15,7 @@ "http.method": "GET", "http.route": "simple_view", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "26ab24e6-051f-4e1f-9adf-3d609bef6946", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json index 345b0d7373..fb342972d7 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json @@ -15,7 +15,7 @@ "http.method": "GET", "http.route": "exits", "http.status_code": "200", - "http.url": "http://localhost:9999/exits", + "http.url": "http://localhost/exits", "runtime-id": "de8ed04e-02e4-40ef-be2a-20aeeb1398ef", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_to_missing_route.json index c024f9c3ca..ef64c585d8 100644 --- a/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -12,7 +12,7 @@ "component": "drupal", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "1b0e863a-39eb-40f0-8723-5446bc1f418b", "span.kind": "server", "symfony.route.action": "Drupal\\system\\Controller\\Http4xxController@on404" @@ -81,8 +81,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException: No routes found for \"/does_not_exist\". in /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Routing/Router.php:144", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Routing/AccessAwareRouter.php(90): Drupal\\Core\\Routing\\Router->matchRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/EventListener/RouterListener.php(105): Drupal\\Core\\Routing\\AccessAwareRouter->matchRequest()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#13 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET http://localhost:9999/does_not_exist\" in /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/EventListener/RouterListener.php:127\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#11 {main}", + "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException: No routes found for \"/does_not_exist\". in /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Routing/Router.php:144", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Routing/AccessAwareRouter.php(90): Drupal\\Core\\Routing\\Router->matchRequest()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/EventListener/RouterListener.php(105): Drupal\\Core\\Routing\\AccessAwareRouter->matchRequest()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#13 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET http://localhost/does_not_exist\" in /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/EventListener/RouterListener.php:127\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#11 {main}", "error.type": "Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException" } }, diff --git a/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_exception.json index bcd55ced67..ee7ee96a88 100644 --- a/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_exception.json @@ -11,12 +11,12 @@ "meta": { "_dd.p.dm": "-0", "component": "drupal", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/modules/datadog/src/Controller/DatadogController.php:19", - "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Render/Renderer.php(592): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(181): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#13 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/modules/datadog/src/Controller/DatadogController.php:19", + "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Render/Renderer.php(592): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(181): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#13 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "9909a648-1886-44d2-b341-b1ddc0f62f34", "span.kind": "server", "symfony.route.action": "Drupal\\datadog\\Controller\\DatadogController@error", @@ -122,8 +122,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/modules/datadog/src/Controller/DatadogController.php:19", - "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Render/Renderer.php(592): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(181): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#13 /home/circleci/datadog/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/modules/datadog/src/Controller/DatadogController.php:19", + "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/Render/Renderer.php(592): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(181): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/core/lib/Drupal/Core/DrupalKernel.php(704): Drupal\\Core\\StackMiddleware\\StackedHttpKernel->handle()\n#13 /home/circleci/app/tests/Frameworks/Drupal/Version_10_1/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_view.json index c6c7b140b0..16afc5508e 100644 --- a/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.drupal.v10_1.common_scenarios_test.test_scenario_get_with_view.json @@ -12,7 +12,7 @@ "component": "drupal", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view", + "http.url": "http://localhost/simple_view", "runtime-id": "cecb7d5b-eb5a-49f8-8374-29dce2d6fce6", "span.kind": "server", "symfony.route.action": "Drupal\\datadog\\Controller\\DatadogController@simpleView", diff --git a/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_to_missing_route.json index 107ebf6b3e..0dcc9db5c7 100644 --- a/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -12,7 +12,7 @@ "component": "drupal", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "692423cd-81b3-449e-a67e-43150df99f74", "span.kind": "server", "symfony.route.action": "Drupal\\system\\Controller\\Http4xxController@on404" @@ -81,8 +81,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException: No routes found for \"/does_not_exist\". in /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Routing/Router.php:125", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Routing/AccessAwareRouter.php(92): Drupal\\Core\\Routing\\Router->matchRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/EventListener/RouterListener.php(113): Drupal\\Core\\Routing\\AccessAwareRouter->matchRequest()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(127): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#13 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/EventListener/RouterListener.php:137\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(127): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#11 {main}", + "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException: No routes found for \"/does_not_exist\". in /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Routing/Router.php:125", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Routing/AccessAwareRouter.php(92): Drupal\\Core\\Routing\\Router->matchRequest()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/EventListener/RouterListener.php(113): Drupal\\Core\\Routing\\AccessAwareRouter->matchRequest()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(127): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#13 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/EventListener/RouterListener.php:137\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(127): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#11 {main}", "error.type": "Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException" } }, diff --git a/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_exception.json index 8b289f78b4..f30d57b633 100644 --- a/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_exception.json @@ -11,12 +11,12 @@ "meta": { "_dd.p.dm": "-0", "component": "drupal", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/modules/datadog/src/Controller/DatadogController.php:19", - "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Render/Renderer.php(573): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/modules/datadog/src/Controller/DatadogController.php:19", + "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Render/Renderer.php(573): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "692423cd-81b3-449e-a67e-43150df99f74", "span.kind": "server", "symfony.route.action": "Drupal\\datadog\\Controller\\DatadogController@error", @@ -122,8 +122,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/modules/datadog/src/Controller/DatadogController.php:19", - "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Render/Renderer.php(573): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/datadog/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/modules/datadog/src/Controller/DatadogController.php:19", + "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/Render/Renderer.php(573): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/app/tests/Frameworks/Drupal/Version_8_9/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_view.json index c936976841..42eaba32f3 100644 --- a/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.drupal.v8_9.common_scenarios_test.test_scenario_get_with_view.json @@ -12,7 +12,7 @@ "component": "drupal", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view", + "http.url": "http://localhost/simple_view", "runtime-id": "692423cd-81b3-449e-a67e-43150df99f74", "span.kind": "server", "symfony.route.action": "Drupal\\datadog\\Controller\\DatadogController@simpleView", diff --git a/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_to_missing_route.json index e71b4094b6..30bad45145 100644 --- a/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -12,7 +12,7 @@ "component": "drupal", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "7a7b1e1f-04e8-41dc-82bd-81f24ef6e0a4", "span.kind": "server", "symfony.route.action": "Drupal\\system\\Controller\\Http4xxController@on404" @@ -81,8 +81,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException: No routes found for \"/does_not_exist\". in /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Routing/Router.php:124", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Routing/AccessAwareRouter.php(93): Drupal\\Core\\Routing\\Router->matchRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/EventListener/RouterListener.php(112): Drupal\\Core\\Routing\\AccessAwareRouter->matchRequest()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(145): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#13 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/EventListener/RouterListener.php:136\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(145): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#11 {main}", + "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException: No routes found for \"/does_not_exist\". in /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Routing/Router.php:124", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Routing/AccessAwareRouter.php(93): Drupal\\Core\\Routing\\Router->matchRequest()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/EventListener/RouterListener.php(112): Drupal\\Core\\Routing\\AccessAwareRouter->matchRequest()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(145): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#13 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/EventListener/RouterListener.php:136\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(145): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#11 {main}", "error.type": "Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException" } }, diff --git a/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_exception.json index 7611262ecd..d6a0f8f00d 100644 --- a/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_exception.json @@ -11,12 +11,12 @@ "meta": { "_dd.p.dm": "-0", "component": "drupal", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/modules/datadog/src/Controller/DatadogController.php:19", - "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Render/Renderer.php(580): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(169): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/modules/datadog/src/Controller/DatadogController.php:19", + "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Render/Renderer.php(580): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(169): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "3afe4427-fcda-4ade-bdc5-572a8c5f8b12", "span.kind": "server", "symfony.route.action": "Drupal\\datadog\\Controller\\DatadogController@error", @@ -122,8 +122,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/modules/datadog/src/Controller/DatadogController.php:19", - "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Render/Renderer.php(580): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(169): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/datadog/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/modules/datadog/src/Controller/DatadogController.php:19", + "error.stack": "#0 [internal function]: Drupal\\datadog\\Controller\\DatadogController->error()\n#1 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/Render/Renderer.php(580): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\\Core\\Render\\Renderer->executeInRenderContext()\n#4 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()\n#5 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(169): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\\Core\\StackMiddleware\\Session->handle()\n#9 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle()\n#10 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle()\n#11 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle()\n#12 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\\StackedHttpKernel->handle()\n#13 /home/circleci/app/tests/Frameworks/Drupal/Version_9_5/index.php(19): Drupal\\Core\\DrupalKernel->handle()\n#14 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_view.json index 546d0db745..fbcc57a8a7 100644 --- a/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.drupal.v9_5.common_scenarios_test.test_scenario_get_with_view.json @@ -12,7 +12,7 @@ "component": "drupal", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view", + "http.url": "http://localhost/simple_view", "runtime-id": "7a7b1e1f-04e8-41dc-82bd-81f24ef6e0a4", "span.kind": "server", "symfony.route.action": "Drupal\\datadog\\Controller\\DatadogController@simpleView", diff --git a/tests/snapshots/tests.integrations.guzzle.v7.guzzle_integration_test.test_multi_exec.json b/tests/snapshots/tests.integrations.guzzle.v7.guzzle_integration_test.test_multi_exec.json index 480964ea38..52c0b445a8 100644 --- a/tests/snapshots/tests.integrations.guzzle.v7.guzzle_integration_test.test_multi_exec.json +++ b/tests/snapshots/tests.integrations.guzzle.v7.guzzle_integration_test.test_multi_exec.json @@ -228,7 +228,7 @@ "curl.total_time_us": "1834", "curl.upload_content_length": "-1", "error.message": "Couldn't resolve host name", - "error.stack": "#0 /home/circleci/datadog/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(233): curl_multi_info_read()\n#1 /home/circleci/datadog/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(174): GuzzleHttp\\Handler\\CurlMultiHandler->processMessages()\n#2 /home/circleci/datadog/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(189): GuzzleHttp\\Handler\\CurlMultiHandler->tick()\n#3 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(251): GuzzleHttp\\Handler\\CurlMultiHandler->execute()\n#4 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(227): GuzzleHttp\\Promise\\Promise->invokeWaitFn()\n#5 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(272): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#6 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(229): GuzzleHttp\\Promise\\Promise->invokeWaitList()\n#7 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(69): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#8 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Utils.php(121): GuzzleHttp\\Promise\\Promise->wait()\n#9 /home/circleci/datadog/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(77): GuzzleHttp\\Promise\\Utils::unwrap()\n#10 /home/circleci/datadog/tests/Common/SnapshotTestTrait.php(171): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->DDTrace\\Tests\\Integrations\\Guzzle\\V7\\{closure}()\n#11 /home/circleci/datadog/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(86): DDTrace\\Tests\\Integrations\\Guzzle\\V6\\GuzzleIntegrationTest->isolateTracerSnapshot()\n#12 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1612): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->testMultiExec()\n#13 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1218): PHPUnit\\Framework\\TestCase->runTest()\n#14 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(728): PHPUnit\\Framework\\TestCase->runBare()\n#15 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(968): PHPUnit\\Framework\\TestResult->run()\n#16 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestCase->run()\n#17 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestSuite->run()\n#18 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#19 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(144): PHPUnit\\TextUI\\TestRunner->run()\n#20 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(97): PHPUnit\\TextUI\\Command->run()\n#21 phpvfscomposer:///home/circleci/datadog/tests/vendor/phpunit/phpunit/phpunit(106): PHPUnit\\TextUI\\Command::main()\n#22 /home/circleci/datadog/tests/vendor/bin/phpunit(118): include()\n#23 {main}", + "error.stack": "#0 /home/circleci/app/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(233): curl_multi_info_read()\n#1 /home/circleci/app/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(174): GuzzleHttp\\Handler\\CurlMultiHandler->processMessages()\n#2 /home/circleci/app/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(189): GuzzleHttp\\Handler\\CurlMultiHandler->tick()\n#3 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(251): GuzzleHttp\\Handler\\CurlMultiHandler->execute()\n#4 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(227): GuzzleHttp\\Promise\\Promise->invokeWaitFn()\n#5 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(272): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#6 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(229): GuzzleHttp\\Promise\\Promise->invokeWaitList()\n#7 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(69): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#8 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Utils.php(121): GuzzleHttp\\Promise\\Promise->wait()\n#9 /home/circleci/app/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(77): GuzzleHttp\\Promise\\Utils::unwrap()\n#10 /home/circleci/app/tests/Common/SnapshotTestTrait.php(171): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->DDTrace\\Tests\\Integrations\\Guzzle\\V7\\{closure}()\n#11 /home/circleci/app/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(86): DDTrace\\Tests\\Integrations\\Guzzle\\V6\\GuzzleIntegrationTest->isolateTracerSnapshot()\n#12 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1612): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->testMultiExec()\n#13 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1218): PHPUnit\\Framework\\TestCase->runTest()\n#14 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(728): PHPUnit\\Framework\\TestCase->runBare()\n#15 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(968): PHPUnit\\Framework\\TestResult->run()\n#16 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestCase->run()\n#17 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestSuite->run()\n#18 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#19 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(144): PHPUnit\\TextUI\\TestRunner->run()\n#20 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(97): PHPUnit\\TextUI\\Command->run()\n#21 phpvfscomposer:///home/circleci/app/tests/vendor/phpunit/phpunit/phpunit(106): PHPUnit\\TextUI\\Command::main()\n#22 /home/circleci/app/tests/vendor/bin/phpunit(118): include()\n#23 {main}", "error.type": "curl error", "http.status_code": "0", "http.url": "https://google.wrong/", @@ -496,7 +496,7 @@ "curl.total_time_us": "1572", "curl.upload_content_length": "-1", "error.message": "Couldn't resolve host name", - "error.stack": "#0 /home/circleci/datadog/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(233): curl_multi_info_read()\n#1 /home/circleci/datadog/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(174): GuzzleHttp\\Handler\\CurlMultiHandler->processMessages()\n#2 /home/circleci/datadog/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(189): GuzzleHttp\\Handler\\CurlMultiHandler->tick()\n#3 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(251): GuzzleHttp\\Handler\\CurlMultiHandler->execute()\n#4 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(227): GuzzleHttp\\Promise\\Promise->invokeWaitFn()\n#5 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(272): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#6 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(229): GuzzleHttp\\Promise\\Promise->invokeWaitList()\n#7 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Promise.php(69): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#8 /home/circleci/datadog/tests/vendor/guzzlehttp/promises/src/Utils.php(121): GuzzleHttp\\Promise\\Promise->wait()\n#9 /home/circleci/datadog/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(77): GuzzleHttp\\Promise\\Utils::unwrap()\n#10 /home/circleci/datadog/tests/Common/SnapshotTestTrait.php(171): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->DDTrace\\Tests\\Integrations\\Guzzle\\V7\\{closure}()\n#11 /home/circleci/datadog/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(86): DDTrace\\Tests\\Integrations\\Guzzle\\V6\\GuzzleIntegrationTest->isolateTracerSnapshot()\n#12 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1612): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->testMultiExec()\n#13 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1218): PHPUnit\\Framework\\TestCase->runTest()\n#14 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(728): PHPUnit\\Framework\\TestCase->runBare()\n#15 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(968): PHPUnit\\Framework\\TestResult->run()\n#16 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestCase->run()\n#17 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestSuite->run()\n#18 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#19 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(144): PHPUnit\\TextUI\\TestRunner->run()\n#20 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(97): PHPUnit\\TextUI\\Command->run()\n#21 phpvfscomposer:///home/circleci/datadog/tests/vendor/phpunit/phpunit/phpunit(106): PHPUnit\\TextUI\\Command::main()\n#22 /home/circleci/datadog/tests/vendor/bin/phpunit(118): include()\n#23 {main}", + "error.stack": "#0 /home/circleci/app/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(233): curl_multi_info_read()\n#1 /home/circleci/app/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(174): GuzzleHttp\\Handler\\CurlMultiHandler->processMessages()\n#2 /home/circleci/app/tests/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(189): GuzzleHttp\\Handler\\CurlMultiHandler->tick()\n#3 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(251): GuzzleHttp\\Handler\\CurlMultiHandler->execute()\n#4 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(227): GuzzleHttp\\Promise\\Promise->invokeWaitFn()\n#5 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(272): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#6 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(229): GuzzleHttp\\Promise\\Promise->invokeWaitList()\n#7 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Promise.php(69): GuzzleHttp\\Promise\\Promise->waitIfPending()\n#8 /home/circleci/app/tests/vendor/guzzlehttp/promises/src/Utils.php(121): GuzzleHttp\\Promise\\Promise->wait()\n#9 /home/circleci/app/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(77): GuzzleHttp\\Promise\\Utils::unwrap()\n#10 /home/circleci/app/tests/Common/SnapshotTestTrait.php(171): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->DDTrace\\Tests\\Integrations\\Guzzle\\V7\\{closure}()\n#11 /home/circleci/app/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php(86): DDTrace\\Tests\\Integrations\\Guzzle\\V6\\GuzzleIntegrationTest->isolateTracerSnapshot()\n#12 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1612): DDTrace\\Tests\\Integrations\\Guzzle\\V7\\GuzzleIntegrationTest->testMultiExec()\n#13 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1218): PHPUnit\\Framework\\TestCase->runTest()\n#14 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(728): PHPUnit\\Framework\\TestCase->runBare()\n#15 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(968): PHPUnit\\Framework\\TestResult->run()\n#16 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestCase->run()\n#17 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(684): PHPUnit\\Framework\\TestSuite->run()\n#18 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#19 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(144): PHPUnit\\TextUI\\TestRunner->run()\n#20 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(97): PHPUnit\\TextUI\\Command->run()\n#21 phpvfscomposer:///home/circleci/app/tests/vendor/phpunit/phpunit/phpunit(106): PHPUnit\\TextUI\\Command::main()\n#22 /home/circleci/app/tests/vendor/bin/phpunit(118): include()\n#23 {main}", "error.type": "curl error", "http.status_code": "0", "http.url": "https://google.still.wrong/", diff --git a/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest2xx.json b/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest2xx.json index 3bd72d2481..62d9258fd7 100644 --- a/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest2xx.json +++ b/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest2xx.json @@ -12,7 +12,7 @@ "component": "laminas", "http.method": "POST", "http.status_code": "201", - "http.url": "http://localhost:9999/datadog-rest-service", + "http.url": "http://localhost/datadog-rest-service", "http.version": "1.1", "laminas.route.action": "DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource@create", "laminas.route.name": "datadog-api.rest.datadog-rest-service", diff --git a/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest4xx.json b/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest4xx.json index 55e9528630..0b7f9591c2 100644 --- a/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest4xx.json +++ b/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest4xx.json @@ -12,7 +12,7 @@ "component": "laminas", "http.method": "GET", "http.status_code": "405", - "http.url": "http://localhost:9999/datadog-rest-service/1", + "http.url": "http://localhost/datadog-rest-service/1", "http.version": "1.1", "laminas.route.action": "DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource@fetch", "laminas.route.name": "datadog-api.rest.datadog-rest-service", @@ -383,7 +383,7 @@ "meta": { "component": "laminas", "error.message": "The GET method has not been defined for individual resources", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(548): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(548): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n", "error.type": "ApiProblem" } }, diff --git a/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest5xx.json b/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest5xx.json index 7b7e1dbf6a..f6a8684ce5 100644 --- a/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest5xx.json +++ b/tests/snapshots/tests.integrations.laminas.api_tools.v1_9.rest_test.test_scenario_rest5xx.json @@ -11,12 +11,12 @@ "meta": { "_dd.p.dm": "-0", "component": "laminas", - "error.message": "Uncaught Error (500): Attempt to assign property \"b\" on null in /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/module/DatadogApi/src/V1/Rest/DatadogRestService/DatadogRestServiceResource.php:55", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(544): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n#17 {main}", + "error.message": "Uncaught Error (500): Attempt to assign property \"b\" on null in /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/module/DatadogApi/src/V1/Rest/DatadogRestService/DatadogRestServiceResource.php:55", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(544): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n#17 {main}", "error.type": "Error", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/datadog-rest-service/42", + "http.url": "http://localhost/datadog-rest-service/42", "http.version": "1.1", "laminas.route.action": "DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource@fetch", "laminas.route.name": "datadog-api.rest.datadog-rest-service", @@ -374,8 +374,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Error (500): Attempt to assign property \"b\" on null in /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/module/DatadogApi/src/V1/Rest/DatadogRestService/DatadogRestServiceResource.php:55", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(544): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n#17 {main}", + "error.message": "Thrown Error (500): Attempt to assign property \"b\" on null in /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/module/DatadogApi/src/V1/Rest/DatadogRestService/DatadogRestServiceResource.php:55", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(544): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n#17 {main}", "error.type": "Error" } }, @@ -390,8 +390,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Error (500): Attempt to assign property \"b\" on null in /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/module/DatadogApi/src/V1/Rest/DatadogRestService/DatadogRestServiceResource.php:55", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(544): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/datadog/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n#17 {main}", + "error.message": "Thrown Error (500): Attempt to assign property \"b\" on null in /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/module/DatadogApi/src/V1/Rest/DatadogRestService/DatadogRestServiceResource.php:55", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/AbstractResourceListener.php(182): DatadogApi\\V1\\Rest\\DatadogRestService\\DatadogRestServiceResource->fetch()\n#1 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\AbstractResourceListener->dispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(544): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/Resource.php(499): Laminas\\ApiTools\\Rest\\Resource->triggerEvent()\n#5 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(493): Laminas\\ApiTools\\Rest\\Resource->fetch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(372): Laminas\\ApiTools\\Rest\\RestController->get()\n#7 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas-api-tools/api-tools-rest/src/RestController.php(335): Laminas\\Mvc\\Controller\\AbstractRestfulController->onDispatch()\n#8 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\ApiTools\\Rest\\RestController->onDispatch()\n#9 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#10 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#11 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Controller/AbstractRestfulController.php(306): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#12 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractRestfulController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#14 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-eventmanager/src/EventManager.php(177): Laminas\\EventManager\\EventManager->triggerListeners()\n#15 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#16 /home/circleci/app/tests/Frameworks/Laminas/ApiTools/Version_1_9/public/index.php(60): Laminas\\Mvc\\Application->run()\n#17 {main}", "error.type": "Error" } }, diff --git a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_return_string.json index f6018e1251..bf9b87bbc1 100644 --- a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_return_string.json @@ -13,7 +13,7 @@ "http.method": "GET", "laminas.route.name": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "http.version": "1.1", "laminas.route.action": "Application\\Controller\\CommonSpecsController@simple", "span.kind": "server" diff --git a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_to_missing_route.json index 1fe603ebae..9a3d7fbc43 100644 --- a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -12,7 +12,7 @@ "component": "laminas", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "http.version": "1.1", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_exception.json index dd89efef43..3daf1afba2 100644 --- a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_exception.json @@ -11,13 +11,13 @@ "meta": { "_dd.p.dm": "-0", "component": "laminas", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception", "http.method": "GET", "laminas.route.name": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "http.version": "1.1", "laminas.route.action": "Application\\Controller\\CommonSpecsController@error", "span.kind": "server" @@ -205,8 +205,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception" } }, @@ -221,8 +221,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception" } }, @@ -237,8 +237,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(71): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(97): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/DispatchListener.php(132): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(319): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-eventmanager/src/EventManager.php(179): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/vendor/laminas/laminas-mvc/src/Application.php(325): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_1_4/public/index.php(42): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_view.json index b42283934e..aaace5641a 100644 --- a/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laminas.v1_4.common_scenarios_test.test_scenario_get_with_view.json @@ -13,7 +13,7 @@ "http.method": "GET", "laminas.route.name": "simpleView", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "http.version": "1.1", "laminas.route.action": "Application\\Controller\\CommonSpecsController@view", "span.kind": "server" diff --git a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_return_string.json index 02fe659dc1..bc4864a27d 100644 --- a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_return_string.json @@ -13,7 +13,7 @@ "http.method": "GET", "laminas.route.name": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "http.version": "1.1", "laminas.route.action": "Application\\Controller\\CommonSpecsController@simple", "span.kind": "server" diff --git a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_to_missing_route.json index 81bd2112f1..38776b7911 100644 --- a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -12,7 +12,7 @@ "component": "laminas", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "http.version": "1.1", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_exception.json index 66ad41ea34..5b5d0ced68 100644 --- a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_exception.json @@ -11,13 +11,13 @@ "meta": { "_dd.p.dm": "-0", "component": "laminas", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch(Object(Laminas\\Mvc\\MvcEvent))\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners(Object(Laminas\\Mvc\\MvcEvent), Object(Closure))\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\\Mvc\\MvcEvent))\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch(Object(Laminas\\Http\\PhpEnvironment\\Request), Object(Laminas\\Http\\PhpEnvironment\\Response))\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch(Object(Laminas\\Mvc\\MvcEvent))\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners(Object(Laminas\\Mvc\\MvcEvent), Object(Closure))\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\\Mvc\\MvcEvent))\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch(Object(Laminas\\Mvc\\MvcEvent))\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners(Object(Laminas\\Mvc\\MvcEvent), Object(Closure))\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\\Mvc\\MvcEvent))\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch(Object(Laminas\\Http\\PhpEnvironment\\Request), Object(Laminas\\Http\\PhpEnvironment\\Response))\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch(Object(Laminas\\Mvc\\MvcEvent))\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners(Object(Laminas\\Mvc\\MvcEvent), Object(Closure))\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\\Mvc\\MvcEvent))\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception", "http.method": "GET", "laminas.route.name": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "http.version": "1.1", "laminas.route.action": "Application\\Controller\\CommonSpecsController@error", "span.kind": "server" @@ -205,8 +205,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception" } }, @@ -221,8 +221,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception" } }, @@ -237,8 +237,8 @@ "error": 1, "meta": { "component": "laminas", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/datadog/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/module/Application/src/Controller/CommonSpecsController.php:33", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(72): Application\\Controller\\CommonSpecsController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\Controller\\AbstractActionController->onDispatch()\n#2 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#3 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(105): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#4 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/DispatchListener.php(117): Laminas\\Mvc\\Controller\\AbstractController->dispatch()\n#5 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(320): Laminas\\Mvc\\DispatchListener->onDispatch()\n#6 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\\EventManager\\EventManager->triggerListeners()\n#7 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/vendor/laminas/laminas-mvc/src/Application.php(319): Laminas\\EventManager\\EventManager->triggerEventUntil()\n#8 /home/circleci/app/tests/Frameworks/Laminas/Version_2_0/public/index.php(37): Laminas\\Mvc\\Application->run()\n#9 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_view.json index 17dc87e777..447e982da3 100644 --- a/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laminas.v2_0.common_scenarios_test.test_scenario_get_with_view.json @@ -13,7 +13,7 @@ "http.method": "GET", "laminas.route.name": "simpleView", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "http.version": "1.1", "laminas.route.action": "Application\\Controller\\CommonSpecsController@view", "span.kind": "server" diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_dynamic_route.json index d4e3db996a..9a8a1a25ed 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/dynamic_route/dynamic01/static/dynamic02", + "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "runtime-id": "b32e6f79-c5ef-4d17-bae1-33f912b0a03d" }, "metrics": { @@ -239,7 +239,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 21, "parent_id": 18, diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json index f4f849cb49..4f0313c6f4 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", "laravel.route.name": "simple_route", "runtime-id": "d74a50e5-32ec-4668-ad83-9a267ef93418", diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_to_missing_route.json index c7f5bf628e..c8c608c023 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "d74a50e5-32ec-4668-ad83-9a267ef93418" }, "metrics": { @@ -239,7 +239,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 21, "parent_id": 18, diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json index 7f306ef712..c77278a12b 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "662b88ba00000000", "component": "laravel", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/app/Http/Controllers/CommonSpecsController.php:19", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(806): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(805): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(784): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(748): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(737): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(99): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/app/Http/Controllers/CommonSpecsController.php:19", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(806): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(805): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(784): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(748): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(737): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(99): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", "laravel.route.name": "unnamed_route", "runtime-id": "d74a50e5-32ec-4668-ad83-9a267ef93418", @@ -231,8 +231,8 @@ "error": 1, "meta": { "component": "laravel", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/app/Http/Controllers/CommonSpecsController.php:19", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(806): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(805): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(784): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(748): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(737): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(99): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/app/Http/Controllers/CommonSpecsController.php:19", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(806): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(805): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(784): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(748): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(737): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(99): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_10_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json index da1642411a..3521ab143b 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple_view", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", "laravel.route.name": "unnamed_route", "runtime-id": "d74a50e5-32ec-4668-ad83-9a267ef93418", @@ -279,7 +279,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_10_x/resources/views/simple_view.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_10_x/resources/views/simple_view.blade.php", "trace_id": 0, "span_id": 27, "parent_id": 20, diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_dynamic_route.json index d28120f47a..69b2d5ac11 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/dynamic_route/dynamic01/static/dynamic02", + "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "runtime-id": "3ddc27d7-3081-4e28-a35f-c8c6e7f4ec38" }, "metrics": { @@ -203,7 +203,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 19, "parent_id": 16, diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json index e519386218..8305fe3dd9 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", "laravel.route.name": "simple_route", "runtime-id": "4f76a87a-df89-41fb-909e-e269a4616334", diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_to_missing_route.json index 7f8f7b5052..3d1e616fbd 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "4f76a87a-df89-41fb-909e-e269a4616334" }, "metrics": { @@ -203,7 +203,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 19, "parent_id": 16, diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json index c12c4556c5..ffceb2d891 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "662b8a2800000000", "component": "laravel", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/app/Http/Controllers/CommonSpecsController.php:19", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(46): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(206): Illuminate\\Routing\\Route->runController()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(808): Illuminate\\Routing\\Route->run()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(88): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(75): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(807): Illuminate\\Pipeline\\Pipeline->then()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(786): Illuminate\\Routing\\Router->runRouteWithinStack()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(750): Illuminate\\Routing\\Router->runRoute()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(739): Illuminate\\Routing\\Router->dispatchToRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(201): Illuminate\\Routing\\Router->dispatch()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\ValidatePostSize->handle()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(110): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Pipeline\\Pipeline->then()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(145): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(1188): Illuminate\\Foundation\\Http\\Kernel->handle()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/public/index.php(17): Illuminate\\Foundation\\Application->handleRequest()\n#44 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/app/Http/Controllers/CommonSpecsController.php:19", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(46): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(206): Illuminate\\Routing\\Route->runController()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(808): Illuminate\\Routing\\Route->run()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(88): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(75): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(807): Illuminate\\Pipeline\\Pipeline->then()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(786): Illuminate\\Routing\\Router->runRouteWithinStack()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(750): Illuminate\\Routing\\Router->runRoute()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(739): Illuminate\\Routing\\Router->dispatchToRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(201): Illuminate\\Routing\\Router->dispatch()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\ValidatePostSize->handle()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(110): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Pipeline\\Pipeline->then()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(145): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(1188): Illuminate\\Foundation\\Http\\Kernel->handle()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/public/index.php(17): Illuminate\\Foundation\\Application->handleRequest()\n#44 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", "laravel.route.name": "unnamed_route", "runtime-id": "4f76a87a-df89-41fb-909e-e269a4616334", @@ -219,8 +219,8 @@ "error": 1, "meta": { "component": "laravel", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/app/Http/Controllers/CommonSpecsController.php:19", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(46): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(206): Illuminate\\Routing\\Route->runController()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(808): Illuminate\\Routing\\Route->run()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(88): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(75): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(807): Illuminate\\Pipeline\\Pipeline->then()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(786): Illuminate\\Routing\\Router->runRouteWithinStack()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(750): Illuminate\\Routing\\Router->runRoute()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(739): Illuminate\\Routing\\Router->dispatchToRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(201): Illuminate\\Routing\\Router->dispatch()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\ValidatePostSize->handle()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(110): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Pipeline\\Pipeline->then()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(145): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(1188): Illuminate\\Foundation\\Http\\Kernel->handle()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/public/index.php(17): Illuminate\\Foundation\\Application->handleRequest()\n#44 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/app/Http/Controllers/CommonSpecsController.php:19", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(46): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(206): Illuminate\\Routing\\Route->runController()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(808): Illuminate\\Routing\\Route->run()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(88): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(75): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(807): Illuminate\\Pipeline\\Pipeline->then()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(786): Illuminate\\Routing\\Router->runRouteWithinStack()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(750): Illuminate\\Routing\\Router->runRoute()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(739): Illuminate\\Routing\\Router->dispatchToRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(201): Illuminate\\Routing\\Router->dispatch()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\ValidatePostSize->handle()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(110): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Pipeline\\Pipeline->then()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(145): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(1188): Illuminate\\Foundation\\Http\\Kernel->handle()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_11_x/public/index.php(17): Illuminate\\Foundation\\Application->handleRequest()\n#44 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json index 071b0f5b27..6403732db9 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple_view", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", "laravel.route.name": "unnamed_route", "runtime-id": "4f76a87a-df89-41fb-909e-e269a4616334", @@ -243,7 +243,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_11_x/resources/views/simple_view.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_11_x/resources/views/simple_view.blade.php", "trace_id": 0, "span_id": 25, "parent_id": 19, diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json index fdf0d5f5c6..6f8bd98d19 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "dynamic_route/{param01}/static/{param02?}", "http.status_code": "200", - "http.url": "http://localhost:9999/dynamic_route/dynamic01/static/dynamic02", + "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@dynamicRoute", "laravel.route.name": "unnamed_route", "runtime-id": "4878b25b-9211-4ac8-8906-d7721b2fa3e5", diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json index 83b601cdf9..70b03dbba7 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", "laravel.route.name": "simple_route", "runtime-id": "4878b25b-9211-4ac8-8906-d7721b2fa3e5", diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_to_missing_route.json index 10e73cc490..d12233d5d3 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "4878b25b-9211-4ac8-8906-d7721b2fa3e5" }, "metrics": { @@ -218,7 +218,7 @@ { "name": "laravel.view", "service": "laravel_test_app", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 19, "parent_id": 16, diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json index 94138284a4..d9713635a8 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "664c86b600000000", "component": "laravel", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/app/Http/Controllers/CommonSpecsController.php:22", - "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(684): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(659): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(625): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(614): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/app/Http/Controllers/CommonSpecsController.php:22", + "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(684): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(659): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(625): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(614): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", "laravel.route.name": "unnamed_route", "runtime-id": "4878b25b-9211-4ac8-8906-d7721b2fa3e5", @@ -210,8 +210,8 @@ "error": 1, "meta": { "component": "laravel", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/app/Http/Controllers/CommonSpecsController.php:22", - "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(684): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(659): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(625): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(614): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/app/Http/Controllers/CommonSpecsController.php:22", + "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(684): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(659): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(625): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Router.php(614): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_5_7/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json index 5fdd4f20b7..0f745e4712 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple_view", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", "laravel.route.name": "unnamed_route", "runtime-id": "4878b25b-9211-4ac8-8906-d7721b2fa3e5", @@ -246,7 +246,7 @@ { "name": "laravel.view", "service": "laravel_test_app", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_5_7/resources/views/simple_view.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_5_7/resources/views/simple_view.blade.php", "trace_id": 0, "span_id": 21, "parent_id": 17, diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json index ee11b0f6dc..a61c2451a4 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "dynamic_route/{param01}/static/{param02?}", "http.status_code": "200", - "http.url": "http://localhost:9999/dynamic_route/dynamic01/static/dynamic02", + "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@dynamicRoute", "laravel.route.name": "unnamed_route", "runtime-id": "5db5654d-5a9e-4cf0-908b-a51f7ced2ff2", diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json index f7e465454e..f54e4fac1d 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", "laravel.route.name": "simple_route", "runtime-id": "5db5654d-5a9e-4cf0-908b-a51f7ced2ff2", diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_to_missing_route.json index 83f1ce2ea1..3b220f829e 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "5db5654d-5a9e-4cf0-908b-a51f7ced2ff2" }, "metrics": { @@ -218,7 +218,7 @@ { "name": "laravel.view", "service": "laravel_test_app", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 19, "parent_id": 16, diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json index c33afc309b..a02ff15ede 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "664c88ca00000000", "component": "laravel", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/app/Http/Controllers/CommonSpecsController.php:22", - "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(680): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(657): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(623): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(612): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/app/Http/Controllers/CommonSpecsController.php:22", + "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(680): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(657): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(623): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(612): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", "laravel.route.name": "unnamed_route", "runtime-id": "5db5654d-5a9e-4cf0-908b-a51f7ced2ff2", @@ -210,8 +210,8 @@ "error": 1, "meta": { "component": "laravel", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/app/Http/Controllers/CommonSpecsController.php:22", - "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(680): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(657): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(623): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(612): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/app/Http/Controllers/CommonSpecsController.php:22", + "error.stack": "#0 [internal function]: App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(680): Illuminate\\Routing\\Route->run()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Pipeline\\Pipeline->then()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(657): Illuminate\\Routing\\Router->runRouteWithinStack()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(623): Illuminate\\Routing\\Router->runRoute()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Router.php(612): Illuminate\\Routing\\Router->dispatchToRoute()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_5_8/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()\n#18 {main}", "error.type": "Exception" } }, @@ -266,7 +266,7 @@ { "name": "laravel.view", "service": "laravel_test_app", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/500.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_5_8/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/500.blade.php", "trace_id": 0, "span_id": 22, "parent_id": 19, diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json index 066d91bbc5..2347d4af8d 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple_view", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", "laravel.route.name": "unnamed_route", "runtime-id": "5db5654d-5a9e-4cf0-908b-a51f7ced2ff2", @@ -246,7 +246,7 @@ { "name": "laravel.view", "service": "laravel_test_app", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_5_8/resources/views/simple_view.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_5_8/resources/views/simple_view.blade.php", "trace_id": 0, "span_id": 21, "parent_id": 17, diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json index e2337941b9..888f07cfc1 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "dynamic_route/{param01}/static/{param02?}", "http.status_code": "200", - "http.url": "http://localhost:9999/dynamic_route/dynamic01/static/dynamic02", + "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@dynamicRoute", "laravel.route.name": "unnamed_route", "runtime-id": "3bd45f16-c853-4884-a7ca-1017a443b1fe", diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json index 5b7dceca26..5bd41327ec 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", "laravel.route.name": "simple_route", "runtime-id": "3bd45f16-c853-4884-a7ca-1017a443b1fe", diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_to_missing_route.json index 2e4560c2f2..a83c10872f 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "3bd45f16-c853-4884-a7ca-1017a443b1fe" }, "metrics": { @@ -215,7 +215,7 @@ { "name": "laravel.view", "service": "laravel_test_app", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 19, "parent_id": 16, diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json index d6e7846b63..1c2888ba55 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "664c898e00000000", "component": "laravel", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/app/Http/Controllers/CommonSpecsController.php:21", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(262): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(721): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(723): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(698): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(662): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(651): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(167): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/fruitcake/laravel-cors/src/HandleCors.php(38): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(142): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(111): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/app/Http/Controllers/CommonSpecsController.php:21", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(262): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(721): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(723): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(698): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(662): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(651): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(167): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/fruitcake/laravel-cors/src/HandleCors.php(38): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(142): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(111): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", "laravel.route.name": "unnamed_route", "runtime-id": "3bd45f16-c853-4884-a7ca-1017a443b1fe", @@ -207,8 +207,8 @@ "error": 1, "meta": { "component": "laravel", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/app/Http/Controllers/CommonSpecsController.php:21", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(262): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(721): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(723): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(698): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(662): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(651): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(167): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/fruitcake/laravel-cors/src/HandleCors.php(38): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(142): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(111): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/app/Http/Controllers/CommonSpecsController.php:21", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(262): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(721): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(723): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(698): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(662): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(651): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(167): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/fruitcake/laravel-cors/src/HandleCors.php(38): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(142): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(111): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_8_x/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json index 7f6142e012..132a2cbfe8 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple_view", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", "laravel.route.name": "unnamed_route", "runtime-id": "3bd45f16-c853-4884-a7ca-1017a443b1fe", @@ -243,7 +243,7 @@ { "name": "laravel.view", "service": "laravel_test_app", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_8_x/resources/views/simple_view.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_8_x/resources/views/simple_view.blade.php", "trace_id": 0, "span_id": 21, "parent_id": 17, diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json b/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json index fd3f39e1b8..c18eeccd87 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "queue/broadcast", "http.status_code": "200", - "http.url": "http://localhost:9999/queue/broadcast", + "http.url": "http://localhost/queue/broadcast", "laravel.route.action": "App\\Http\\Controllers\\QueueTestController@broadcast", "laravel.route.name": "broadcast", "runtime-id": "2b8747b7-5a3e-44cb-8545-9c808e7b4e93", diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_dynamic_route.json index 7cb9c0b4ab..78b1ba2cac 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/dynamic_route/dynamic01/static/dynamic02", + "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "runtime-id": "5c9f7cdc-4833-4e56-a8af-852b6607f988" }, "metrics": { @@ -239,7 +239,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 21, "parent_id": 18, diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json index dadf0a490b..01e28d6b43 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", "laravel.route.name": "simple_route", "runtime-id": "614cba3e-708f-4797-8da5-40fa71e9c272", diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_to_missing_route.json index a5e8b4e4eb..78d03746d8 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "laravel", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "614cba3e-708f-4797-8da5-40fa71e9c272" }, "metrics": { @@ -239,7 +239,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php", "trace_id": 0, "span_id": 21, "parent_id": 18, diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json index 50bb3ff268..ece8e66718 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "662b874300000000", "component": "laravel", - "error.message": "Uncaught Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/app/Http/Controllers/CommonSpecsController.php:19", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(798): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(797): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(776): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(740): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(729): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(190): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(165): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(134): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", + "error.message": "Uncaught Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/app/Http/Controllers/CommonSpecsController.php:19", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(798): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(797): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(776): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(740): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(729): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(190): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(165): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(134): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "error", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", "laravel.route.name": "unnamed_route", "runtime-id": "614cba3e-708f-4797-8da5-40fa71e9c272", @@ -231,8 +231,8 @@ "error": 1, "meta": { "component": "laravel", - "error.message": "Thrown Exception (500): Controller error in /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/app/Http/Controllers/CommonSpecsController.php:19", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(798): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(797): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(776): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(740): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(729): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(190): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(165): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(134): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", + "error.message": "Thrown Exception (500): Controller error in /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/app/Http/Controllers/CommonSpecsController.php:19", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CommonSpecsController->error()\n#1 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()\n#2 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()\n#4 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(798): Illuminate\\Routing\\Route->run()\n#5 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()\n#6 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#7 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()\n#8 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#9 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()\n#10 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()\n#12 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()\n#14 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()\n#15 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#16 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()\n#17 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#18 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()\n#19 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#20 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(797): Illuminate\\Pipeline\\Pipeline->then()\n#21 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(776): Illuminate\\Routing\\Router->runRouteWithinStack()\n#22 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(740): Illuminate\\Routing\\Router->runRoute()\n#23 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Routing/Router.php(729): Illuminate\\Routing\\Router->dispatchToRoute()\n#24 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(190): Illuminate\\Routing\\Router->dispatch()\n#25 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()\n#26 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#27 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#28 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()\n#29 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#30 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()\n#31 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()\n#32 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#33 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()\n#34 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#35 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()\n#36 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#37 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle()\n#38 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#39 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()\n#40 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()\n#41 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(165): Illuminate\\Pipeline\\Pipeline->then()\n#42 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(134): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()\n#43 /home/circleci/app/tests/Frameworks/Laravel/Version_9_x/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()\n#44 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json index 3e5dd32003..8a07e2caaa 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "simple_view", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", "laravel.route.name": "unnamed_route", "runtime-id": "614cba3e-708f-4797-8da5-40fa71e9c272", @@ -267,7 +267,7 @@ { "name": "laravel.view", "service": "my_service", - "resource": "/home/circleci/datadog/tests/Frameworks/Laravel/Version_9_x/resources/views/simple_view.blade.php", + "resource": "/home/circleci/app/tests/Frameworks/Laravel/Version_9_x/resources/views/simple_view.blade.php", "trace_id": 0, "span_id": 23, "parent_id": 19, diff --git a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_return_string.json index d1935c5815..2997d6ac17 100644 --- a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_return_string.json @@ -12,7 +12,7 @@ "component": "magento", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/datadog/simple/index?key=value&", + "http.url": "http://localhost/datadog/simple/index?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "datadog", diff --git a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json index 1db2098449..19ee5e01bd 100644 --- a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -12,7 +12,7 @@ "component": "magento", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "does_not_exist", diff --git a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_exception.json index 7c86977209..6fab5fd34d 100644 --- a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_exception.json @@ -11,12 +11,12 @@ "meta": { "_dd.p.dm": "-0", "component": "magento", - "error.message": "Caught Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", + "error.message": "Caught Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/datadog/error/index?key=value&", + "http.url": "http://localhost/datadog/error/index?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "datadog", @@ -64,8 +64,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", "error.type": "Exception" } }, @@ -94,8 +94,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", "error.type": "Exception" } }, @@ -141,8 +141,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", "error.type": "Exception" } }, @@ -157,8 +157,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", "error.type": "Exception", "magento.action": "index", "magento.controller": "error", @@ -282,8 +282,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(24): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Action/Action.php(108): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(39): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(186): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/FrontController.php(118): Magento\\Framework\\App\\FrontController->processRequest()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(73): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/lib/internal/Magento/Framework/App/Bootstrap.php(261): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_3/pub/index.php(40): Magento\\Framework\\App\\Bootstrap->run()\n#20 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_view.json index 957464d6ec..61fc7ee450 100644 --- a/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.magento.v2_3.common_scenarios_test.test_scenario_get_with_view.json @@ -12,7 +12,7 @@ "component": "magento", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/datadog/simpleview/index?key=value&", + "http.url": "http://localhost/datadog/simpleview/index?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "datadog", diff --git a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_return_string.json index 50812fac22..72c71d68a0 100644 --- a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_return_string.json @@ -12,7 +12,7 @@ "component": "magento", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/datadog/simple/index?key=value&", + "http.url": "http://localhost/datadog/simple/index?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "datadog", diff --git a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_to_missing_route.json index 4d692939d4..b953f93fb2 100644 --- a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -12,7 +12,7 @@ "component": "magento", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "does_not_exist", diff --git a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_exception.json index da7242f8d5..ae3c2463f1 100644 --- a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_exception.json @@ -11,12 +11,12 @@ "meta": { "_dd.p.dm": "-0", "component": "magento", - "error.message": "Caught Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", + "error.message": "Caught Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/datadog/error/index?key=value&", + "http.url": "http://localhost/datadog/error/index?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "datadog", @@ -64,8 +64,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", "error.type": "Exception" } }, @@ -94,8 +94,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", "error.type": "Exception" } }, @@ -141,8 +141,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", "error.type": "Exception" } }, @@ -261,8 +261,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", "error.type": "Exception", "magento.action": "index", "magento.controller": "error", @@ -282,8 +282,8 @@ "error": 1, "meta": { "component": "magento", - "error.message": "Thrown Exception (500): This is an exception in /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/datadog/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", + "error.message": "Thrown Exception (500): This is an exception in /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/CustomElement/Datadog/Controller/error/Index.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): CustomElement\\Datadog\\Controller\\Error\\Index->execute()\n#1 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#2 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#3 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(23): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#4 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Action/Action.php(111): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->execute()\n#5 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\Action\\Action->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callParent()\n#7 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#8 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/CustomElement/Datadog/Controller/error/Index/Interceptor.php(32): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->___callPlugins()\n#9 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(245): CustomElement\\Datadog\\Controller\\error\\Index\\Interceptor->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(212): Magento\\Framework\\App\\FrontController->getActionResponse()\n#11 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/FrontController.php(147): Magento\\Framework\\App\\FrontController->processRequest()\n#12 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Magento\\Framework\\App\\FrontController->dispatch()\n#13 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\\Framework\\App\\FrontController\\Interceptor->___callParent()\n#14 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php(99): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#15 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\Store\\App\\FrontController\\Plugin\\RequestPreprocessor->aroundDispatch()\n#16 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php(75): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#17 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(135): Magento\\PageCache\\Model\\App\\FrontController\\BuiltinPlugin->aroundDispatch()\n#18 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\\Framework\\App\\FrontController\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#19 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/FrontController/Interceptor.php(23): Magento\\Framework\\App\\FrontController\\Interceptor->___callPlugins()\n#20 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Http.php(116): Magento\\Framework\\App\\FrontController\\Interceptor->dispatch()\n#21 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\\Framework\\App\\Http->launch()\n#22 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/lib/internal/Magento/Framework/App/Bootstrap.php(264): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#23 /home/circleci/app/tests/Frameworks/Magento/Version_2_4/pub/index.php(30): Magento\\Framework\\App\\Bootstrap->run()\n#24 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_view.json index 5d239d7b68..eb115310c0 100644 --- a/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.magento.v2_4.common_scenarios_test.test_scenario_get_with_view.json @@ -12,7 +12,7 @@ "component": "magento", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/datadog/simpleview/index?key=value&", + "http.url": "http://localhost/datadog/simpleview/index?key=value&", "magento.area": "frontend", "magento.cached": "false", "magento.frontname": "datadog", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json index 9a64fff5b0..948c7b2782 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json @@ -12,8 +12,8 @@ "_dd.p.dm": "0", "_dd.p.tid": "6659d8a000000000", "env": "test", - "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: The server had an error while processing your request. Sorry about that! in /home/circleci/datadog/tests/vendor/openai-php/client/src/Responses/StreamResponse.php:60", - "error.stack": "#0 [internal function]: OpenAI\\Responses\\StreamResponse->getIterator()\n#1 /home/circleci/datadog/src/DDTrace/Integrations/OpenAI/OpenAIIntegration.php(1079): Generator->valid()\n#2 /home/circleci/datadog/src/DDTrace/Integrations/OpenAI/OpenAIIntegration.php(1033): DDTrace\\Integrations\\OpenAI\\OpenAIIntegration::readAndStoreStreamedResponse()\n#3 /home/circleci/datadog/src/DDTrace/Integrations/OpenAI/OpenAIIntegration.php(204): DDTrace\\Integrations\\OpenAI\\OpenAIIntegration::handleStreamedResponse()\n#4 /home/circleci/datadog/tests/vendor/openai-php/client/src/Resources/Chat.php(54): OpenAI\\Resources\\Chat->DDTrace\\Integrations\\OpenAI\\{closure}()\n#5 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(100): OpenAI\\Resources\\Chat->createStreamed()\n#6 /home/circleci/datadog/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#7 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(93): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#8 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(455): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->callStreamed()\n#9 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testCreateChatCompletionStreamWithError()\n#10 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#11 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#12 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#13 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#14 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#15 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#16 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#17 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#18 /home/circleci/datadog/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#19 /home/circleci/datadog/tests/vendor/bin/phpunit(122): include()\n#20 {main}", + "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: The server had an error while processing your request. Sorry about that! in /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Responses/StreamResponse.php:60", + "error.stack": "#0 [internal function]: OpenAI\\Responses\\StreamResponse->getIterator()\n#1 /home/circleci/app/src/DDTrace/Integrations/OpenAI/OpenAIIntegration.php(1079): Generator->valid()\n#2 /home/circleci/app/src/DDTrace/Integrations/OpenAI/OpenAIIntegration.php(1033): DDTrace\\Integrations\\OpenAI\\OpenAIIntegration::readAndStoreStreamedResponse()\n#3 /home/circleci/app/src/DDTrace/Integrations/OpenAI/OpenAIIntegration.php(204): DDTrace\\Integrations\\OpenAI\\OpenAIIntegration::handleStreamedResponse()\n#4 /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Resources/Chat.php(54): OpenAI\\Resources\\Chat->DDTrace\\Integrations\\OpenAI\\{closure}()\n#5 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(100): OpenAI\\Resources\\Chat->createStreamed()\n#6 /home/circleci/app/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#7 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(93): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#8 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(455): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->callStreamed()\n#9 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testCreateChatCompletionStreamWithError()\n#10 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#11 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#12 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#13 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#14 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#15 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#16 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#17 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#18 /home/circleci/app/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#19 /home/circleci/app/tests/vendor/bin/phpunit(122): include()\n#20 {main}", "error.type": "OpenAI\\Exceptions\\ErrorException", "openai.api_base": "https://api.openai.com/v1/", "openai.request.endpoint": "/v1/chat/completions", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json index de1e1d04c0..e5f86487cd 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json @@ -12,8 +12,8 @@ "_dd.p.dm": "0", "_dd.p.tid": "6659d74a00000000", "env": "test", - "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: Invalid schema for function 'get_current_weather':\nIn context=('properties', 'location'), array schema missing items in /home/circleci/datadog/tests/vendor/openai-php/client/src/Transporters/HttpTransporter.php:131", - "error.stack": "#0 /home/circleci/datadog/tests/vendor/openai-php/client/src/Transporters/HttpTransporter.php(57): OpenAI\\Transporters\\HttpTransporter->throwIfJsonError()\n#1 /home/circleci/datadog/tests/vendor/openai-php/client/src/Resources/Completions.php(33): OpenAI\\Transporters\\HttpTransporter->requestObject()\n#2 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(81): OpenAI\\Resources\\Completions->create()\n#3 /home/circleci/datadog/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#4 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(76): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#5 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(468): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->call()\n#6 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testCreateCompletionsWithMultipleErrorMessages()\n#7 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#8 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#9 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#10 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#11 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#12 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#13 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#14 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#15 /home/circleci/datadog/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#16 /home/circleci/datadog/tests/vendor/bin/phpunit(122): include()\n#17 {main}", + "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: Invalid schema for function 'get_current_weather':\nIn context=('properties', 'location'), array schema missing items in /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Transporters/HttpTransporter.php:131", + "error.stack": "#0 /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Transporters/HttpTransporter.php(57): OpenAI\\Transporters\\HttpTransporter->throwIfJsonError()\n#1 /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Resources/Completions.php(33): OpenAI\\Transporters\\HttpTransporter->requestObject()\n#2 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(81): OpenAI\\Resources\\Completions->create()\n#3 /home/circleci/app/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#4 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(76): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#5 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(468): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->call()\n#6 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testCreateCompletionsWithMultipleErrorMessages()\n#7 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#8 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#9 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#10 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#11 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#12 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#13 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#14 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#15 /home/circleci/app/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#16 /home/circleci/app/tests/vendor/bin/phpunit(122): include()\n#17 {main}", "error.type": "OpenAI\\Exceptions\\ErrorException", "openai.api_base": "https://api.openai.com/v1/", "openai.request.endpoint": "/v1/completions", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json index 628958802f..c76b32e0a0 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json @@ -12,8 +12,8 @@ "_dd.p.dm": "0", "_dd.p.tid": "6659d8a500000000", "env": "test", - "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: Incorrect API key provided: foo. You can find your API key at https://platform.openai.com. in /home/circleci/datadog/tests/vendor/openai-php/client/src/Transporters/HttpTransporter.php:131", - "error.stack": "#0 /home/circleci/datadog/tests/vendor/openai-php/client/src/Transporters/HttpTransporter.php(57): OpenAI\\Transporters\\HttpTransporter->throwIfJsonError()\n#1 /home/circleci/datadog/tests/vendor/openai-php/client/src/Resources/Models.php(28): OpenAI\\Transporters\\HttpTransporter->requestObject()\n#2 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(83): OpenAI\\Resources\\Models->list()\n#3 /home/circleci/datadog/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#4 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(76): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#5 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(463): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->call()\n#6 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testListModelsWithError()\n#7 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#8 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#9 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#10 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#11 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#12 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#13 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#14 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#15 /home/circleci/datadog/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#16 /home/circleci/datadog/tests/vendor/bin/phpunit(122): include()\n#17 {main}", + "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: Incorrect API key provided: foo. You can find your API key at https://platform.openai.com. in /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Transporters/HttpTransporter.php:131", + "error.stack": "#0 /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Transporters/HttpTransporter.php(57): OpenAI\\Transporters\\HttpTransporter->throwIfJsonError()\n#1 /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Resources/Models.php(28): OpenAI\\Transporters\\HttpTransporter->requestObject()\n#2 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(83): OpenAI\\Resources\\Models->list()\n#3 /home/circleci/app/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#4 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(76): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#5 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(463): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->call()\n#6 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testListModelsWithError()\n#7 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#8 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#9 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#10 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#11 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#12 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#13 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#14 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#15 /home/circleci/app/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#16 /home/circleci/app/tests/vendor/bin/phpunit(122): include()\n#17 {main}", "error.type": "OpenAI\\Exceptions\\ErrorException", "openai.api_base": "https://api.openai.com/v1/", "openai.request.endpoint": "/v1/models", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json index 79f0b7633b..891c75a521 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json @@ -12,8 +12,8 @@ "_dd.p.dm": "0", "_dd.p.tid": "6659d8b000000000", "env": "test", - "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: You exceeded your current quota, please check in /home/circleci/datadog/tests/vendor/openai-php/client/src/Transporters/HttpTransporter.php:131", - "error.stack": "#0 /home/circleci/datadog/tests/vendor/openai-php/client/src/Transporters/HttpTransporter.php(57): OpenAI\\Transporters\\HttpTransporter->throwIfJsonError()\n#1 /home/circleci/datadog/tests/vendor/openai-php/client/src/Resources/Models.php(28): OpenAI\\Transporters\\HttpTransporter->requestObject()\n#2 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(83): OpenAI\\Resources\\Models->list()\n#3 /home/circleci/datadog/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#4 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(76): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#5 /home/circleci/datadog/tests/Integrations/OpenAI/OpenAITest.php(473): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->call()\n#6 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testListModelsWithNullErrorType()\n#7 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#8 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#9 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#10 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#11 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#12 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#13 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#14 /home/circleci/datadog/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#15 /home/circleci/datadog/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#16 /home/circleci/datadog/tests/vendor/bin/phpunit(122): include()\n#17 {main}", + "error.message": "Uncaught OpenAI\\Exceptions\\ErrorException: You exceeded your current quota, please check in /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Transporters/HttpTransporter.php:131", + "error.stack": "#0 /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Transporters/HttpTransporter.php(57): OpenAI\\Transporters\\HttpTransporter->throwIfJsonError()\n#1 /home/circleci/app/tests/Integrations/OpenAI/vendor/openai-php/client/src/Resources/Models.php(28): OpenAI\\Transporters\\HttpTransporter->requestObject()\n#2 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(83): OpenAI\\Resources\\Models->list()\n#3 /home/circleci/app/tests/Common/SnapshotTestTrait.php(347): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->DDTrace\\Tests\\Integrations\\OpenAI\\{closure}()\n#4 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(76): DDTrace\\Tests\\Common\\IntegrationTestCase->isolateTracerSnapshot()\n#5 /home/circleci/app/tests/Integrations/OpenAI/OpenAITest.php(473): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->call()\n#6 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1617): DDTrace\\Tests\\Integrations\\OpenAI\\OpenAITest->testListModelsWithNullErrorType()\n#7 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(1223): PHPUnit\\Framework\\TestCase->runTest()\n#8 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestResult.php(729): PHPUnit\\Framework\\TestCase->runBare()\n#9 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestCase.php(973): PHPUnit\\Framework\\TestResult->run()\n#10 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestCase->run()\n#11 /home/circleci/app/tests/vendor/phpunit/phpunit/src/Framework/TestSuite.php(685): PHPUnit\\Framework\\TestSuite->run()\n#12 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(651): PHPUnit\\Framework\\TestSuite->run()\n#13 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(146): PHPUnit\\TextUI\\TestRunner->run()\n#14 /home/circleci/app/tests/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit\\TextUI\\Command->run()\n#15 /home/circleci/app/tests/vendor/phpunit/phpunit/phpunit(107): PHPUnit\\TextUI\\Command::main()\n#16 /home/circleci/app/tests/vendor/bin/phpunit(122): include()\n#17 {main}", "error.type": "OpenAI\\Exceptions\\ErrorException", "openai.api_base": "https://api.openai.com/v1/", "openai.request.endpoint": "/v1/models", diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string.json index bac36e6323..82348173bb 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/app.php/simple?key=value&", + "http.url": "http://localhost/app.php/simple?key=value&", "runtime-id": "93535b2f-0ff1-4add-8907-53304703f545", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string_apache.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string_apache.json index 7ccbb60f63..7d67fefaba 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_return_string_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "ac93bd2f-42bb-4c03-888a-da12a9f25915", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json index 982048534c..c61b412da0 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/app.php/does_not_exist?key=value&", + "http.url": "http://localhost/app.php/does_not_exist?key=value&", "runtime-id": "93535b2f-0ff1-4add-8907-53304703f545", "span.kind": "server", "symfony.route.action": "Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController@showAction" @@ -70,8 +70,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/appProdUrlMatcher.php:55", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1227): appProdUrlMatcher->match()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1886): Symfony\\Component\\Routing\\Router->match()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php:1896\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#8 {main}", + "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/appProdUrlMatcher.php:55", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1227): appProdUrlMatcher->match()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1886): Symfony\\Component\\Routing\\Router->match()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php:1896\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#8 {main}", "error.type": "Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route_apache.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route_apache.json index eb1fed3cd7..e1e5acac6c 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_to_missing_route_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "1e42fc1f-1d82-4b6d-924f-b260e67bd97d", "span.kind": "server", "symfony.route.action": "Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController@showAction" @@ -70,8 +70,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/appProdUrlMatcher.php:55", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1227): appProdUrlMatcher->match()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1886): Symfony\\Component\\Routing\\Router->match()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php:1896\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#8 {main}", + "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/appProdUrlMatcher.php:55", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1227): appProdUrlMatcher->match()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1886): Symfony\\Component\\Routing\\Router->match()\n#2 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php:1896\nStack trace:\n#0 [internal function]: Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1691): call_user_func()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/cache/prod/classes.php(1617): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2967): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#8 {main}", "error.type": "Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception.json index fd5e74862d..e76f67bda8 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,12 +12,12 @@ "_dd.p.dm": "-0", "_dd.p.tid": "6661b35000000000", "component": "symfony", - "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", + "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/app.php/error?key=value&", + "http.url": "http://localhost/app.php/error?key=value&", "runtime-id": "93535b2f-0ff1-4add-8907-53304703f545", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@errorAction", @@ -99,8 +99,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", + "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception_apache.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception_apache.json index 7d1b3dfd46..1ce2e63f95 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_exception_apache.json @@ -12,12 +12,12 @@ "_dd.p.dm": "-0", "_dd.p.tid": "6661bb9f00000000", "component": "symfony", - "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", + "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "264f8207-13c8-42ee-bd15-24d728192870", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@errorAction", @@ -99,8 +99,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", + "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 [internal function]: AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2978): call_user_func_array()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2952): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(3081): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/app/bootstrap.php.cache(2351): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_3/web/app.php(14): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#6 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view.json index e51b4db73b..02531a7ac6 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/app.php/simple_view?key=value&", + "http.url": "http://localhost/app.php/simple_view?key=value&", "runtime-id": "05d2d546-7cb6-4e46-bc10-0ff5602cdf9d", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleViewAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view_apache.json b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view_apache.json index 26618af5f2..955ae18522 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.common_scenarios_test.test_scenario_get_with_view_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "daab05ca-d6fc-450e-b1bf-e4a6478521aa", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleViewAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping.json b/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping.json index eaa17a7c23..420610342c 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/app.php?key=value&", + "http.url": "http://localhost/app.php?key=value&", "runtime-id": "7ddbbd63-c083-4257-a7f5-44101e551ef1", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\DefaultController@testingRouteNameAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping_apache.json b/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping_apache.json index 131ad7010f..684028567f 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_3.route_name_test.test_resource_to_uri_mapping_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/?key=value&", + "http.url": "http://localhost/?key=value&", "runtime-id": "d29fcd90-cbec-4796-bdc8-5e3fb022b605", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\DefaultController@testingRouteNameAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string.json index 25abc6024d..a0a9c47464 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/app.php/simple?key=value&", + "http.url": "http://localhost/app.php/simple?key=value&", "runtime-id": "4374c14f-def1-4b62-97c0-598f46322850", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string_apache.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string_apache.json index 30c67847c1..a024fdc05c 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_return_string_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "ff0fb76b-fe5f-4bb4-8306-ba78e1172e49", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route.json index fe07dbc60b..f6fea7bb66 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/app.php/does_not_exist?key=value&", + "http.url": "http://localhost/app.php/does_not_exist?key=value&", "runtime-id": "4374c14f-def1-4b62-97c0-598f46322850", "span.kind": "server", "symfony.route.action": "Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController@showAction" @@ -70,8 +70,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/appProdProjectContainerUrlMatcher.php:57", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1455): appProdProjectContainerUrlMatcher->match()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1318): Symfony\\Component\\Routing\\Matcher\\UrlMatcher->matchRequest()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(2119): Symfony\\Component\\Routing\\Router->matchRequest()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3264): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php:2135\nStack trace:\n#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3264): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#7 {main}", + "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/appProdProjectContainerUrlMatcher.php:57", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1455): appProdProjectContainerUrlMatcher->match()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1318): Symfony\\Component\\Routing\\Matcher\\UrlMatcher->matchRequest()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(2119): Symfony\\Component\\Routing\\Router->matchRequest()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3264): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php:2135\nStack trace:\n#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3264): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#7 {main}", "error.type": "Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route_apache.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route_apache.json index a03f1921a1..d757b10341 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_to_missing_route_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "0371654f-098d-496a-ad8e-f1ea99fc59c6", "span.kind": "server", "symfony.route.action": "Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController@showAction" @@ -70,8 +70,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/appProdProjectContainerUrlMatcher.php:57", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1455): appProdProjectContainerUrlMatcher->match()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1318): Symfony\\Component\\Routing\\Matcher\\UrlMatcher->matchRequest()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(2119): Symfony\\Component\\Routing\\Router->matchRequest()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3265): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php:2135\nStack trace:\n#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3265): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#7 {main}", + "error.message": "Thrown Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/appProdProjectContainerUrlMatcher.php:57", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1455): appProdProjectContainerUrlMatcher->match()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1318): Symfony\\Component\\Routing\\Matcher\\UrlMatcher->matchRequest()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(2119): Symfony\\Component\\Routing\\Router->matchRequest()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3265): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#10 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /does_not_exist\" in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php:2135\nStack trace:\n#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1886): Symfony\\Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/cache/prod/classes.php(1801): Symfony\\Component\\EventDispatcher\\EventDispatcher->doDispatch()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3265): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#7 {main}", "error.type": "Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception.json index 5785921142..d5292f1bbf 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,12 +12,12 @@ "_dd.p.dm": "-0", "_dd.p.tid": "6660700e00000000", "component": "symfony", - "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3275): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", + "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3275): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/app.php/error?key=value&", + "http.url": "http://localhost/app.php/error?key=value&", "runtime-id": "4374c14f-def1-4b62-97c0-598f46322850", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@errorAction", @@ -111,8 +111,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3275): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", + "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3275): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3388): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception_apache.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception_apache.json index 80f9337a9e..d44ebe924d 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_exception_apache.json @@ -12,12 +12,12 @@ "_dd.p.dm": "-0", "_dd.p.tid": "6663129e00000000", "component": "symfony", - "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3277): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", + "error.message": "Uncaught Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3277): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", "error.type": "Exception", "http.method": "GET", "http.status_code": "500", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "686fd8bf-e7d3-47f9-bbae-eadd9fdb7bfd", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@errorAction", @@ -111,8 +111,8 @@ "error": 1, "meta": { "component": "symfony", - "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3277): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", + "error.message": "Thrown Exception (500): An exception occurred in /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php:40", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3277): AppBundle\\Controller\\CommonScenariosController->errorAction()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3234): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(3392): Symfony\\Component\\HttpKernel\\HttpKernel->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/app/bootstrap.php.cache(2594): Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_2_8/web/app.php(15): Symfony\\Component\\HttpKernel\\Kernel->handle()\n#5 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view.json index bc9c3810ab..37bea1d81a 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/app.php/simple_view?key=value&", + "http.url": "http://localhost/app.php/simple_view?key=value&", "runtime-id": "a908b910-f481-44e7-bf7b-8d48954a4639", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleViewAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view_apache.json b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view_apache.json index 9723d0599f..d2b15a256e 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.common_scenarios_test.test_scenario_get_with_view_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "86589533-e0a0-485f-bc9a-135cbd0cf009", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\CommonScenariosController@simpleViewAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping.json b/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping.json index a9f8a874f9..f6586cf06a 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/app.php?key=value&", + "http.url": "http://localhost/app.php?key=value&", "runtime-id": "4b578cb2-1f1d-441e-aacb-a4aaff240713", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\DefaultController@testingRouteNameAction", diff --git a/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping_apache.json b/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping_apache.json index 9e79731fe6..ea4b9f9c97 100644 --- a/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping_apache.json +++ b/tests/snapshots/tests.integrations.symfony.v2_8.route_name_test.test_resource_to_uri_mapping_apache.json @@ -13,7 +13,7 @@ "component": "symfony", "http.method": "GET", "http.status_code": "200", - "http.url": "http://localhost:9999/?key=value&", + "http.url": "http://localhost/?key=value&", "runtime-id": "a57c3bd6-89e0-4682-b232-7fceb6a5d409", "span.kind": "server", "symfony.route.action": "AppBundle\\Controller\\DefaultController@testingRouteNameAction", diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json index 56149bed8d..a94467e5ad 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "b4ee1995-4afb-4457-9e9d-b361460bfa16", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 27245bd3bb..2adc90fcde 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e7019100000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "b4ee1995-4afb-4457-9e9d-b361460bfa16", "span.kind": "server" }, @@ -400,8 +400,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -428,8 +428,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json index 0294b312db..4cea0bce0d 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "(.?.+?)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "b4ee1995-4afb-4457-9e9d-b361460bfa16", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json index 123707ce63..34e87afcbd 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "8fdcf6ef-7cd9-4910-b426-c7c9809f3dd4", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json index a804c59ea9..9879dbab07 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e701c900000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "8fdcf6ef-7cd9-4910-b426-c7c9809f3dd4", "span.kind": "server" }, @@ -248,8 +248,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -276,8 +276,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(298): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/plugin.php(515): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/class-wp.php(735): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-includes/functions.php(955): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_4_8/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json index afea354d76..3da297ba27 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "(.?.+?)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "8fdcf6ef-7cd9-4910-b426-c7c9809f3dd4", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json index 68f0766b86..5fe007192c 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "4ad4333f-2e0b-4278-a6f7-2182e7771b34", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json index 3b8d8a4ee4..2f1cd43bf7 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "4ad4333f-2e0b-4278-a6f7-2182e7771b34", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 9fe8f0edfc..c99f52c4c2 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e7011700000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "4ad4333f-2e0b-4278-a6f7-2182e7771b34", "span.kind": "server" }, @@ -680,8 +680,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -708,8 +708,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json index 300851b9bc..f74b08cfaf 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "4ad4333f-2e0b-4278-a6f7-2182e7771b34", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json index 839b9656bb..b2cc305f4f 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "f188c752-a672-4955-97f7-e41a31d13fe7", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_to_missing_route.json index 788a941f34..80cf347caa 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "f188c752-a672-4955-97f7-e41a31d13fe7", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json index b884cb598d..f3e3533156 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e7016000000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "f188c752-a672-4955-97f7-e41a31d13fe7", "span.kind": "server" }, @@ -248,8 +248,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -276,8 +276,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(287): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp-hook.php(311): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/plugin.php(544): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(388): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/class-wp.php(745): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-includes/functions.php(1285): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_5/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json index 492176538a..ca8d5fe134 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "f188c752-a672-4955-97f7-e41a31d13fe7", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json index 216092a205..ebfef74a26 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "896f86bc-7139-44f3-a99f-ed35e643f726", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json index 9f7a269e17..07c0e83a94 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "896f86bc-7139-44f3-a99f-ed35e643f726", "span.kind": "server" }, @@ -2065,7 +2065,7 @@ "parent_id": 124, "type": "web", "meta": { - "closure.declaration": "/home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/block-supports/duotone.php:464", + "closure.declaration": "/home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/block-supports/duotone.php:464", "component": "wordpress", "wordpress.callback": "Closure", "wordpress.hook": "wp_footer" diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 87568082bb..1e4f677a7f 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e7000f00000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "896f86bc-7139-44f3-a99f-ed35e643f726", "span.kind": "server" }, @@ -1226,8 +1226,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -1254,8 +1254,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json index 719184349d..26b4263431 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "896f86bc-7139-44f3-a99f-ed35e643f726", "span.kind": "server" }, @@ -2143,7 +2143,7 @@ "parent_id": 130, "type": "web", "meta": { - "closure.declaration": "/home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/block-supports/duotone.php:464", + "closure.declaration": "/home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/block-supports/duotone.php:464", "component": "wordpress", "wordpress.callback": "Closure", "wordpress.hook": "wp_footer" diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json index 945e9fe194..a2f281d70d 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "4c46007f-c934-41aa-bcbe-c48ecee2d4cc", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_to_missing_route.json index 1cd65d6f3b..29e5c09037 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,7 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.status_code": "404", - "http.url": "http://localhost:9999/does_not_exist?key=value&", + "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "4c46007f-c934-41aa-bcbe-c48ecee2d4cc", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json index 083ca48221..2efffe0509 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e7005c00000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "4c46007f-c934-41aa-bcbe-c48ecee2d4cc", "span.kind": "server" }, @@ -248,8 +248,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -276,8 +276,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-content/plugins/datadog/datadog.php:23", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(307): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/plugin.php(522): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(396): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/class-wp.php(758): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-includes/functions.php(1310): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_5_9/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json index d281b5f8ec..671eb3f7d1 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "4c46007f-c934-41aa-bcbe-c48ecee2d4cc", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json index 1bbbc80ab1..e6d2db3849 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "333590aa-cf9b-4804-9dde-1ac7b59c09ab", "span.kind": "server" }, @@ -1459,7 +1459,7 @@ "parent_id": 10, "type": "web", "meta": { - "closure.declaration": "/home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/script-loader.php:3398", + "closure.declaration": "/home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/script-loader.php:3398", "component": "wordpress", "wordpress.callback": "Closure", "wordpress.hook": "wp_loaded" diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 1584ac8565..031cdd1f31 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e6feb000000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "333590aa-cf9b-4804-9dde-1ac7b59c09ab", "span.kind": "server" }, @@ -1463,7 +1463,7 @@ "parent_id": 10, "type": "web", "meta": { - "closure.declaration": "/home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/script-loader.php:3398", + "closure.declaration": "/home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/script-loader.php:3398", "component": "wordpress", "wordpress.callback": "Closure", "wordpress.hook": "wp_loaded" @@ -1494,8 +1494,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -1522,8 +1522,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json index c93632d63a..19747ed578 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "333590aa-cf9b-4804-9dde-1ac7b59c09ab", "span.kind": "server" }, @@ -1459,7 +1459,7 @@ "parent_id": 10, "type": "web", "meta": { - "closure.declaration": "/home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/script-loader.php:3398", + "closure.declaration": "/home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/script-loader.php:3398", "component": "wordpress", "wordpress.callback": "Closure", "wordpress.hook": "wp_loaded" diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json index b8a960127b..cfbeb419d9 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple?key=value&", + "http.url": "http://localhost/simple?key=value&", "runtime-id": "df54db4d-0cc0-4b1c-9fce-8004a54aa78b", "span.kind": "server" }, diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json index b689214e37..b71f520f3c 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json @@ -12,13 +12,13 @@ "_dd.p.dm": "-0", "_dd.p.tid": "65e6ff0700000000", "component": "wordpress", - "error.message": "Uncaught Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", + "error.message": "Uncaught Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/error?key=value&", + "http.url": "http://localhost/error?key=value&", "runtime-id": "df54db4d-0cc0-4b1c-9fce-8004a54aa78b", "span.kind": "server" }, @@ -235,8 +235,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, @@ -263,8 +263,8 @@ "error": 1, "meta": { "component": "wordpress", - "error.message": "Thrown Exception: Oops! in /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", - "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/datadog/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", + "error.message": "Thrown Exception: Oops! in /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-content/plugins/datadog/datadog.php:20", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(308): datadog_parse_request()\n#1 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()\n#2 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/plugin.php(565): WP_Hook->do_action()\n#3 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(399): do_action_ref_array()\n#4 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/class-wp.php(780): WP->parse_request()\n#5 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-includes/functions.php(1332): WP->main()\n#6 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/wp-blog-header.php(16): wp()\n#7 /home/circleci/app/tests/Frameworks/WordPress/Version_6_1/index.php(17): require()\n#8 {main}", "error.type": "Exception" } }, diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json index 50c224d266..4b9c200068 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json @@ -14,7 +14,7 @@ "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", "http.status_code": "200", - "http.url": "http://localhost:9999/simple_view?key=value&", + "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "df54db4d-0cc0-4b1c-9fce-8004a54aa78b", "span.kind": "server" }, From 14de5d582f7277b57c89f32d7103300b940c0f90 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Tue, 20 Aug 2024 15:44:32 +0200 Subject: [PATCH 029/103] Fix flaky test (#2800) Other logs, like `[ddtrace] [datadog_sidecar::service::blocking] The sidecar transport is closed. Reconnecting...` can be mixed in the output --- tests/ext/autoload-php-files/dd_init_open_basedir.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ext/autoload-php-files/dd_init_open_basedir.phpt b/tests/ext/autoload-php-files/dd_init_open_basedir.phpt index a04f9b4f04..291b1376ec 100644 --- a/tests/ext/autoload-php-files/dd_init_open_basedir.phpt +++ b/tests/ext/autoload-php-files/dd_init_open_basedir.phpt @@ -1,7 +1,7 @@ --TEST-- Calling dd_init.php is confined to open_basedir settings --ENV-- -DD_TRACE_LOG_LEVEL=info,startup=off +DD_TRACE_LOG_LEVEL=info,startup=off,datadog_sidecar=off --INI-- open_basedir="{PWD}" datadog.trace.sources_path="{PWD}/.." From e79d9c33d58da7786fa566755e6a0c3944a25200 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 21 Aug 2024 13:31:17 +0200 Subject: [PATCH 030/103] Send x-datadog-test-session-token metric and send metrics to request-replayer (#2802) This allows for clear test session separation without interference from other processes. --- Makefile | 2 +- .../request-replayer/linux.Dockerfile | 1 + .../services/request-replayer/src/index.php | 92 ++++++++++++++++++- .../request-replayer/src/metricsserver.php | 8 ++ .../request-replayer/windows.Dockerfile | 1 + ext/sidecar.c | 4 + tests/Common/SnapshotTestTrait.php | 40 +++----- tests/Common/TracerTestTrait.php | 20 +++- tests/Common/UDPServer.php | 41 --------- 9 files changed, 137 insertions(+), 72 deletions(-) create mode 100644 dockerfiles/services/request-replayer/src/metricsserver.php delete mode 100644 tests/Common/UDPServer.php diff --git a/Makefile b/Makefile index 074f046978..7e582fe2b4 100644 --- a/Makefile +++ b/Makefile @@ -489,7 +489,7 @@ cores: # TESTS ######################################################################################################################## TRACER_SOURCES_INI := -d datadog.trace.sources_path=$(TRACER_SOURCE_DIR) -ENV_OVERRIDE := $(shell [ -n "${DD_TRACE_DOCKER_DEBUG}" ] && echo DD_AUTOLOAD_NO_COMPILE=true DD_TRACE_SOURCES_PATH=$(TRACER_SOURCE_DIR)) DD_DOGSTATSD_URL=http://127.0.0.1:9876 DD_TRACE_CLI_ENABLED=true DD_TRACE_GIT_METADATA_ENABLED=false +ENV_OVERRIDE := $(shell [ -n "${DD_TRACE_DOCKER_DEBUG}" ] && echo DD_AUTOLOAD_NO_COMPILE=true DD_TRACE_SOURCES_PATH=$(TRACER_SOURCE_DIR)) DD_DOGSTATSD_URL=http://request-replayer:80 DD_TRACE_CLI_ENABLED=true DD_TRACE_GIT_METADATA_ENABLED=false TEST_EXTRA_INI ?= TEST_EXTRA_ENV ?= diff --git a/dockerfiles/services/request-replayer/linux.Dockerfile b/dockerfiles/services/request-replayer/linux.Dockerfile index 9928511205..040be82310 100644 --- a/dockerfiles/services/request-replayer/linux.Dockerfile +++ b/dockerfiles/services/request-replayer/linux.Dockerfile @@ -9,6 +9,7 @@ WORKDIR /var/www COPY src /var/www EXPOSE 80 +EXPOSE 80/udp RUN composer install diff --git a/dockerfiles/services/request-replayer/src/index.php b/dockerfiles/services/request-replayer/src/index.php index d1edcfda67..8363e9ed55 100644 --- a/dockerfiles/services/request-replayer/src/index.php +++ b/dockerfiles/services/request-replayer/src/index.php @@ -12,9 +12,61 @@ exit; } +function decodeDogStatsDMetrics($metrics) +{ + $metrics = array_filter($metrics); + + // Format of DogStatsD metrics: metric_name:value|type|#tag1:value1,tag2:value2 + // Parts: |-> 0 |-> 1|-> 2 + $decodedMetrics = []; + foreach ($metrics as $metric) { + $parts = explode('|', $metric); + + $nameAndValue = explode(':', $parts[0]); + $metricName = $nameAndValue[0]; + $value = $nameAndValue[1]; + + $type = $parts[1]; + + $tags = []; + if (count($parts) > 2) { + $parts[2] = substr($parts[2], 1); // Remove leading # + $tags = explode(',', $parts[2]); + $tags = array_map(function ($tag) { + return explode(':', $tag); + }, $tags); + $tags = array_combine(array_column($tags, 0), array_column($tags, 1)); + } + $decodedMetrics[] = [ + 'name' => $metricName, + 'value' => $value, + 'type' => $type, + 'tags' => $tags, + ]; + } + return $decodedMetrics; +} + +$uri = explode("?", $_SERVER['REQUEST_URI'])[0]; + $temp_location = sys_get_temp_dir(); -if ("" != $token = ($_SERVER["HTTP_X_DATADOG_TEST_SESSION_TOKEN"] ?? "")) { +$metricsServerPid = "$temp_location/metrics-server.pid"; +if (!file_exists($metricsServerPid)) { + shell_exec("nohup bash -c 'php metricsserver.php & pid=$!; echo \$pid > $metricsServerPid; wait \$pid; rm $metricsServerPid' > /dev/null 2>&1 &"); +} + +$token = $_SERVER["HTTP_X_DATADOG_TEST_SESSION_TOKEN"] ?? ""; + +if ($uri === "/metrics") { + $decodedMetrics = decodeDogStatsDMetrics(explode("\n", trim($_GET["metrics"], "\n"))); + if (isset($decodedMetrics[0]["tags"]["x-datadog-test-session-token"])) { + $token = $decodedMetrics[0]["tags"]["x-datadog-test-session-token"]; + unset($decodedMetrics[0]["tags"]["x-datadog-test-session-token"]); + } +} + +if ($token != "") { $token = str_replace("/", "-", $token); $temp_location .= "/token-$token"; @mkdir($temp_location); @@ -24,6 +76,8 @@ define('REQUEST_NEXT_RESPONSE_FILE', getenv('REQUEST_NEXT_RESPONSE_FILE') ?: ("$temp_location/response.json")); define('REQUEST_LOG_FILE', getenv('REQUEST_LOG_FILE') ?: ("$temp_location/requests-log.txt")); define('REQUEST_RC_CONFIGS_FILE', getenv('REQUEST_RC_CONFIGS_FILE') ?: ("$temp_location/rc_configs.json")); +define('REQUEST_METRICS_FILE', getenv('REQUEST_METRICS_FILE') ?: ("$temp_location/metrics.json")); +define('REQUEST_METRICS_LOG_FILE', getenv('REQUEST_METRICS_LOG_FILE') ?: ("$temp_location/metrics-log.txt")); function logRequest($message, $data = '') { @@ -37,13 +91,13 @@ function logRequest($message, $data = '') } set_error_handler(function ($number, $message, $errfile, $errline) { - logRequest("Triggered error $number $message in $errfile on line $errline"); + logRequest("Triggered error $number $message in $errfile on line $errline: " . (new \Exception)->getTraceAsString()); trigger_error($message, $number); }); $rc_configs = file_exists(REQUEST_RC_CONFIGS_FILE) ? json_decode(file_get_contents(REQUEST_RC_CONFIGS_FILE), true) : []; -switch (explode("?", $_SERVER['REQUEST_URI'])[0]) { +switch ($uri) { case '/replay': if (!file_exists(REQUEST_LATEST_DUMP_FILE)) { logRequest('Cannot replay last request; request log does not exist'); @@ -55,8 +109,19 @@ function logRequest($message, $data = '') unlink(REQUEST_LOG_FILE); logRequest('Returned last request and deleted request log', $request); break; + case '/replay-metrics': + if (!file_exists(REQUEST_METRICS_FILE)) { + logRequest('Cannot replay last request; metrics log does not exist'); + break; + } + $request = file_get_contents(REQUEST_METRICS_FILE); + echo $request; + unlink(REQUEST_METRICS_FILE); + unlink(REQUEST_METRICS_LOG_FILE); + logRequest('Returned last metrics and deleted metrics log', $request); + break; case '/clear-dumped-data': - if (!file_exists(REQUEST_LATEST_DUMP_FILE) && !file_exists(REQUEST_RC_CONFIGS_FILE)) { + if (!file_exists(REQUEST_LATEST_DUMP_FILE) && !file_exists(REQUEST_METRICS_FILE) && !file_exists(REQUEST_RC_CONFIGS_FILE)) { logRequest('Cannot delete request log; request log does not exist'); break; } @@ -67,6 +132,10 @@ function logRequest($message, $data = '') unlink(REQUEST_LATEST_DUMP_FILE); unlink(REQUEST_LOG_FILE); } + if (file_exists(REQUEST_METRICS_FILE)) { + unlink(REQUEST_METRICS_FILE); + unlink(REQUEST_METRICS_LOG_FILE); + } if (file_exists(REQUEST_NEXT_RESPONSE_FILE)) { unlink(REQUEST_NEXT_RESPONSE_FILE); } @@ -127,6 +196,21 @@ function logRequest($message, $data = '') $response["targets"] = base64_encode(json_encode($response["targets"], JSON_UNESCAPED_SLASHES)); echo json_encode($response, JSON_UNESCAPED_SLASHES); break; + case "/metrics": + $_SERVER['REQUEST_URI'] = $uri; + logRequest('Logged new metrics', json_encode($decodedMetrics)); + foreach ($decodedMetrics as $metric) { + file_put_contents(REQUEST_METRICS_LOG_FILE, json_encode($metric) . "\n", FILE_APPEND); + + if (file_exists(REQUEST_METRICS_FILE)) { + $allMetrics = json_decode(file_get_contents(REQUEST_METRICS_FILE), true); + } else { + $allMetrics = []; + } + $allMetrics[] = $metric; + file_put_contents(REQUEST_METRICS_FILE, json_encode($allMetrics)); + } + break; default: $headers = getallheaders(); if (isset($headers['X-Datadog-Diagnostic-Check']) || isset($headers['x-datadog-diagnostic-check'])) { diff --git a/dockerfiles/services/request-replayer/src/metricsserver.php b/dockerfiles/services/request-replayer/src/metricsserver.php new file mode 100644 index 0000000000..dda9bde3bc --- /dev/null +++ b/dockerfiles/services/request-replayer/src/metricsserver.php @@ -0,0 +1,8 @@ +server = new UDPServer(self::$dogstatsdAddr, self::$dogstatsdPort); - } - /** * Start a snapshotting session associated with a given token. * @@ -68,9 +59,8 @@ private function startMetricsSnapshotSession() * @param string $token The token to associate with the snapshotting session * @return void */ - private function startSnapshotSession(string $token, $snapshotMetrics = false, $logsFile = null) + private function startSnapshotSession(string $token, $logsFile = null) { - $url = self::$testAgentUrl . '/test/session/start?test_session_token=' . $token; $ch = curl_init($url); @@ -81,10 +71,6 @@ private function startSnapshotSession(string $token, $snapshotMetrics = false, $ TestCase::fail('Error starting snapshot session: ' . $response); } - if ($snapshotMetrics) { - $this->startMetricsSnapshotSession(); - } - if ($logsFile) { if (file_exists($logsFile)) { $this->logFileSize = (int)filesize($logsFile); @@ -214,8 +200,10 @@ private function stopAndCompareMetricsSnapshotSession( string $token, array $fieldsToIgnore = ['openai.request.duration'] ) { - $receivedMetrics = $this->server->dump(); - $this->server->close(); + $receivedMetrics = $this->retrieveDumpedMetrics(function($metrics) { + return $metrics["name"] == "tracer-snapshot-end"; + }); + array_pop($receivedMetrics); $basePath = implode('/', array_slice(explode('/', getcwd()), 0, 4)); // /home/circleci/[app|datadog] $expectedMetricsFile = $basePath . '/tests/snapshots/metrics/' . $token . '.txt'; @@ -230,10 +218,7 @@ private function stopAndCompareMetricsSnapshotSession( private function compareMetrics($expectedMetrics, $receivedMetrics, $fieldsToIgnore) { $expectedMetrics = explode("\n", $expectedMetrics); - $receivedMetrics = explode("\n", $receivedMetrics); - $expectedMetrics = $this->decodeDogStatsDMetrics($expectedMetrics); - $receivedMetrics = $this->decodeDogStatsDMetrics($receivedMetrics); $this->compareMetricsArrays($expectedMetrics, $receivedMetrics, $fieldsToIgnore); } @@ -345,10 +330,11 @@ public function isolateTracerSnapshot( self::putEnv('DD_TRACE_AGENT_RETRIES=3'); $token = $this->generateToken(); - $this->startSnapshotSession($token, $snapshotMetrics, $logsFile); + $this->startSnapshotSession($token, $logsFile); $originalToken = ini_get("datadog.trace.agent_test_session_token"); update_test_agent_session_token($token); + $this->resetRequestDumper(); $this->resetTracer($tracer, $config); $tracer = GlobalTracer::get(); @@ -357,15 +343,15 @@ public function isolateTracerSnapshot( } $fn($tracer); + if ($snapshotMetrics) { + \DDTrace\dogstatsd_count("tracer-snapshot-end", 1); + } + $traces = $this->flushAndGetTraces($tracer); if (!empty($traces)) { $this->sendTracesToTestAgent($traces); } - update_test_agent_session_token($originalToken); - self::putEnv('DD_TRACE_SHUTDOWN_TIMEOUT'); - self::putEnv('DD_TRACE_AGENT_RETRIES'); - $this->stopAndCompareSnapshotSession( $token, $fieldsToIgnore, @@ -375,5 +361,9 @@ public function isolateTracerSnapshot( $logsFile, $fieldsToIgnoreLogs ); + + update_test_agent_session_token($originalToken); + self::putEnv('DD_TRACE_SHUTDOWN_TIMEOUT'); + self::putEnv('DD_TRACE_AGENT_RETRIES'); } } diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index debb1c6851..8da57c21c3 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -444,13 +444,31 @@ public function retrieveDumpedData(callable $until = null, $throw = false) }; } + return $this->retrieveAnyDumpedData($until, $throw); + } + + /** + * Returns the raw response body, if any, or null otherwise. + */ + public function retrieveDumpedMetrics(callable $until = null, $throw = false) + { + if (!$until) { + $until = function ($request) { + return (strpos($request["uri"] ?? "", "/telemetry/") !== 0); + }; + } + + return $this->retrieveAnyDumpedData($until, $throw, true); + } + + public function retrieveAnyDumpedData(callable $until, $throw, $metrics = false) { $allResponses = []; // When tests run with the background sender enabled, there might be some delay between when a trace is flushed // and actually sent. While we should find a smart way to tackle this, for now we do it quick and dirty, in a // for loop. for ($attemptNumber = 1; $attemptNumber <= 50; $attemptNumber++) { - $curl = curl_init(self::$agentRequestDumperUrl . '/replay'); + $curl = curl_init(self::$agentRequestDumperUrl . '/replay' . ($metrics ? '-metrics' : '')); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, ['x-datadog-test-session-token: ' . ini_get("datadog.trace.agent_test_session_token")]); // Retrieving data diff --git a/tests/Common/UDPServer.php b/tests/Common/UDPServer.php deleted file mode 100644 index 2c4c72cbd1..0000000000 --- a/tests/Common/UDPServer.php +++ /dev/null @@ -1,41 +0,0 @@ -socket = socket_create(AF_INET, SOCK_DGRAM, 0))) { - $errorCode = socket_last_error(); - $errorMessage = socket_strerror($errorCode); - die("Couldn't create socket: [$errorCode] $errorMessage\n"); - } - - if (!socket_bind($this->socket, $addr, $port)) { - $errorCode = socket_last_error(); - $errorMessage = socket_strerror($errorCode); - die("Couldn't bind socket: [$errorCode] $errorMessage\n"); - } - - - } - - public function dump($iter = 100, $usleep = 100) { - $ret = ''; - $buf = ''; - for ($i = 0; $i < $iter; $i++) { - usleep($usleep); - $r = socket_recvfrom($this->socket, $buf, 2048, MSG_DONTWAIT, $remote_ip, $remote_port); - if ($buf) { - $ret .= $buf . PHP_EOL; - $buf = ''; - } - } - return $ret; - } - - public function close() { - socket_close($this->socket); - } -} From 5f56332bef42ca87352aa6d77fdd966745e3ddb4 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 21 Aug 2024 13:31:33 +0200 Subject: [PATCH 031/103] Fix setup with spaces in path to php binary (#2803) e.g. C:\Program Files\php\php.exe --- datadog-setup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog-setup.php b/datadog-setup.php index 018ed5db39..2e8a34b120 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -1101,7 +1101,7 @@ function check_library_prerequisite_or_exit($requiredLibrary) */ function check_php_ext_prerequisite_or_exit($binary, $extName) { - $extensions = shell_exec("$binary -m"); + $extensions = shell_exec(escapeshellarg($binary) . " -m"); if (!in_array($extName, array_map("trim", explode("\n", $extensions)))) { print_error_and_exit("Required PHP extension '$extName' not found.\n"); From 4172cb820396888514e77eb9b1045ed07f9b2d0f Mon Sep 17 00:00:00 2001 From: Maximo Bautista Date: Wed, 21 Aug 2024 07:32:56 -0400 Subject: [PATCH 032/103] [Tracer] Adds SpanEvents Support for DD and OTEL (#2754) * Adding class details for SpanEvent * Updated serializer and other C files to add event * Adding API to add events and use existent ones * Adding event and checking in test * Trying to test Otel to DD * Fixing test and applying nit changes * Currently passing full AddEvent and RecordException tests * Updating how we recordException * Removing manually added code * Added constructor and updated args were needed * Doing fixes so that test pass * Fixing missing CLOCK_REALTIME for Windows build error * Using inhouse time method and fixing array errors * Implementing addEvent for system-tests * Adding ExceptionSpanEvent class and recordException * Applying C code nits and limiting recordException * Merging master and Updating ddtrace_arginfo.h * Applying nits and using exception property in C * Adding manual usage work for both classes * Trying to fix CircleCI build nts extension errors * Trying to change where attributes are handled * Cleanup span event serialization Signed-off-by: Bob Weinand * Updating broken tests * Fixing what seems to be a version issue * Tested with 7.4 passing * Fixing test based on updated behavior * Initializing empty array * Addressing nits, updating libdatadog and recording exceptions * Cleanup implementation slightly Signed-off-by: Bob Weinand --------- Signed-off-by: Bob Weinand Co-authored-by: Bob Weinand --- ext/ddtrace.c | 135 ++++++++++++++++ ext/ddtrace.h | 4 + ext/ddtrace.stub.php | 51 ++++++ ext/ddtrace_arginfo.h | 83 +++++++++- ext/serializer.c | 17 ++ ext/span.h | 18 +++ src/DDTrace/OpenTelemetry/Context.php | 7 + src/DDTrace/OpenTelemetry/Span.php | 91 ++++++++++- src/DDTrace/OpenTelemetry/SpanBuilder.php | 42 +++++ .../Integration/API/TracerTest.php | 5 - .../Integration/InteroperabilityTest.php | 147 ++++++++++++++++++ tests/ext/active_span.phpt | 5 +- .../dd_trace_exception_span_event.phpt | 54 +++++++ .../request-replayer/dd_trace_span_event.phpt | 47 ++++++ tests/ext/sandbox/span_clone.phpt | 15 +- 15 files changed, 703 insertions(+), 18 deletions(-) create mode 100644 tests/ext/request-replayer/dd_trace_exception_span_event.phpt create mode 100644 tests/ext/request-replayer/dd_trace_span_event.phpt diff --git a/ext/ddtrace.c b/ext/ddtrace.c index cc78f7b477..dd81d580f7 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -625,6 +625,138 @@ static PHP_GSHUTDOWN_FUNCTION(ddtrace) { #endif } +static void dd_span_event_construct(ddtrace_span_event *event, zend_string *name, zend_long timestamp, zval *attributes) +{ + zval garbage_name, garbage_timestamp, garbage_attributes; + + // Copy current values to temporary zval variables + ZVAL_COPY_VALUE(&garbage_name, &event->property_name); + ZVAL_COPY_VALUE(&garbage_timestamp, &event->property_timestamp); + ZVAL_COPY_VALUE(&garbage_attributes, &event->property_attributes); + + ZVAL_STR_COPY(&event->property_name, name); + + // Use the provided timestamp or the current time in nanoseconds + if (timestamp == 0) { + struct timespec ts; + timespec_get(&ts, TIME_UTC); + timestamp = ts.tv_sec * ZEND_NANO_IN_SEC + ts.tv_nsec; + } + ZVAL_LONG(&event->property_timestamp, timestamp); + + // Initialize attributes + if (attributes) { + ZVAL_COPY(&event->property_attributes, attributes); + } else { + array_init(&event->property_attributes); + } + + // Free the copied values after replacement + zval_ptr_dtor(&garbage_name); + zval_ptr_dtor(&garbage_timestamp); + zval_ptr_dtor(&garbage_attributes); +} + +/* DDTrace\SpanEvent */ +zend_class_entry *ddtrace_ce_span_event; + +PHP_METHOD(DDTrace_SpanEvent, jsonSerialize) { + ddtrace_span_event *event = (ddtrace_span_event*)Z_OBJ_P(ZEND_THIS); + + zval array; + array_init(&array); + + Z_TRY_ADDREF(event->property_name); + add_assoc_zval_ex(&array, ZEND_STRL("name"), &event->property_name); + Z_TRY_ADDREF(event->property_timestamp); + add_assoc_zval_ex(&array, ZEND_STRL("time_unix_nano"), &event->property_timestamp); + + // Handle attributes dynamically + zval *attributes = &event->property_attributes; + zval combined_attributes; + array_init(&combined_attributes); + + if (instanceof_function(event->std.ce, ddtrace_ce_exception_span_event)) { + // Handle exception attributes dynamically if an exception property exists + ddtrace_exception_span_event *exception_event = (ddtrace_exception_span_event *) event; + zval *exception = &exception_event->property_exception; + if (Z_TYPE_P(exception) == IS_OBJECT && instanceof_function(Z_OBJCE_P(exception), zend_ce_throwable)) { + // Get exception message, type, and stack trace directly + zend_string *message = zai_exception_message(Z_OBJ_P(exception)); + if (ZSTR_LEN(message)) { + add_assoc_str_ex(&combined_attributes, ZEND_STRL("exception.message"), zend_string_copy(message)); + } + add_assoc_str_ex(&combined_attributes, ZEND_STRL("exception.type"), zend_string_copy(Z_OBJCE_P(exception)->name)); + + // Get the exception stack trace using zai_get_trace_without_args_from_exception + zend_string *stacktrace = zai_get_trace_without_args_from_exception(Z_OBJ_P(exception)); + add_assoc_str_ex(&combined_attributes, ZEND_STRL("exception.stacktrace"), stacktrace); + } + } + + if (Z_TYPE_P(attributes) == IS_ARRAY) { + zend_hash_copy(Z_ARRVAL(combined_attributes), Z_ARRVAL_P(attributes), (copy_ctor_func_t)zval_add_ref); + } + + if (zend_hash_num_elements(Z_ARRVAL(combined_attributes)) > 0) { + add_assoc_zval_ex(&array, ZEND_STRL("attributes"), &combined_attributes); + } else { + zval_ptr_dtor(&combined_attributes); // Clean up if no elements + } + + RETURN_ARR(Z_ARR(array)); // Return the array +} + +PHP_METHOD(DDTrace_SpanEvent, __construct) +{ + UNUSED(return_value); + + zend_string *name; + zval *attributes = NULL; + zend_long timestamp = 0; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STR(name) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_EX(attributes, 1, 0) + Z_PARAM_LONG(timestamp) + ZEND_PARSE_PARAMETERS_END(); + + ddtrace_span_event *event = (ddtrace_span_event*)Z_OBJ_P(ZEND_THIS); + + // Use the static function to set properties and handle cleanup + dd_span_event_construct(event, name, timestamp, attributes); +} + +/* DDTrace\ExceptionSpanEvent */ +zend_class_entry *ddtrace_ce_exception_span_event; + +PHP_METHOD(DDTrace_ExceptionSpanEvent, __construct) +{ + UNUSED(return_value); + + zval *exception; + zval *attributes = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(exception, zend_ce_throwable) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_EX(attributes, 1, 0) + ZEND_PARSE_PARAMETERS_END(); + + ddtrace_exception_span_event *event = (ddtrace_exception_span_event*)Z_OBJ_P(ZEND_THIS); + + // Use the static function to set properties and handle cleanup + zend_string *name = zend_string_init(ZEND_STRL("exception"), 0); + dd_span_event_construct(&event->span_event, name, 0, attributes); + zend_string_release(name); + + zval garbage; + ZVAL_COPY_VALUE(&garbage, &event->property_exception); + ZVAL_COPY(&event->property_exception, exception); + zval_ptr_dtor(&garbage); +} + /* DDTrace\SpanLink */ zend_class_entry *ddtrace_ce_span_link; @@ -718,6 +850,7 @@ static zend_object *dd_init_span_data_object(zend_class_entry *class_type, ddtra array_init(&span->property_metrics); array_init(&span->property_meta_struct); array_init(&span->property_links); + array_init(&span->property_events); array_init(&span->property_peer_service_sources); #endif // Explicitly assign property-mapped NULLs @@ -1169,6 +1302,8 @@ static PHP_MINIT_FUNCTION(ddtrace) { dd_register_fatal_error_ce(); ddtrace_ce_integration = register_class_DDTrace_Integration(); ddtrace_ce_span_link = register_class_DDTrace_SpanLink(php_json_serializable_ce); + ddtrace_ce_span_event = register_class_DDTrace_SpanEvent(php_json_serializable_ce); + ddtrace_ce_exception_span_event = register_class_DDTrace_ExceptionSpanEvent(ddtrace_ce_span_event); ddtrace_ce_git_metadata = register_class_DDTrace_GitMetadata(); ddtrace_ce_git_metadata->create_object = ddtrace_git_metadata_create; memcpy(&ddtrace_git_metadata_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/ddtrace.h b/ext/ddtrace.h index d6b8fb1d98..94f45127b6 100644 --- a/ext/ddtrace.h +++ b/ext/ddtrace.h @@ -23,6 +23,8 @@ extern zend_class_entry *ddtrace_ce_root_span_data; extern zend_class_entry *ddtrace_ce_span_stack; extern zend_class_entry *ddtrace_ce_fatal_error; extern zend_class_entry *ddtrace_ce_span_link; +extern zend_class_entry *ddtrace_ce_span_event; +extern zend_class_entry *ddtrace_ce_exception_span_event; extern zend_class_entry *ddtrace_ce_integration; extern zend_class_entry *ddtrace_ce_git_metadata; @@ -31,6 +33,8 @@ typedef struct ddtrace_span_data ddtrace_span_data; typedef struct ddtrace_root_span_data ddtrace_root_span_data; typedef struct ddtrace_span_stack ddtrace_span_stack; typedef struct ddtrace_span_link ddtrace_span_link; +typedef struct ddtrace_span_event ddtrace_span_event; +typedef struct ddtrace_exception_span_event ddtrace_exception_span_event; typedef struct ddtrace_git_metadata ddtrace_git_metadata; extern datadog_php_sapi ddtrace_active_sapi; diff --git a/ext/ddtrace.stub.php b/ext/ddtrace.stub.php index c2b6a4fd8a..f10d16dd39 100644 --- a/ext/ddtrace.stub.php +++ b/ext/ddtrace.stub.php @@ -23,6 +23,52 @@ */ const DBM_PROPAGATION_FULL = UNKNOWN; + class SpanEvent implements \JsonSerializable { + /** + * SpanEvent constructor. + * + * @param string $name The event name. + * @param int|null $timestamp The event start time in nanoseconds, if not provided set the current Unix timestamp. + * @param array $attributes Optional attributes for the event. + */ + public function __construct(string $name, array $attributes = [], ?int $timestamp = null) {} + + /** + * @var string The event name + */ + public string $name; + + /** + * @var string[] $attributes + */ + public array $attributes; + + /** + * @var int The event start time in nanoseconds, if not provided set the current Unix timestamp + */ + public int $timestamp; + + /** + * @return mixed + */ + public function jsonSerialize(): mixed {} + } + + class ExceptionSpanEvent extends SpanEvent { + /** + * ExceptionSpanEvent constructor. + * + * @param \Throwable $exception exception to record. + * @param array $attributes Optional attributes for the event. + */ + public function __construct(\Throwable $exception, array $attributes = []) {} + + /** + * @var \Throwable + */ + public \Throwable $exception; + } + class SpanLink implements \JsonSerializable { /** * @var string $traceId A 32-character, lower-case hexadecimal encoded string of the linked trace ID. This field @@ -144,6 +190,11 @@ class SpanData { */ public array $links = []; + /** + * @var SpanEvent[] $spanEvents An array of span events + */ + public array $events = []; + /** * @var string[] $peerServiceSources A sorted list of tag names used to set the `peer.service` tag. If a tag * name is added to this field and the tag exists on the span at serialization time, then the value of the tag diff --git a/ext/ddtrace_arginfo.h b/ext/ddtrace_arginfo.h index d0d048e706..6ef23c7e21 100644 --- a/ext/ddtrace_arginfo.h +++ b/ext/ddtrace_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b7ca444d39b9a8489e4e93042e0f7e7eb9aa8b05 */ + * Stub hash: fa4bda312fa3b405b09e09c6bc81a05d2a8e3372 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_trace_method, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, className, IS_STRING, 0) @@ -271,9 +271,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dd_trace_synchronous_flush, 0, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, "100") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DDTrace_SpanLink_jsonSerialize, 0, 0, IS_MIXED, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DDTrace_SpanEvent___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, attributes, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestamp, IS_LONG, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DDTrace_SpanEvent_jsonSerialize, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DDTrace_ExceptionSpanEvent___construct, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, exception, Throwable, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, attributes, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + +#define arginfo_class_DDTrace_SpanLink_jsonSerialize arginfo_class_DDTrace_SpanEvent_jsonSerialize + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DDTrace_SpanLink_fromHeaders, 0, 1, DDTrace\\SpanLink, 0) ZEND_ARG_TYPE_MASK(0, headersOrCallback, MAY_BE_ARRAY|MAY_BE_CALLABLE, NULL) ZEND_END_ARG_INFO() @@ -361,6 +374,9 @@ ZEND_FUNCTION(DDTrace_trace_function); ZEND_FUNCTION(DDTrace_trace_method); ZEND_FUNCTION(dd_untrace); ZEND_FUNCTION(dd_trace_synchronous_flush); +ZEND_METHOD(DDTrace_SpanEvent, __construct); +ZEND_METHOD(DDTrace_SpanEvent, jsonSerialize); +ZEND_METHOD(DDTrace_ExceptionSpanEvent, __construct); ZEND_METHOD(DDTrace_SpanLink, jsonSerialize); ZEND_METHOD(DDTrace_SpanLink, fromHeaders); ZEND_METHOD(DDTrace_SpanData, getDuration); @@ -444,6 +460,17 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; +static const zend_function_entry class_DDTrace_SpanEvent_methods[] = { + ZEND_ME(DDTrace_SpanEvent, __construct, arginfo_class_DDTrace_SpanEvent___construct, ZEND_ACC_PUBLIC) + ZEND_ME(DDTrace_SpanEvent, jsonSerialize, arginfo_class_DDTrace_SpanEvent_jsonSerialize, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_DDTrace_ExceptionSpanEvent_methods[] = { + ZEND_ME(DDTrace_ExceptionSpanEvent, __construct, arginfo_class_DDTrace_ExceptionSpanEvent___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_DDTrace_SpanLink_methods[] = { ZEND_ME(DDTrace_SpanLink, jsonSerialize, arginfo_class_DDTrace_SpanLink_jsonSerialize, ZEND_ACC_PUBLIC) ZEND_ME(DDTrace_SpanLink, fromHeaders, arginfo_class_DDTrace_SpanLink_fromHeaders, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -491,6 +518,52 @@ static void register_ddtrace_symbols(int module_number) REGISTER_LONG_CONSTANT("DD_TRACE_PRIORITY_SAMPLING_UNSET", DDTRACE_PRIORITY_SAMPLING_UNSET, CONST_PERSISTENT); } +static zend_class_entry *register_class_DDTrace_SpanEvent(zend_class_entry *class_entry_JsonSerializable) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DDTrace", "SpanEvent", class_DDTrace_SpanEvent_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + zend_class_implements(class_entry, 1, class_entry_JsonSerializable); + + zval property_name_default_value; + ZVAL_UNDEF(&property_name_default_value); + zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1); + zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_name_name); + + zval property_attributes_default_value; + ZVAL_UNDEF(&property_attributes_default_value); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); + zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release(property_attributes_name); + + zval property_timestamp_default_value; + ZVAL_UNDEF(&property_timestamp_default_value); + zend_string *property_timestamp_name = zend_string_init("timestamp", sizeof("timestamp") - 1, 1); + zend_declare_typed_property(class_entry, property_timestamp_name, &property_timestamp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_timestamp_name); + + return class_entry; +} + +static zend_class_entry *register_class_DDTrace_ExceptionSpanEvent(zend_class_entry *class_entry_DDTrace_SpanEvent) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DDTrace", "ExceptionSpanEvent", class_DDTrace_ExceptionSpanEvent_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DDTrace_SpanEvent); + + zval property_exception_default_value; + ZVAL_UNDEF(&property_exception_default_value); + zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, 1); + zend_string *property_exception_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); + zend_declare_typed_property(class_entry, property_exception_name, &property_exception_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_exception_class_Throwable, 0, 0)); + zend_string_release(property_exception_name); + + return class_entry; +} + static zend_class_entry *register_class_DDTrace_SpanLink(zend_class_entry *class_entry_JsonSerializable) { zend_class_entry ce, *class_entry; @@ -634,6 +707,12 @@ static zend_class_entry *register_class_DDTrace_SpanData(void) zend_declare_typed_property(class_entry, property_links_name, &property_links_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); zend_string_release(property_links_name); + zval property_events_default_value; + ZVAL_EMPTY_ARRAY(&property_events_default_value); + zend_string *property_events_name = zend_string_init("events", sizeof("events") - 1, 1); + zend_declare_typed_property(class_entry, property_events_name, &property_events_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release(property_events_name); + zval property_peerServiceSources_default_value; ZVAL_EMPTY_ARRAY(&property_peerServiceSources_default_value); zend_string *property_peerServiceSources_name = zend_string_init("peerServiceSources", sizeof("peerServiceSources") - 1, 1); diff --git a/ext/serializer.c b/ext/serializer.c index 279ed53fcd..7f97e61d38 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -1326,6 +1326,23 @@ static void _serialize_meta(zval *el, ddtrace_span_data *span) { EG(exception) = current_exception; } + zend_array *span_events = ddtrace_property_array(&span->property_events); + if (zend_hash_num_elements(span_events) > 0) { + // Save the current exception, if any, and clear it for php_json_encode_serializable_object not to fail + // and zend_call_function to actually call the jsonSerialize method + // Restored after span events are serialized + zend_object* current_exception = EG(exception); + EG(exception) = NULL; + + smart_str buf = {0}; + _dd_serialize_json(span_events, &buf, 0); + add_assoc_str(meta, "events", buf.s); + + // Restore the exception + EG(exception) = current_exception; + } + + zval *git_metadata = &span->root->property_git_metadata; if (git_metadata && Z_TYPE_P(git_metadata) == IS_OBJECT) { ddtrace_git_metadata *metadata = (ddtrace_git_metadata *)Z_OBJ_P(git_metadata); diff --git a/ext/span.h b/ext/span.h index cefb20e0a7..58eb14d57e 100644 --- a/ext/span.h +++ b/ext/span.h @@ -55,6 +55,7 @@ typedef union ddtrace_span_properties { zval property_id; }; zval property_links; + zval property_events; zval property_peer_service_sources; union { union ddtrace_span_properties *parent; @@ -171,6 +172,23 @@ struct ddtrace_span_link { }; }; +struct ddtrace_span_event { + union { + zend_object std; + struct { + char object_placeholder[sizeof(zend_object) - sizeof(zval)]; + zval property_name; + zval property_attributes; + zval property_timestamp; + }; + }; +}; + +struct ddtrace_exception_span_event { + ddtrace_span_event span_event; + zval property_exception; +}; + struct ddtrace_git_metadata { union { zend_object std; diff --git a/src/DDTrace/OpenTelemetry/Context.php b/src/DDTrace/OpenTelemetry/Context.php index d5aa42765c..44910695e9 100644 --- a/src/DDTrace/OpenTelemetry/Context.php +++ b/src/DDTrace/OpenTelemetry/Context.php @@ -189,6 +189,12 @@ private static function activateParent(?SpanData $currentSpan): ContextInterface $links[] = new SDK\Link($linkSpanContext, Attributes::create($spanLink->attributes ?? [])); } + // Check for span events + $events = []; + foreach ($currentSpan->events as $spanEvent) { + $events[] = new SDK\Event($spanEvent->name, (int)$spanEvent->timestamp, Attributes::create((array)$spanEvent->attributes ?? [])); + } + $OTelCurrentSpan = SDK\Span::startSpan( $currentSpan, API\SpanContext::create($currentTraceId, $currentSpanId, $traceFlags, $traceState), // $context @@ -201,6 +207,7 @@ private static function activateParent(?SpanData $currentSpan): ContextInterface [], // $attributesBuilder $links, // $links count($links), // $totalRecordedLinks + $events, //$events false // The span was created using the DD Api ); ObjectKVStore::put($currentSpan, 'otel_span', $OTelCurrentSpan); diff --git a/src/DDTrace/OpenTelemetry/Span.php b/src/DDTrace/OpenTelemetry/Span.php index e459c8b8a0..52779a8775 100644 --- a/src/DDTrace/OpenTelemetry/Span.php +++ b/src/DDTrace/OpenTelemetry/Span.php @@ -6,6 +6,8 @@ use DDTrace\SpanData; use DDTrace\SpanLink; +use DDTrace\SpanEvent; +use DDTrace\ExceptionSpanEvent; use DDTrace\Tag; use DDTrace\OpenTelemetry\Convention; use DDTrace\Util\ObjectKVStore; @@ -46,6 +48,16 @@ final class Span extends API\Span implements ReadWriteSpanInterface /** @readonly */ private int $totalRecordedLinks; + /** + * @readonly + * + * @var list + */ + private array $events; + + /** @readonly */ + private int $totalRecordedEvents; + /** @readonly */ private int $kind; @@ -69,6 +81,7 @@ private function __construct( ResourceInfo $resource, array $links = [], int $totalRecordedLinks = 0, + array $events = [], bool $isRemapped = true ) { $this->span = $span; @@ -80,6 +93,7 @@ private function __construct( $this->resource = $resource; $this->links = $links; $this->totalRecordedLinks = $totalRecordedLinks; + $this->events = $events; $this->status = StatusData::unset(); @@ -91,7 +105,7 @@ private function __construct( $span->name = $this->operationNameConvention = Convention::defaultOperationName($span); } - // Set the span links + // Set the span links and events if ($isRemapped) { // At initialization time (now), only set the links if the span was created using the OTel API // Otherwise, the links were already set in DD's OpenTelemetry\Context\Context @@ -110,6 +124,20 @@ private function __construct( ObjectKVStore::put($spanLink, "link", $link); $span->links[] = $spanLink; } + + foreach ($events as $event) { + /** @var EventInterface $event */ + + $spanEvent = new SpanEvent( + $event->getName(), + $event->getAttributes()->toArray(), + $event->getEpochNanos() + ); + + // Save the event + ObjectKVStore::put($spanEvent, "event", $event); + $span->events[] = $spanEvent; + } } } @@ -136,6 +164,7 @@ public static function startSpan( array $attributes, array $links, int $totalRecordedLinks, + array $events, bool $isRemapped = true // Answers the question "Was the span created using the OTel API?" ): self { self::_setAttributes($span, $attributes); @@ -156,6 +185,7 @@ public static function startSpan( $resource, $links, $totalRecordedLinks, + $events, $isRemapped ); @@ -203,12 +233,13 @@ public function toSpanData(): SpanDataInterface $hasEnded = $this->hasEnded(); $this->updateSpanLinks(); + $this->updateSpanEvents(); return new ImmutableSpan( $this, $this->getName(), $this->links, - [], // TODO: Handle Span Events + $this->events, Attributes::create(array_merge($this->span->meta, $this->span->metrics)), 0, StatusData::create($this->status->getCode(), $this->status->getDescription()), @@ -253,7 +284,7 @@ public function getTotalRecordedLinks(): int public function getTotalRecordedEvents(): int { - return 0; + return $this->totalRecordedEvents; } /** @@ -332,7 +363,14 @@ public function setAttributes(iterable $attributes): SpanInterface */ public function addEvent(string $name, iterable $attributes = [], int $timestamp = null): SpanInterface { - // no-op + if (!$this->hasEnded()) { + $this->span->events[] = new SpanEvent( + $name, + $attributes, + $timestamp ?? (int)(microtime(true) * 1e9) + ); + } + return $this; } @@ -342,9 +380,13 @@ public function addEvent(string $name, iterable $attributes = [], int $timestamp public function recordException(Throwable $exception, iterable $attributes = []): SpanInterface { if (!$this->hasEnded()) { - $this->span->meta[Tag::ERROR_MSG] = $exception->getMessage(); - $this->span->meta[Tag::ERROR_TYPE] = get_class($exception); - $this->span->meta[Tag::ERROR_STACK] = $exception->getTraceAsString(); + // Update span metadata based on exception stack + $this->setAttribute(Tag::ERROR_STACK, \DDTrace\get_sanitized_exception_trace($exception)); + + $this->span->events[] = new ExceptionSpanEvent( + $exception, + $attributes + ); } return $this; @@ -475,4 +517,39 @@ private function updateSpanLinks() $this->links = $otel; $this->totalRecordedLinks = count($otel); } + + private function updateSpanEvents() + { + $datadogSpanEvents = $this->span->events; + $this->span->meta["events"] = count($this->events); + + $otel = []; + foreach ($datadogSpanEvents as $datadogSpanEvent) { + $exceptionAttributes = []; + $event = ObjectKVStore::get($datadogSpanEvent, "event"); + if ($event === null) { + if ($datadogSpanEvent instanceof ExceptionSpanEvent) { + // Standardized exception attributes + $exceptionAttributes = [ + 'exception.message' => $attributes['exception.message'] ?? $datadogSpanEvent->exception->getMessage(), + 'exception.type' => $attributes['exception.type'] ?? get_class($datadogSpanEvent->exception), + 'exception.stacktrace' => $attributes['exception.stacktrace'] ?? \DDTrace\get_sanitized_exception_trace($datadogSpanEvent->exception) + ]; + } + $event = new Event( + $datadogSpanEvent->name, + (int)$datadogSpanEvent->timestamp, + Attributes::create(array_merge($exceptionAttributes, \is_array($datadogSpanEvent->attributes) ? $datadogSpanEvent->attributes : iterator_to_array($datadogSpanEvent->attributes))) + ); + + // Save the event + ObjectKVStore::put($datadogSpanEvent, "event", $event); + } + $otel[] = $event; + } + + // Update the events + $this->events = $otel; + $this->totalRecordedEvents = count($otel); + } } diff --git a/src/DDTrace/OpenTelemetry/SpanBuilder.php b/src/DDTrace/OpenTelemetry/SpanBuilder.php index 29f1a92438..c6937ab36d 100644 --- a/src/DDTrace/OpenTelemetry/SpanBuilder.php +++ b/src/DDTrace/OpenTelemetry/SpanBuilder.php @@ -17,6 +17,7 @@ use OpenTelemetry\SDK\Common\Attribute\AttributesFactory; use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeInterface; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; +use Throwable; final class SpanBuilder implements API\SpanBuilderInterface { @@ -43,6 +44,9 @@ final class SpanBuilder implements API\SpanBuilderInterface /** @var list */ private array $links = []; + /** @var list */ + private array $events = []; + /** @var array */ private array $attributes; @@ -92,6 +96,42 @@ public function addLink(SpanContextInterface $context, iterable $attributes = [] return $this; } + public function addEvent(string $name, iterable $attributes = [], int $timestamp = null): SpanBuilderInterface + { + $this->events[] = new Event( + $name, + $timestamp ?? (int)(microtime(true) * 1e9), + $this->tracerSharedState + ->getSpanLimits() + ->getEventAttributesFactory() + ->builder($attributes) + ->build(), + ); + + return $this; + } + + public function recordException(Throwable $exception, iterable $attributes = []): SpanBuilderInterface + { + // Standardized exception attributes + $exceptionAttributes = [ + 'exception.message' => $attributes['exception.message'] ?? $exception->getMessage(), + 'exception.type' => $attributes['exception.type'] ?? get_class($exception), + 'exception.stacktrace' => $attributes['exception.stacktrace'] ?? \DDTrace\get_sanitized_exception_trace($exception), + ]; + + // Update span metadata based on exception stack + $this->setAttribute(Tag::ERROR_STACK, $exceptionAttributes['exception.stacktrace']); + + // Merge additional attributes + $allAttributes = array_merge($exceptionAttributes, \is_array($attributes) ? $attributes : iterator_to_array($attributes)); + + // Record the exception event + $this->addEvent('exception', $allAttributes); + + return $this; + } + /** @inheritDoc */ public function setAttribute(string $key, $value): API\SpanBuilderInterface { @@ -156,6 +196,7 @@ public function startSpan(): SpanInterface $this->spanKind, Attributes::create($this->attributes), $this->links, + $this->events ); $span = $span ?? \DDTrace\start_trace_span($this->startEpochNanos); @@ -204,6 +245,7 @@ public function startSpan(): SpanInterface $this->attributes, $this->links, $this->totalNumberOfLinksAdded, + $this->events ); } diff --git a/tests/OpenTelemetry/Integration/API/TracerTest.php b/tests/OpenTelemetry/Integration/API/TracerTest.php index b7aa1d6ed8..4cc6da8c37 100644 --- a/tests/OpenTelemetry/Integration/API/TracerTest.php +++ b/tests/OpenTelemetry/Integration/API/TracerTest.php @@ -433,12 +433,7 @@ public function testRecordException() $span = $traces[0][0]; $this->assertSame('internal', $span['name']); $this->assertSame('test.span', $span['resource']); - $this->assertSame('exception message', $span['meta'][Tag::ERROR_MSG]); - $this->assertSame('RuntimeException', $span['meta'][Tag::ERROR_TYPE]); $this->assertNotEmpty($span['meta'][Tag::ERROR_STACK]); - $this->assertEquals(1, $span['error']); - - $this->markTestIncomplete("Span Events aren't yet supported"); } public function testSpanNameUpdate() diff --git a/tests/OpenTelemetry/Integration/InteroperabilityTest.php b/tests/OpenTelemetry/Integration/InteroperabilityTest.php index 0a7eacc767..6de97dbdc0 100644 --- a/tests/OpenTelemetry/Integration/InteroperabilityTest.php +++ b/tests/OpenTelemetry/Integration/InteroperabilityTest.php @@ -3,6 +3,8 @@ namespace DDTrace\Tests\OpenTelemetry\Integration; use DDTrace\SpanLink; +use DDTrace\SpanEvent; +use DDTrace\ExceptionSpanEvent; use DDTrace\Tag; use DDTrace\Tests\Common\BaseTestCase; use DDTrace\Tests\Common\SpanAssertion; @@ -1175,4 +1177,149 @@ public function testSpanLinksInteroperabilityAddDuplicates() $this->assertNotSame($otelSpanLinks[1], $otelSpanLinks[3]); }); } + + public function testSpanEventsInteroperabilityFromDatadogSpan() + { + $traces = $this->isolateTracer(function () { + $span = start_span(); + $span->name = "dd.span"; + + $spanEvent = new SpanEvent( + "event-name", + [ + 'arg1' => 'value1', + 'int_array' => [3, 4], + 'string_array' => ["5", "6"] + ], + 1720037568765201300 + ); + $span->events[] = $spanEvent; + + /** @var en $OTelSpan */ + $otelSpan = Span::getCurrent(); + $otelSpanEvent = $otelSpan->toSpanData()->getEvents()[0]; + + $this->assertSame('event-name', $otelSpanEvent->getName()); + $this->assertSame([ + 'arg1' => 'value1', + 'int_array' => [3, 4], + 'string_array' => ["5", "6"] + ], $otelSpanEvent->getAttributes()->toArray()); + $this->assertSame(1720037568765201300, (int)$otelSpanEvent->getEpochNanos()); + + close_span(); + }); + + $this->assertCount(1, $traces[0]); + $this->assertSame("[{\"name\":\"event-name\",\"time_unix_nano\":1720037568765201300,\"attributes\":{\"arg1\":\"value1\",\"int_array\":[3,4],\"string_array\":[\"5\",\"6\"]}}]", $traces[0][0]['meta']['events']); + } + + public function testSpanEventsInteroperabilityFromOpenTelemetrySpan() + { + $traces = $this->isolateTracer(function () { + $otelSpan = self::getTracer()->spanBuilder("otel.span") + ->startSpan(); + $otelSpan->addEvent( + "event-name", + [ + 'arg1' => 'value1', + 'int_array' => [3, 4], + 'string_array' => ["5", "6"] + ], + 1720037568765201300 + ); + + $activeSpan = active_span(); + $spanEvent = $activeSpan->events[0]; + $this->assertSame("event-name", $spanEvent->name); + $this->assertSame([ + 'arg1' => 'value1', + 'int_array' => [3, 4], + 'string_array' => ["5", "6"] + ], $spanEvent->attributes); + $this->assertSame(1720037568765201300, (int)$spanEvent->timestamp); + + $otelSpan->end(); + }); + + $this->assertCount(1, $traces[0]); + $this->assertSame("[{\"name\":\"event-name\",\"time_unix_nano\":1720037568765201300,\"attributes\":{\"arg1\":\"value1\",\"int_array\":[3,4],\"string_array\":[\"5\",\"6\"]}}]", $traces[0][0]['meta']['events']); + } + + public function testOtelRecordExceptionAttributesSerialization() + { + $lastException = new \Exception("woof3"); + + $traces = $this->isolateTracer(function () use ($lastException) { + $otelSpan = self::getTracer()->spanBuilder("operation") + ->recordException(new \Exception("woof1"), [ + "string_val" => "value", + "exception.stacktrace" => "stacktrace1" + ]) + ->startSpan(); + + $otelSpan->addEvent("non_exception_event", ["exception.stacktrace" => "non-error"]); + $otelSpan->recordException($lastException, ["exception.message" => "message override"]); + + $otelSpan->end(); + }); + + $events = json_decode($traces[0][0]['meta']['events'], true); + $this->assertCount(3, $events); + + $event1 = $events[0]; + $this->assertSame('value', $event1['attributes']['string_val']); + $this->assertSame('woof1', $event1['attributes']['exception.message']); + $this->assertSame('stacktrace1', $event1['attributes']['exception.stacktrace']); + + $event2 = $events[1]; + $this->assertSame('non-error', $event2['attributes']['exception.stacktrace']); + + $event3 = $events[2]; + $this->assertSame('message override', $event3['attributes']['exception.message']); + + $this->assertSame(\DDTrace\get_sanitized_exception_trace($lastException), $traces[0][0]['meta']['error.stack']); + + $this->assertArrayNotHasKey('error.message', $traces[0][0]['meta']); + $this->assertArrayNotHasKey('error.type', $traces[0][0]['meta']); + $this->assertArrayNotHasKey('error', $traces[0][0]); + } + + public function testExceptionSpanEvents() + { + $traces = $this->isolateTracer(function () { + $span = start_span(); + $span->name = "dd.span"; + + $spanEvent = new ExceptionSpanEvent( + new \Exception("Test exception message"), + [ + 'arg1' => 'value1', + 'exception.stacktrace' => 'Stacktrace Override' + ] + ); + + $span->events[] = $spanEvent; + + /** @var Span $otelSpan */ + $otelSpan = Span::getCurrent(); + $otelSpanEvent = $otelSpan->toSpanData()->getEvents()[0]; + + $this->assertSame('exception', $otelSpanEvent->getName()); + $this->assertSame([ + 'exception.message' => 'Test exception message', + 'exception.type' => 'Exception', + 'exception.stacktrace' => 'Stacktrace Override', + 'arg1' => 'value1' + ], $otelSpanEvent->getAttributes()->toArray()); + + close_span(); + }); + $event = json_decode($traces[0][0]['meta']['events'], true)[0]; + + $this->assertSame('Test exception message', $event['attributes']['exception.message']); + $this->assertSame('Exception', $event['attributes']['exception.type']); + $this->assertSame('Stacktrace Override', $event['attributes']['exception.stacktrace']); + $this->assertSame('value1', $event['attributes']['arg1']); + } } diff --git a/tests/ext/active_span.phpt b/tests/ext/active_span.phpt index 72ac13c2f7..b80ca376b6 100644 --- a/tests/ext/active_span.phpt +++ b/tests/ext/active_span.phpt @@ -28,7 +28,7 @@ var_dump(DDTrace\active_span() == DDTrace\active_span()); Hello, Datadog. greet tracer. bool(true) -object(DDTrace\RootSpanData)#%d (20) { +object(DDTrace\RootSpanData)#%d (21) { ["name"]=> string(15) "active_span.php" ["resource"]=> @@ -61,6 +61,9 @@ object(DDTrace\RootSpanData)#%d (20) { ["links"]=> array(0) { } + ["events"]=> + array(0) { + } ["peerServiceSources"]=> array(0) { } diff --git a/tests/ext/request-replayer/dd_trace_exception_span_event.phpt b/tests/ext/request-replayer/dd_trace_exception_span_event.phpt new file mode 100644 index 0000000000..645dfef29e --- /dev/null +++ b/tests/ext/request-replayer/dd_trace_exception_span_event.phpt @@ -0,0 +1,54 @@ +--TEST-- +DDTrace\ExceptionSpanEvent serialization with overridden attributes +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_AGENT_FLUSH_INTERVAL=333 +DD_TRACE_AUTO_FLUSH_ENABLED=1 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 +--FILE-- +name = 'ExceptionClass.exceptionMethod'; + $exception = new \Exception("initial exception"); + $spanEvent = new ExceptionSpanEvent($exception, [ + "exception.message" => "override message", + "custom.attribute" => "custom value" + ]); + $span->events[] = $spanEvent; +}); + +$rr = new RequestReplayer(); + +try { + $exceptionClass = new ExceptionClass(); + $exceptionClass->exceptionMethod(); +} catch (\Exception $e) { + echo 'Caught exception: ' . $e->getMessage() . PHP_EOL; +} + +$replay = $rr->waitForDataAndReplay(); +$root = json_decode($replay["body"], true); +$spans = $root["chunks"][0]["spans"] ?? $root[0]; +$span = $spans[0]; + +var_dump($span['meta']['events']); +?> +--EXPECTF-- +Caught exception: Exception in method +string(%d) "[{"name":"exception","time_unix_nano":%d,"attributes":{"exception.message":"override message","exception.type":"Exception","exception.stacktrace":"#0 %s(%d): ExceptionClass->{closure}()\n#1 %s(%d): ExceptionClass->exceptionMethod()\n#2 {main}","custom.attribute":"custom value"}}]" diff --git a/tests/ext/request-replayer/dd_trace_span_event.phpt b/tests/ext/request-replayer/dd_trace_span_event.phpt new file mode 100644 index 0000000000..989260673c --- /dev/null +++ b/tests/ext/request-replayer/dd_trace_span_event.phpt @@ -0,0 +1,47 @@ +--TEST-- +DDTrace\SpanEvent serialization with attributes +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_AGENT_FLUSH_INTERVAL=333 +DD_TRACE_AUTO_FLUSH_ENABLED=1 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 +--FILE-- +name = 'TestClass.testMethod'; + $spanEvent = new SpanEvent("event-name", [ + 'arg1' => 'value1', + 'int_array' => [3, 4], + 'string_array' => ["5", "6"] + ], 1720037568765201300); + $span->events[] = $spanEvent; +}); + +$rr = new RequestReplayer(); +$test = new TestClass(); +$test->testMethod(); +$replay = $rr->waitForDataAndReplay(); +$root = json_decode($replay["body"], true); +$spans = $root["chunks"][0]["spans"] ?? $root[0]; +$span = $spans[0]; +var_dump($span['meta']['events']); +?> +--EXPECT-- +In testMethod +string(134) "[{"name":"event-name","time_unix_nano":1720037568765201300,"attributes":{"arg1":"value1","int_array":[3,4],"string_array":["5","6"]}}]" diff --git a/tests/ext/sandbox/span_clone.phpt b/tests/ext/sandbox/span_clone.phpt index ac5b3d2c85..34fe3f3604 100644 --- a/tests/ext/sandbox/span_clone.phpt +++ b/tests/ext/sandbox/span_clone.phpt @@ -24,7 +24,7 @@ var_dump(dd_trace_serialize_closed_spans()); ?> --EXPECTF-- -object(DDTrace\RootSpanData)#%d (20) { +object(DDTrace\RootSpanData)#%d (21) { ["name"]=> string(3) "foo" ["resource"]=> @@ -57,6 +57,9 @@ object(DDTrace\RootSpanData)#%d (20) { ["links"]=> array(0) { } + ["events"]=> + array(0) { + } ["peerServiceSources"]=> array(0) { } @@ -87,7 +90,7 @@ object(DDTrace\RootSpanData)#%d (20) { ["gitMetadata"]=> NULL } -object(DDTrace\RootSpanData)#%d (20) { +object(DDTrace\RootSpanData)#%d (21) { ["name"]=> string(5) "dummy" ["resource"]=> @@ -120,6 +123,9 @@ object(DDTrace\RootSpanData)#%d (20) { ["links"]=> array(0) { } + ["events"]=> + array(0) { + } ["peerServiceSources"]=> array(0) { } @@ -135,7 +141,7 @@ object(DDTrace\RootSpanData)#%d (20) { NULL } ["active"]=> - object(DDTrace\RootSpanData)#%d (20) { + object(DDTrace\RootSpanData)#%d (21) { ["name"]=> string(3) "foo" ["resource"]=> @@ -168,6 +174,9 @@ object(DDTrace\RootSpanData)#%d (20) { ["links"]=> array(0) { } + ["events"]=> + array(0) { + } ["peerServiceSources"]=> array(0) { } From b21e6a000be18613a9e5f24e395242f423610923 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 21 Aug 2024 13:34:02 +0200 Subject: [PATCH 033/103] Fix #2774: shared hooks removal caused xdebug end handler to be skipped (#2780) * Handle removal of recursive hooks Signed-off-by: Bob Weinand * Add test Signed-off-by: Bob Weinand --------- Signed-off-by: Bob Weinand --- tests/xdebug/3.0.0/line-breakpoint.inc | 17 + tests/xdebug/3.0.0/line-breakpoint.phpt | 55 +++ .../Integrations/Logs/LogsIntegration.php | 10 + tests/xdebug/dbgpclient.php | 313 ++++++++++++++++++ zend_abstract_interface/hook/hook.c | 8 + 5 files changed, 403 insertions(+) create mode 100644 tests/xdebug/3.0.0/line-breakpoint.inc create mode 100644 tests/xdebug/3.0.0/line-breakpoint.phpt create mode 100644 tests/xdebug/DDTrace/Integrations/Logs/LogsIntegration.php create mode 100644 tests/xdebug/dbgpclient.php diff --git a/tests/xdebug/3.0.0/line-breakpoint.inc b/tests/xdebug/3.0.0/line-breakpoint.inc new file mode 100644 index 0000000000..505c5f3f11 --- /dev/null +++ b/tests/xdebug/3.0.0/line-breakpoint.inc @@ -0,0 +1,17 @@ +log(); +echo "post-hook\n"; diff --git a/tests/xdebug/3.0.0/line-breakpoint.phpt b/tests/xdebug/3.0.0/line-breakpoint.phpt new file mode 100644 index 0000000000..e1d61b2872 --- /dev/null +++ b/tests/xdebug/3.0.0/line-breakpoint.phpt @@ -0,0 +1,55 @@ +--TEST-- +Line breakpoint on interface-hook +--FILE-- + "xdebug-" . phpversion('xdebug'), "datadog.trace.sources_path" => __DIR__ . "/..", "datadog.logs_injection" => 0], ["show-stdout" => true]); + +?> +--EXPECTF-- + + + +-> feature_set -i 1 -n resolved_breakpoint -v 1 + + + +-> step_into -i 2 + + + +-> breakpoint_set -i 3 -t line -f %s/line-breakpoint.inc -n 15 + + + +-> breakpoint_set -i 4 -t line -f %s/line-breakpoint.inc -n 17 + + + +-> run -i 5 + +%S + +-> run -i 6 + +%S + +-> detach -i 7 + + + +pre-hook +hey +post-hook diff --git a/tests/xdebug/DDTrace/Integrations/Logs/LogsIntegration.php b/tests/xdebug/DDTrace/Integrations/Logs/LogsIntegration.php new file mode 100644 index 0000000000..25dd6fef32 --- /dev/null +++ b/tests/xdebug/DDTrace/Integrations/Logs/LogsIntegration.php @@ -0,0 +1,10 @@ +port; + } + + public function setPort($port) + { + $this->port = $port; + } + + protected function getIPAddress() + { + return "127.0.0.1"; + } + + protected function getAddress() + { + return "tcp://" . $this->getIPAddress() . ":" . $this->getPort(); + } + + public function __construct() + { + $this->tmpDir = getTmpDir(); + } + + private function open( &$errno, &$errstr ) + { + $socket = @stream_socket_server( $this->getAddress(), $errno, $errstr ); + if ( $socket ) + { + $name = stream_socket_get_name( $socket, false ); + $name = explode( ":", $name ); + $this->port = array_pop( $name ); + } + return $socket; + } + + private function launchPhp( &$pipes, $filename, array $ini_options = [], array $extra_options = [] ) + { + @unlink( $this->tmpDir . 'error-output.txt' ); + @unlink( $this->tmpDir . 'remote_log.txt' ); + + $descriptorspec = array( + 0 => array( 'pipe', 'r' ), + 1 => array( 'pipe', 'w' ), + 2 => array( 'file', $this->tmpDir . 'error-output.txt', 'a' ) + ); + + $default_options = array( + "xdebug.mode" => "debug", + "xdebug.start_with_request" => "'yes'", + "xdebug.client_host" => $this->getIPAddress(), + "xdebug.client_port" => $this->getPort(), + 'xdebug.control_socket' => "'no'", + ); + + $env_vars = array_key_exists( 'env', $extra_options ) ? $extra_options['env'] : []; + $env_vars += $_ENV; + + $options = (getenv('TEST_PHP_ARGS') ?: ''); + $ini_options = array_merge( $default_options, $ini_options ); + foreach ( $ini_options as $key => $value ) + { + $options .= " -d{$key}=$value"; + } + + if ( array_key_exists( 'auto_prepend', $extra_options ) ) + { + $prependFile = "{$this->tmpDir}auto-prepend.inc"; + file_put_contents( $prependFile, $extra_options['auto_prepend'] ); + $options .= " -dauto_prepend_file={$prependFile}"; + } + + $php = getenv( 'TEST_PHP_EXECUTABLE' ); + $cmd = "{$php} $options {$filename} >{$this->tmpDir}php-stdout.txt 2>{$this->tmpDir}php-stderr.txt"; + if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { + $cmd = "exec {$cmd}"; + } + $cwd = dirname( __FILE__ ); + + $process = proc_open( $cmd, $descriptorspec, $pipes, $cwd, $env_vars ); + return $process; + } + + function fixFilePath( $m ) + { + preg_match( '@.*/(.*\.inc)@', $m[2], $fm ); + if ( !isset( $fm[1] ) ) + { + $fm[1] = ''; + } + return " {$m[1]}=\"file://{$fm[1]}\""; + } + + function doRead( $conn, ?string $transaction_id = null ) + { + stream_set_timeout( $conn, 3 ); + do { + $trans_id = null; + $length = 0; + while ( "\0" !== ( $char = fgetc($conn) ) ) { + if ( $char === false ) { + echo "read a false for $transaction_id" . PHP_EOL; + return null; + } + if ( !is_numeric($char) ) { + echo "read a non-number for $transaction_id" . PHP_EOL; + return null; + } + $length = $length * 10 + (int)$char; + } + + $read = ''; + while ( 0 < $length ) { + $data = fread( $conn, $length ); + if ( $data === false ) + { + echo "read a false for $transaction_id" . PHP_EOL; + return null; + } + $length -= strlen( $data ); + $read .= $data; + } + $char = fgetc( $conn ); + if ( $char !== "\0" ) + { + echo 'must end with \0' . PHP_EOL; + } + + // sanitize + $read = preg_replace( '@(?\d+)"@', $read, $matches ) ) + { + $trans_id = $matches['transaction_id'] ?? null; + } + } while ( $trans_id !== $transaction_id ); + } + + function start( $filename, array $ini_options = [], array $options = []) + { + $filename = realpath( $filename ); + + $this->socket = $this->open( $errno, $errstr ); + if ( $this->socket === false ) + { + echo "Could not create socket server - already in use?\n"; + echo "Error: {$errstr}, errno: {$errno}\n"; + echo "Address: {$this->getAddress()}\n"; + return false; + } + $this->php = $this->launchPhp( $this->ppipes, $filename, $ini_options, $options ); + $conn = @stream_socket_accept( $this->socket, isset( $options['timeout'] ) ? $options['timeout'] : 5 ); + + if ( $conn === false ) + { + echo @file_get_contents( $this->tmpDir . 'php-stdout.txt' ), "\n"; + echo @file_get_contents( $this->tmpDir . 'php-stderr.txt' ), "\n"; + echo @file_get_contents( $this->tmpDir . 'error-output.txt' ), "\n"; + echo @file_get_contents( $this->tmpDir . 'remote_log.txt' ), "\n"; + proc_close( $this->php ); + return false; + } + return $conn; + } + + function stop( $conn, array $options = [] ) + { + fclose( $conn ); + fclose( $this->ppipes[0] ); + fclose( $this->ppipes[1] ); + fclose( $this->socket ); + proc_close( $this->php ); + + if ( array_key_exists( 'show-stdout', $options ) && $options['show-stdout'] ) + { + echo @file_get_contents( $this->tmpDir . 'php-stdout.txt' ), "\n"; + } + // echo @file_get_contents( $this->tmpDir . 'php-stderr.txt' ), "\n"; + // echo @file_get_contents( $this->tmpDir . 'error-output.txt' ), "\n"; + } + + function sendCommand( $conn, $command, $transaction_id ) + { + // inject identifier + $parts = explode( ' ', $command, 2 ); + if ( count($parts) == 1 ) + { + $command = $parts[0] . " -i $transaction_id"; + } + else + { + $command = $parts[0] . " -i $transaction_id " . $parts[1]; + } + + /* Replace PID macro */ + $command = str_replace( "{{PID}}", $this->pid & 0x1ffff, $command ); + + $sanitised = $command; + $sanitised = preg_replace( '@\sfile://.*[/\\\\](.*\.inc)\s@', ' file://\\1 ', $sanitised ); + + echo "-> ", $sanitised, "\n"; + fwrite( $conn, $command . "\0" ); + } + + function runTest( $filename, array $commands, array $ini_options = [], array $options = [] ) + { + $conn = $this->start( $filename, $ini_options, $options ); + if ( $conn === false ) + { + return; + } + $i = 1; + $procInfo = proc_get_status( $this->php ); + $this->pid = $procInfo['pid']; + + // read header + $this->doRead( $conn ); + foreach ( $commands as $command ) + { + $this->sendCommand( $conn, $command, $i ); + $this->doRead( $conn, (string)$i ); + + $i++; + } + $this->stop( $conn, $options ); + } +} + +class DebugClientIPv6 extends DebugClient +{ + protected function getIPAddress() + { + return "::1"; + } + + protected function getAddress() + { + return "tcp://[" . $this->getIPAddress() . "]:" . $this->getPort(); + } + + public static function isSupported( &$errno, &$errstr ) + { + $socket = @stream_socket_server( "tcp://[::1]:0", $errno, $errstr ); + + if ( $socket === false ) + { + return false; + } + + fclose( $socket ); + return true; + } +} + +function dbgpRunFile( $data, $commands, array $ini_options = [], array $options = [] ) +{ + if ( isset( $options['ipv'] ) && $options['ipv'] == 6 ) + { + $t = new DebugClientIPv6(); + } + else + { + $t = new DebugClient(); + } + + $t->runTest( $data, $commands, $ini_options, $options ); +} +?> \ No newline at end of file diff --git a/zend_abstract_interface/hook/hook.c b/zend_abstract_interface/hook/hook.c index a2fe7d770c..d3e4b4ad4e 100644 --- a/zend_abstract_interface/hook/hook.c +++ b/zend_abstract_interface/hook/hook.c @@ -1089,7 +1089,15 @@ void zai_hook_finish(zend_execute_data *ex, zval *rv, zai_hook_memory_t *memory) zend_class_entry *ce = NULL; zend_function *origin_func = zai_hook_lookup_function((zai_str)ZAI_STR_FROM_ZSTR(hook->scope), (zai_str)ZAI_STR_FROM_ZSTR(hook->function), &ce); zai_hook_table_find(&zai_hook_resolved, zai_hook_install_address(origin_func), (void**)&hooks); +#if PHP_VERSION_ID >= 80000 + zai_hook_last_observer = NULL; zai_hook_remove_abstract_recursive(hooks, ce, hook->function, (zend_ulong)-hook->id); + if (zai_hook_last_observer) { + zai_hook_last_observer(ex, rv); + } +#else + zai_hook_remove_abstract_recursive(hooks, ce, hook->function, (zend_ulong)-hook->id); +#endif address = zai_hook_install_address(hooks->resolved); } zend_hash_index_del(&hooks->hooks, (zend_ulong) -hook->id); From 91e6bce49e0cfa5c693b9cf1f36262ec01f1de6c Mon Sep 17 00:00:00 2001 From: Alejandro Estringana Ruiz Date: Wed, 21 Aug 2024 15:45:55 +0200 Subject: [PATCH 034/103] Report backtraces (#2715) --- appsec/src/extension/backtrace.c | 341 +++++++++++++ appsec/src/extension/backtrace.h | 21 + appsec/src/extension/commands_helpers.c | 20 + appsec/src/extension/configuration.h | 7 +- appsec/src/extension/ddappsec.c | 4 + appsec/src/extension/ddtrace.c | 26 + appsec/src/extension/ddtrace.h | 7 +- appsec/src/extension/msgpack_helpers.c | 129 +++++ appsec/src/extension/msgpack_helpers.h | 2 + appsec/src/extension/php_compat.h | 24 + appsec/src/extension/php_helpers.c | 2 +- appsec/src/extension/php_helpers.h | 11 +- .../extension/generate_backtrace_01.phpt | 54 ++ .../extension/generate_backtrace_02.phpt | 45 ++ .../extension/generate_backtrace_03.phpt | 24 + .../extension/generate_backtrace_04.phpt | 474 ++++++++++++++++++ .../extension/generate_backtrace_05.phpt | 26 + .../extension/generate_backtrace_06.phpt | 91 ++++ .../extension/generate_backtrace_07.phpt | 81 +++ .../tests/extension/report_backtrace_01.phpt | 70 +++ .../tests/extension/report_backtrace_02.phpt | 106 ++++ .../tests/extension/report_backtrace_03.phpt | 139 +++++ .../tests/extension/report_backtrace_04.phpt | 139 +++++ .../tests/extension/report_backtrace_05.phpt | 83 +++ .../tests/extension/report_backtrace_06.phpt | 33 ++ .../com/datadog/appsec/php/model/Span.groovy | 1 + .../appsec/php/integration/CommonTests.groovy | 51 +- .../integration/src/test/waf/recommended.json | 26 + .../test/www/base/public/generate_stack.php | 17 + 29 files changed, 2038 insertions(+), 16 deletions(-) create mode 100644 appsec/src/extension/backtrace.c create mode 100644 appsec/src/extension/backtrace.h create mode 100644 appsec/tests/extension/generate_backtrace_01.phpt create mode 100644 appsec/tests/extension/generate_backtrace_02.phpt create mode 100644 appsec/tests/extension/generate_backtrace_03.phpt create mode 100644 appsec/tests/extension/generate_backtrace_04.phpt create mode 100644 appsec/tests/extension/generate_backtrace_05.phpt create mode 100644 appsec/tests/extension/generate_backtrace_06.phpt create mode 100644 appsec/tests/extension/generate_backtrace_07.phpt create mode 100644 appsec/tests/extension/report_backtrace_01.phpt create mode 100644 appsec/tests/extension/report_backtrace_02.phpt create mode 100644 appsec/tests/extension/report_backtrace_03.phpt create mode 100644 appsec/tests/extension/report_backtrace_04.phpt create mode 100644 appsec/tests/extension/report_backtrace_05.phpt create mode 100644 appsec/tests/extension/report_backtrace_06.phpt create mode 100644 appsec/tests/integration/src/test/www/base/public/generate_stack.php diff --git a/appsec/src/extension/backtrace.c b/appsec/src/extension/backtrace.c new file mode 100644 index 0000000000..0009583c66 --- /dev/null +++ b/appsec/src/extension/backtrace.c @@ -0,0 +1,341 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog +// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. +#include "backtrace.h" +#include "configuration.h" +#include "ddtrace.h" +#include "logging.h" +#include "php_compat.h" +#include "php_objects.h" +#include "string_helpers.h" + +static const int NO_LIMIT = 0; +static const double STACK_DEFAULT_TOP_RATE = 0.25; +static const char QUALIFIED_NAME_SEPARATOR[] = "::"; + +static zend_string *_frames_key; +static zend_string *_language_key; +static zend_string *_php_value; +static zend_string *_exploit_key; +static zend_string *_dd_stack_key; +static zend_string *_frame_line; +static zend_string *_frame_function; +static zend_string *_frame_file; +static zend_string *_id_key; +static zend_string *_line_field; +static zend_string *_function_field; +static zend_string *_file_field; +static zend_string *_class_field; + +static bool +php_backtrace_frame_to_datadog_backtrace_frame( // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + zval *nonnull php_backtrace_frame, zval *nonnull datadog_backtrace_frame, + zend_ulong index) +{ + if (Z_TYPE_P(php_backtrace_frame) != IS_ARRAY) { + return false; + } + HashTable *frame = Z_ARRVAL_P(php_backtrace_frame); + zval *line = zend_hash_find(frame, _line_field); + zval *function = zend_hash_find(frame, _function_field); + zval *file = zend_hash_find(frame, _file_field); + zval *class = zend_hash_find(frame, _class_field); + zval id; + ZVAL_LONG(&id, index); +#ifdef TESTING + if (file) { + // In order to be able to test full path encoded everywhere lets set + // only the file name without path + char *file_name = memrchr(Z_STRVAL_P(file), '/', Z_STRLEN_P(file)); + if (file_name) { + zend_string *new_file = zend_string_init(file_name + 1, + Z_STRLEN_P(file) - (file_name + 1 - Z_STRVAL_P(file)), 0); + zval_ptr_dtor(file); + ZVAL_NEW_STR(file, new_file); + } + } +#endif + + if (!function) { + return false; + } + + // Remove tracer integration php code frames + if (strncmp(Z_STRVAL_P(function), "DDTrace", sizeof("DDTrace") - 1) == 0) { + return false; + } + + array_init(datadog_backtrace_frame); + HashTable *datadog_backtrace_frame_ht = Z_ARRVAL_P(datadog_backtrace_frame); + if (line) { + zend_hash_add(datadog_backtrace_frame_ht, _frame_line, line); + } + + zend_ulong qualified_name_size = Z_STRLEN_P(function); + qualified_name_size += + class ? Z_STRLEN_P(class) + sizeof(QUALIFIED_NAME_SEPARATOR) - 1 : 0; + zend_string *qualified_name_zstr = + zend_string_alloc(qualified_name_size, 0); + char *qualified_name = ZSTR_VAL(qualified_name_zstr); + int position = 0; + + if (class) { + memcpy(qualified_name, Z_STRVAL_P(class), Z_STRLEN_P(class)); + position = Z_STRLEN_P(class); + memcpy(&qualified_name[position], QUALIFIED_NAME_SEPARATOR, + sizeof(QUALIFIED_NAME_SEPARATOR) - 1); + position += 2; + } + + memcpy( + &qualified_name[position], Z_STRVAL_P(function), Z_STRLEN_P(function)); + + qualified_name[qualified_name_size] = '\0'; + + zval zv_qualified_name; + ZVAL_STR(&zv_qualified_name, qualified_name_zstr); + zend_hash_add( + datadog_backtrace_frame_ht, _frame_function, &zv_qualified_name); + + if (file) { + zend_hash_add(datadog_backtrace_frame_ht, _frame_file, file); + Z_TRY_ADDREF_P(file); + } + zend_hash_add(datadog_backtrace_frame_ht, _id_key, &id); + + return true; +} + +static void php_backtrace_to_datadog_backtrace( + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + zval *nonnull php_backtrace, zval *nonnull datadog_backtrace) +{ + if (Z_TYPE_P(php_backtrace) != IS_ARRAY) { + return; + } + + HashTable *php_backtrace_ht = Z_ARRVAL_P(php_backtrace); + uint32_t frames_on_stack = zend_array_count(php_backtrace_ht); + + uint32_t top = frames_on_stack; + uint32_t bottom = 0; + if (get_global_DD_APPSEC_MAX_STACK_TRACE_DEPTH() != 0 && + frames_on_stack > get_global_DD_APPSEC_MAX_STACK_TRACE_DEPTH()) { + top = (uint32_t)round( + (double)get_global_DD_APPSEC_MAX_STACK_TRACE_DEPTH() * + STACK_DEFAULT_TOP_RATE); + bottom = get_global_DD_APPSEC_MAX_STACK_TRACE_DEPTH() - top; + } + + array_init(datadog_backtrace); + + HashTable *datadog_backtrace_ht = Z_ARRVAL_P(datadog_backtrace); + + zval *php_frame; + zend_ulong index; + if (top > 0) { + ZEND_HASH_FOREACH_NUM_KEY_VAL(php_backtrace_ht, index, php_frame) + { + zval new_frame; + + if (php_backtrace_frame_to_datadog_backtrace_frame( + php_frame, &new_frame, index)) { + zend_hash_next_index_insert_new( + datadog_backtrace_ht, &new_frame); + } + if (--top == 0) { + break; + } + } + ZEND_HASH_FOREACH_END(); + } + + if (bottom > 0) { + unsigned int position = frames_on_stack - bottom; + DD_FOREACH_FROM(php_backtrace_ht, 0, position, index) + { + php_frame = _z; + zval new_frame; + + if (!php_backtrace_frame_to_datadog_backtrace_frame( + php_frame, &new_frame, index)) { + continue; + } + + zend_hash_next_index_insert_new(datadog_backtrace_ht, &new_frame); + } + ZEND_HASH_FOREACH_END(); + } +} + +void dd_generate_backtrace(zend_string *nullable id, zval *nonnull dd_backtrace) +{ + array_init(dd_backtrace); + + if (!id) { + return; + } + + zval language; + ZVAL_STR_COPY(&language, _php_value); + zval id_zv; + ZVAL_STR_COPY(&id_zv, id); + zend_hash_add(Z_ARRVAL_P(dd_backtrace), _language_key, &language); + zend_hash_add(Z_ARRVAL_P(dd_backtrace), _id_key, &id_zv); + + zval frames; + zval php_backtrace; + zend_fetch_debug_backtrace( + &php_backtrace, 1, DEBUG_BACKTRACE_IGNORE_ARGS, NO_LIMIT); + php_backtrace_to_datadog_backtrace(&php_backtrace, &frames); + zend_hash_add(Z_ARRVAL_P(dd_backtrace), _frames_key, &frames); + + zval_dtor(&php_backtrace); +} + +static PHP_FUNCTION(datadog_appsec_testing_generate_backtrace) +{ + zend_string *id = NULL; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &id) != SUCCESS) { + RETURN_FALSE; + } + + dd_generate_backtrace(id, return_value); +} + +bool dd_report_exploit_backtrace(zend_string *nullable id) +{ + if (!get_global_DD_APPSEC_STACK_TRACE_ENABLED()) { + return false; + } + + if (!id) { + mlog(dd_log_warning, + "Backtrace can not be generated because id is missing"); + } + + zend_object *span = dd_trace_get_active_root_span(); + if (!span) { + if (!get_global_DD_APPSEC_TESTING()) { + mlog(dd_log_warning, "Failed to retrieve root span"); + } + return false; + } + + zval *meta_struct = dd_trace_span_get_meta_struct(span); + if (!meta_struct) { + if (!get_global_DD_APPSEC_TESTING()) { + mlog(dd_log_warning, "Failed to retrieve root span meta_struct"); + } + return false; + } + + if (Z_TYPE_P(meta_struct) == IS_NULL) { + array_init(meta_struct); + } else if (Z_TYPE_P(meta_struct) != IS_ARRAY) { + return false; + } + + zval *dd_stack = zend_hash_find(Z_ARR_P(meta_struct), _dd_stack_key); + zval *exploit = NULL; + if (!dd_stack || Z_TYPE_P(dd_stack) == IS_NULL) { + zval new_dd_stack; + zval new_exploit; + dd_stack = zend_hash_add_new( + Z_ARR_P(meta_struct), _dd_stack_key, &new_dd_stack); + array_init(dd_stack); + exploit = + zend_hash_add_new(Z_ARR_P(dd_stack), _exploit_key, &new_exploit); + array_init(exploit); + } else if (Z_TYPE_P(dd_stack) != IS_ARRAY) { + return false; + } else { + exploit = zend_hash_find(Z_ARR_P(dd_stack), _exploit_key); + } + + if (Z_TYPE_P(exploit) != IS_ARRAY) { + return false; + } + + unsigned int limit = get_global_DD_APPSEC_MAX_STACK_TRACES(); + if (limit != 0 && zend_array_count(Z_ARR_P(exploit)) == limit) { + mlog(dd_log_debug, + "Stacktrace not generated due to limit " + "DD_APPSEC_MAX_STACK_TRACES(%u) has been reached", + limit); + return false; + } + + zval backtrace; + dd_generate_backtrace(id, &backtrace); + + if (zend_hash_next_index_insert_new(Z_ARRVAL_P(exploit), &backtrace) == + NULL) { + return false; + } + + return true; +} + +static PHP_FUNCTION(datadog_appsec_testing_report_exploit_backtrace) +{ + zend_string *id = NULL; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &id) != SUCCESS) { + RETURN_FALSE; + } + + if (dd_report_exploit_backtrace(id)) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( + void_ret_bool_arginfo, 0, 1, _IS_BOOL, 0) +ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( + void_ret_array_arginfo, 0, 1, IS_ARRAY, 0) +ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0) +ZEND_END_ARG_INFO() + +// clang-format off +static const zend_function_entry testing_functions[] = { + ZEND_RAW_FENTRY(DD_TESTING_NS "generate_backtrace", PHP_FN(datadog_appsec_testing_generate_backtrace), void_ret_array_arginfo,0) + ZEND_RAW_FENTRY(DD_TESTING_NS "report_exploit_backtrace", PHP_FN(datadog_appsec_testing_report_exploit_backtrace), void_ret_bool_arginfo, 0) + PHP_FE_END +}; +// clang-format on + +static void _register_testing_objects() +{ + if (!get_global_DD_APPSEC_TESTING()) { + return; + } + + dd_phpobj_reg_funcs(testing_functions); +} + +void dd_backtrace_startup() +{ + _frames_key = zend_string_init_interned(LSTRARG("frames"), 1); + _language_key = zend_string_init_interned(LSTRARG("language"), 1); + _php_value = zend_string_init_interned(LSTRARG("php"), 1); + _exploit_key = zend_string_init_interned(LSTRARG("exploit"), 1); + _dd_stack_key = zend_string_init_interned(LSTRARG("_dd.stack"), 1); + _frame_line = zend_string_init_interned(LSTRARG("line"), 1); + _frame_function = zend_string_init_interned(LSTRARG("function"), 1); + _frame_file = zend_string_init_interned(LSTRARG("file"), 1); + _id_key = zend_string_init_interned(LSTRARG("id"), 1); + _line_field = zend_string_init_interned(LSTRARG("line"), 1); + _file_field = zend_string_init_interned(LSTRARG("file"), 1); + _function_field = zend_string_init_interned(LSTRARG("function"), 1); + _class_field = zend_string_init_interned(LSTRARG("class"), 1); +#ifdef TESTING + _register_testing_objects(); +#endif +} diff --git a/appsec/src/extension/backtrace.h b/appsec/src/extension/backtrace.h new file mode 100644 index 0000000000..c16ab1eef5 --- /dev/null +++ b/appsec/src/extension/backtrace.h @@ -0,0 +1,21 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog +// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. +#ifndef BACKTRACE_H +#define BACKTRACE_H + +#include +#include +#include +#include + +#include "attributes.h" + +void dd_backtrace_startup(); +void dd_generate_backtrace( + zend_string *nullable id, zval *nonnull dd_backtrace); +bool dd_report_exploit_backtrace(zend_string *nullable id); + +#endif // BACKTRACE_H diff --git a/appsec/src/extension/commands_helpers.c b/appsec/src/extension/commands_helpers.c index 0db2a7dede..1bedf8323e 100644 --- a/appsec/src/extension/commands_helpers.c +++ b/appsec/src/extension/commands_helpers.c @@ -4,6 +4,7 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "commands_helpers.h" +#include "backtrace.h" #include "commands_ctx.h" #include "configuration.h" #include "ddappsec.h" @@ -419,6 +420,22 @@ static void _command_process_redirect_parameters(mpack_node_t root) dd_set_redirect_code_and_location(status_code, location); } +static void _command_process_stack_trace_parameters(mpack_node_t root) +{ + size_t count = mpack_node_map_count(root); + for (size_t i = 0; i < count; i++) { + mpack_node_t key = mpack_node_map_key_at(root, i); + mpack_node_t value = mpack_node_map_value_at(root, i); + if (dd_mpack_node_lstr_eq(key, "stack_id")) { + zend_string *id = NULL; + size_t id_len = mpack_node_strlen(value); + id = zend_string_init(mpack_node_str(value), id_len, 0); + dd_report_exploit_backtrace(id); + zend_string_release(id); + break; + } + } +} dd_result _command_process_actions(mpack_node_t root, struct req_info *ctx) { @@ -456,6 +473,9 @@ dd_result _command_process_actions(mpack_node_t root, struct req_info *ctx) } else if (dd_mpack_node_lstr_eq(verdict, "record") && res == dd_success) { res = dd_should_record; + } else if (dd_mpack_node_lstr_eq(verdict, "stack_trace")) { + _command_process_stack_trace_parameters( + mpack_node_array_at(action, 1)); } } diff --git a/appsec/src/extension/configuration.h b/appsec/src/extension/configuration.h index 6db9fde266..d4b4838419 100644 --- a/appsec/src/extension/configuration.h +++ b/appsec/src/extension/configuration.h @@ -45,10 +45,13 @@ extern bool runtime_config_first_init; CONFIG(CUSTOM(INT), DD_APPSEC_LOG_LEVEL, "warn", .parser = dd_parse_log_level) \ SYSCFG(STRING, DD_APPSEC_LOG_FILE, "php_error_reporting") \ SYSCFG(BOOL, DD_APPSEC_HELPER_LAUNCH, "true") \ - CONFIG(STRING, DD_APPSEC_HELPER_PATH, DD_BASE("bin/libddappsec-helper.so")) \ + CONFIG(STRING, DD_APPSEC_HELPER_PATH, DD_BASE("bin/libddappsec-helper.so")) \ + SYSCFG(BOOL, DD_APPSEC_STACK_TRACE_ENABLED, "true") \ + SYSCFG(INT, DD_APPSEC_MAX_STACK_TRACE_DEPTH, "32") \ + SYSCFG(INT, DD_APPSEC_MAX_STACK_TRACES, "2") \ CONFIG(STRING, DD_APPSEC_HELPER_RUNTIME_PATH, "/tmp", .ini_change = dd_on_runtime_path_update) \ SYSCFG(STRING, DD_APPSEC_HELPER_LOG_FILE, "/dev/null") \ - SYSCFG(STRING, DD_APPSEC_HELPER_LOG_LEVEL, "info") \ + SYSCFG(STRING, DD_APPSEC_HELPER_LOG_LEVEL, "info") \ CONFIG(CUSTOM(SET), DD_EXTRA_SERVICES, "", .parser = _parse_list) \ CONFIG(STRING, DD_SERVICE, "") \ CONFIG(STRING, DD_ENV, "") \ diff --git a/appsec/src/extension/ddappsec.c b/appsec/src/extension/ddappsec.c index 7fb581ee86..26fbaa6b2b 100644 --- a/appsec/src/extension/ddappsec.c +++ b/appsec/src/extension/ddappsec.c @@ -18,6 +18,7 @@ #include #include +#include "backtrace.h" #include "commands/client_init.h" #include "commands/config_sync.h" #include "commands/request_exec.h" @@ -32,6 +33,7 @@ #include "helper_process.h" #include "ip_extraction.h" #include "logging.h" +#include "msgpack_helpers.h" #include "network.h" #include "php_compat.h" #include "php_helpers.h" @@ -218,6 +220,8 @@ static PHP_MINIT_FUNCTION(ddappsec) dd_tags_startup(); dd_ip_extraction_startup(); dd_entity_body_startup(); + dd_backtrace_startup(); + dd_msgpack_helpers_startup(); return SUCCESS; } diff --git a/appsec/src/extension/ddtrace.c b/appsec/src/extension/ddtrace.c index cbf9d62565..d9dd88c052 100644 --- a/appsec/src/extension/ddtrace.c +++ b/appsec/src/extension/ddtrace.c @@ -26,6 +26,7 @@ static bool _ddtrace_loaded; static zend_string *_ddtrace_root_span_fname; static zend_string *_meta_propname; static zend_string *_metrics_propname; +static zend_string *_meta_struct_propname; static THREAD_LOCAL_ON_ZTS bool _suppress_ddtrace_rshutdown; static uint8_t *_ddtrace_runtime_id = NULL; @@ -106,6 +107,8 @@ void dd_trace_startup() LSTRARG("ddtrace\\root_span"), 1 /* permanent */); _meta_propname = zend_string_init_interned(LSTRARG("meta"), 1); _metrics_propname = zend_string_init_interned(LSTRARG("metrics"), 1); + _meta_struct_propname = + zend_string_init_interned(LSTRARG("meta_struct"), 1); if (get_global_DD_APPSEC_TESTING()) { _register_testing_objects(); @@ -285,6 +288,11 @@ zval *nullable dd_trace_span_get_metrics(zend_object *nonnull zobj) return _get_span_modifiable_array_property(zobj, _metrics_propname); } +zval *nullable dd_trace_span_get_meta_struct(zend_object *nonnull zobj) +{ + return _get_span_modifiable_array_property(zobj, _meta_struct_propname); +} + // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) zend_string *nullable dd_trace_get_formatted_runtime_id(bool persistent) { @@ -412,6 +420,23 @@ static PHP_FUNCTION(datadog_appsec_testing_root_span_get_meta) // NOLINT } } +static PHP_FUNCTION(datadog_appsec_testing_root_span_get_meta_struct) // NOLINT +{ + if (zend_parse_parameters_none() == FAILURE) { + RETURN_FALSE; + } + + __auto_type root_span = dd_trace_get_active_root_span(); + if (!root_span) { + RETURN_NULL(); + } + + zval *meta_struct_zv = dd_trace_span_get_meta_struct(root_span); + if (meta_struct_zv) { + RETURN_ZVAL(meta_struct_zv, 1 /* copy */, 0 /* no destroy original */); + } +} + static PHP_FUNCTION(datadog_appsec_testing_root_span_get_metrics) // NOLINT { if (zend_parse_parameters_none() == FAILURE) { @@ -462,6 +487,7 @@ static const zend_function_entry functions[] = { ZEND_RAW_FENTRY(DD_TESTING_NS "ddtrace_rshutdown", PHP_FN(datadog_appsec_testing_ddtrace_rshutdown), void_ret_bool_arginfo, 0) ZEND_RAW_FENTRY(DD_TESTING_NS "root_span_add_tag", PHP_FN(datadog_appsec_testing_root_span_add_tag), arginfo_root_span_add_tag, 0) ZEND_RAW_FENTRY(DD_TESTING_NS "root_span_get_meta", PHP_FN(datadog_appsec_testing_root_span_get_meta), void_ret_nullable_array, 0) + ZEND_RAW_FENTRY(DD_TESTING_NS "root_span_get_meta_struct", PHP_FN(datadog_appsec_testing_root_span_get_meta_struct), void_ret_nullable_array, 0) ZEND_RAW_FENTRY(DD_TESTING_NS "root_span_get_metrics", PHP_FN(datadog_appsec_testing_root_span_get_metrics), void_ret_nullable_array, 0) ZEND_RAW_FENTRY(DD_TESTING_NS "get_formatted_runtime_id", PHP_FN(datadog_appsec_testing_get_formatted_runtime_id), void_ret_nullable_string, 0) PHP_FE_END diff --git a/appsec/src/extension/ddtrace.h b/appsec/src/extension/ddtrace.h index 98e7d595e9..9e634b6fec 100644 --- a/appsec/src/extension/ddtrace.h +++ b/appsec/src/extension/ddtrace.h @@ -52,6 +52,7 @@ void dd_trace_close_all_spans_and_flush(void); // It is ready for modification, with refcount == 1 zval *nullable dd_trace_span_get_meta(zend_object *nonnull); zval *nullable dd_trace_span_get_metrics(zend_object *nonnull); +zval *nullable dd_trace_span_get_meta_struct(zend_object *nonnull); zend_string *nullable dd_trace_get_formatted_runtime_id(bool persistent); // Set sampling priority on root span @@ -64,14 +65,14 @@ struct _ddtrace_user_req_listeners { zend_array *nullable (*nonnull start_user_req)( ddtrace_user_req_listeners *nonnull self, zend_object *nonnull span, zend_array *nonnull variables, zval *nullable rbe_zv); - zend_array *nullable(*nonnull response_committed)( + zend_array *nullable (*nonnull response_committed)( ddtrace_user_req_listeners *nonnull self, zend_object *nonnull span, int status, zend_array *nonnull headers, zval *nullable entity); void (*nonnull finish_user_req)( ddtrace_user_req_listeners *nonnull self, zend_object *nonnull span); void (*nonnull set_blocking_function)( - ddtrace_user_req_listeners *nonnull self, - zend_object *nonnull span, zval *nonnull blocking_function); + ddtrace_user_req_listeners *nonnull self, zend_object *nonnull span, + zval *nonnull blocking_function); void (*nullable delete)(ddtrace_user_req_listeners *nonnull self); }; bool dd_trace_user_req_add_listeners( diff --git a/appsec/src/extension/msgpack_helpers.c b/appsec/src/extension/msgpack_helpers.c index 809f5f4423..535690cff1 100644 --- a/appsec/src/extension/msgpack_helpers.c +++ b/appsec/src/extension/msgpack_helpers.c @@ -9,7 +9,11 @@ #include "logging.h" #include "msgpack_helpers.h" +#include "php_compat.h" #include "php_helpers.h" +#include "php_objects.h" + +static const int MAX_DEPTH = 32; static void _mpack_write_zval(mpack_writer_t *nonnull w, zval *nonnull zv); @@ -262,3 +266,128 @@ static void _iovec_writer_teardown(mpack_writer_t *w) w->buffer = NULL; w->context = NULL; } + +// NOLINTNEXTLINE(misc-no-recursion) +static bool parse_element( + mpack_reader_t *nonnull reader, int depth, zval *nonnull output) +{ + if (depth >= MAX_DEPTH) { // critical check! + mpack_reader_flag_error(reader, mpack_error_too_big); + mlog(dd_log_error, "decode_msgpack error: msgpack object too big"); + return false; + } + + mpack_tag_t tag = mpack_read_tag(reader); + if (mpack_reader_error(reader) != mpack_ok) { + return false; + } + + switch (mpack_tag_type(&tag)) { + case mpack_type_nil: + ZVAL_NULL(output); + break; + case mpack_type_bool: + ZVAL_BOOL(output, mpack_tag_bool_value(&tag)); + break; + case mpack_type_int: + ZVAL_LONG(output, mpack_tag_int_value(&tag)); + break; + case mpack_type_uint: + ZVAL_LONG(output, mpack_tag_int_value(&tag)); + break; + + case mpack_type_str: { + uint32_t length = mpack_tag_str_length(&tag); + const char *data = mpack_read_bytes_inplace(reader, length); + ZVAL_STRINGL(output, data, length); + mpack_done_str(reader); + break; + } + case mpack_type_array: { + uint32_t count = mpack_tag_array_count(&tag); + array_init(output); + while (count-- > 0) { + zval new; + parse_element(reader, depth + 1, &new); + if (mpack_reader_error(reader) != mpack_ok) { // critical check! + // zval_dtor(&new); + mlog( + dd_log_error, "decode_msgpack error: error decoding array"); + return false; + } + zend_hash_next_index_insert(Z_ARRVAL_P(output), &new); + } + mpack_done_array(reader); + break; + } + case mpack_type_map: { + uint32_t count = mpack_tag_map_count(&tag); + array_init(output); + while (count-- > 0) { + zval key; + zval value; + if (!parse_element(reader, depth + 1, &key) || + Z_TYPE(key) != IS_STRING || + !parse_element(reader, depth + 1, &value) || + mpack_reader_error(reader) != mpack_ok) { // critical check! + mlog(dd_log_error, "decode_msgpack error: error decoding map"); + return false; + } + // Ignore clang because key is a string here + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + zend_hash_add_new(Z_ARRVAL_P(output), Z_STR(key), &value); + zval_dtor(&key); + } + mpack_done_map(reader); + break; + } + default: + mlog(dd_log_error, "decode_msgpack error: type %s not implemented.\n", + mpack_type_to_string(mpack_tag_type(&tag))); + return false; + } + + return true; +} + +static bool parse_messagepack( + const char *nonnull data, size_t length, zval *nonnull output) +{ + mpack_reader_t reader; + mpack_reader_init_data(&reader, data, length); + parse_element(&reader, 0, output); + return mpack_ok == mpack_reader_destroy(&reader); +} + +static PHP_FUNCTION(datadog_appsec_testing_decode_msgpack) +{ + zend_string *encoded = NULL; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &encoded) != SUCCESS) { + RETURN_FALSE; + } + + parse_messagepack(ZSTR_VAL(encoded), ZSTR_LEN(encoded), return_value); +} + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( + void_ret_array_arginfo, 0, 1, IS_ARRAY, 0) +ZEND_ARG_TYPE_INFO(0, encoded, IS_STRING, 0) +ZEND_END_ARG_INFO() + +// clang-format off +static const zend_function_entry testing_functions[] = { + ZEND_RAW_FENTRY(DD_TESTING_NS "decode_msgpack", PHP_FN(datadog_appsec_testing_decode_msgpack), void_ret_array_arginfo, 0) + PHP_FE_END +}; +// clang-format on + +static void _register_testing_objects() +{ + if (!get_global_DD_APPSEC_TESTING()) { + return; + } + + dd_phpobj_reg_funcs(testing_functions); +} + +void dd_msgpack_helpers_startup() { _register_testing_objects(); } diff --git a/appsec/src/extension/msgpack_helpers.h b/appsec/src/extension/msgpack_helpers.h index 6f89c346f9..9edcd314db 100644 --- a/appsec/src/extension/msgpack_helpers.h +++ b/appsec/src/extension/msgpack_helpers.h @@ -40,4 +40,6 @@ void dd_mpack_write_zval(mpack_writer_t *nonnull w, zval *nullable zv); void dd_mpack_writer_init_iov( mpack_writer_t *nonnull writer, zend_llist *nonnull iovec_list); +void dd_msgpack_helpers_startup(); + #endif // DD_MSGPACK_HELPERS_H diff --git a/appsec/src/extension/php_compat.h b/appsec/src/extension/php_compat.h index 14be5aa73b..3183a308ec 100644 --- a/appsec/src/extension/php_compat.h +++ b/appsec/src/extension/php_compat.h @@ -107,3 +107,27 @@ static zend_always_inline void _gc_try_delref(zend_refcounted_h *rc) } #define GC_TRY_DELREF(p) _gc_try_delref(&(p)->gc) #endif + +#if PHP_VERSION_ID < 80100 +# define ZEND_HASH_FOREACH_FROM(_ht, indirect, _from) \ + do { \ + Bucket *_p = (_ht)->arData + _from; \ + Bucket *_end = _p + (_ht)->nNumUsed; \ + for (; _p != _end; _p++) { \ + zval *_z = &_p->val; \ + if (indirect && Z_TYPE_P(_z) == IS_INDIRECT) { \ + _z = Z_INDIRECT_P(_z); \ + } \ + if (UNEXPECTED(Z_TYPE_P(_z) == IS_UNDEF)) \ + continue; +#endif + +#if PHP_VERSION_ID < 80200 +# define DD_FOREACH_FROM(_ht, indirect, _from, index) \ + ZEND_HASH_FOREACH_FROM(_ht, indirect, _from) \ + index = _p->h; +#else +# define DD_FOREACH_FROM(_ht, indirect, _from, index) \ + ZEND_HASH_FOREACH_FROM(_ht, indirect, _from) \ + index = __h; +#endif diff --git a/appsec/src/extension/php_helpers.c b/appsec/src/extension/php_helpers.c index 18a62a91d2..779161c9c6 100644 --- a/appsec/src/extension/php_helpers.c +++ b/appsec/src/extension/php_helpers.c @@ -114,4 +114,4 @@ zend_string *nullable dd_php_get_string_elem( } return Z_STR_P(zresult); -} +} \ No newline at end of file diff --git a/appsec/src/extension/php_helpers.h b/appsec/src/extension/php_helpers.h index 552b1e4688..d8f8e61ca3 100644 --- a/appsec/src/extension/php_helpers.h +++ b/appsec/src/extension/php_helpers.h @@ -1,12 +1,12 @@ // Unless explicitly stated otherwise all files in this repository are // dual-licensed under the Apache-2.0 License or BSD-3-Clause License. // -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2021 Datadog, Inc. +// This product includes software developed at Datadog +// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include #include "attributes.h" +#include #ifdef ZTS # define THREAD_LOCAL_ON_ZTS __thread @@ -41,9 +41,8 @@ dd_php_array_type dd_php_determine_array_type(const zend_array *nonnull); zval *nullable dd_php_get_autoglobal( int track_var, const char *nonnull name, size_t len); -const zend_array *nonnull dd_get_superglob_or_equiv( - const char *nonnull name, size_t name_len, int track, - zend_array *nullable equiv); +const zend_array *nonnull dd_get_superglob_or_equiv(const char *nonnull name, + size_t name_len, int track, zend_array *nullable equiv); zend_string *nullable dd_php_get_string_elem( const zend_array *nullable arr, zend_string *nonnull zstr); zend_string *nullable dd_php_get_string_elem_cstr( diff --git a/appsec/tests/extension/generate_backtrace_01.phpt b/appsec/tests/extension/generate_backtrace_01.phpt new file mode 100644 index 0000000000..2b8cb37ae7 --- /dev/null +++ b/appsec/tests/extension/generate_backtrace_01.phpt @@ -0,0 +1,54 @@ +--TEST-- +Generate backtrace +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(7) "some id" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(3) "two" + ["file"]=> + string(25) "generate_backtrace_01.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(15) + ["function"]=> + string(3) "one" + ["file"]=> + string(25) "generate_backtrace_01.php" + ["id"]=> + int(1) + } + } +} diff --git a/appsec/tests/extension/generate_backtrace_02.phpt b/appsec/tests/extension/generate_backtrace_02.phpt new file mode 100644 index 0000000000..4ef30633f8 --- /dev/null +++ b/appsec/tests/extension/generate_backtrace_02.phpt @@ -0,0 +1,45 @@ +--TEST-- +Number of frames can be configured with DD_APPSEC_MAX_STACK_TRACE_DEPTH +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_MAX_STACK_TRACE_DEPTH=1 +--FILE-- + +--EXPECTF-- +array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(7) "some id" + ["frames"]=> + array(1) { + [0]=> + array(4) { + ["line"]=> + int(15) + ["function"]=> + string(3) "one" + ["file"]=> + string(25) "generate_backtrace_02.php" + ["id"]=> + int(1) + } + } +} diff --git a/appsec/tests/extension/generate_backtrace_03.phpt b/appsec/tests/extension/generate_backtrace_03.phpt new file mode 100644 index 0000000000..bff2244861 --- /dev/null +++ b/appsec/tests/extension/generate_backtrace_03.phpt @@ -0,0 +1,24 @@ +--TEST-- +By default DD_APPSEC_MAX_STACK_TRACE_DEPTH is 32 +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +int(32) diff --git a/appsec/tests/extension/generate_backtrace_04.phpt b/appsec/tests/extension/generate_backtrace_04.phpt new file mode 100644 index 0000000000..fbecbc7f76 --- /dev/null +++ b/appsec/tests/extension/generate_backtrace_04.phpt @@ -0,0 +1,474 @@ +--TEST-- +When DD_APPSEC_MAX_STACK_TRACE_DEPTH is lower than the number of frames. 0.25% are picked from top and 75% from bottom +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_MAX_STACK_TRACE_DEPTH=40 +--FILE-- + +--EXPECTF-- +array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(7) "some id" + ["frames"]=> + array(40) { + [0]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(1) + } + [2]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(2) + } + [3]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(3) + } + [4]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(4) + } + [5]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(5) + } + [6]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(6) + } + [7]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(7) + } + [8]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(8) + } + [9]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(9) + } + [10]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(20) + } + [11]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(21) + } + [12]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(22) + } + [13]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(23) + } + [14]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(24) + } + [15]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(25) + } + [16]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(26) + } + [17]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(27) + } + [18]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(28) + } + [19]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(29) + } + [20]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(30) + } + [21]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(31) + } + [22]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(32) + } + [23]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(33) + } + [24]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(34) + } + [25]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(35) + } + [26]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(36) + } + [27]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(37) + } + [28]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(38) + } + [29]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(39) + } + [30]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(40) + } + [31]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(41) + } + [32]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(42) + } + [33]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(43) + } + [34]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(44) + } + [35]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(45) + } + [36]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(46) + } + [37]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(47) + } + [38]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(48) + } + [39]=> + array(4) { + ["line"]=> + int(15) + ["function"]=> + string(18) "recursive_function" + ["file"]=> + string(25) "generate_backtrace_04.php" + ["id"]=> + int(49) + } + } +} diff --git a/appsec/tests/extension/generate_backtrace_05.phpt b/appsec/tests/extension/generate_backtrace_05.phpt new file mode 100644 index 0000000000..89cc1dcafe --- /dev/null +++ b/appsec/tests/extension/generate_backtrace_05.phpt @@ -0,0 +1,26 @@ +--TEST-- +DD_APPSEC_MAX_STACK_TRACE_DEPTH can be set to unlimited with 0 +--INI-- +extension=ddtrace.so +--ENV-- +DD_APPSEC_MAX_STACK_TRACE_DEPTH=0 +--FILE-- + +--EXPECTF-- +int(50) diff --git a/appsec/tests/extension/generate_backtrace_06.phpt b/appsec/tests/extension/generate_backtrace_06.phpt new file mode 100644 index 0000000000..5a93bbf125 --- /dev/null +++ b/appsec/tests/extension/generate_backtrace_06.phpt @@ -0,0 +1,91 @@ +--TEST-- +Backtrace do not contains datadog frames +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +--INI-- +extension=ddtrace.so +--FILE-- +init(); + + DDTrace\start_span(); + $root = DDTrace\active_span(); + one("foo01"); +} + +?> +--EXPECTF-- +array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(7) "some id" + ["frames"]=> + array(3) { + [0]=> + array(4) { + ["line"]=> + int(24) + ["function"]=> + string(5) "ltrim" + ["file"]=> + string(25) "generate_backtrace_06.php" + ["id"]=> + int(1) + } + [1]=> + array(4) { + ["line"]=> + int(29) + ["function"]=> + string(3) "two" + ["file"]=> + string(25) "generate_backtrace_06.php" + ["id"]=> + int(2) + } + [2]=> + array(4) { + ["line"]=> + int(37) + ["function"]=> + string(3) "one" + ["file"]=> + string(25) "generate_backtrace_06.php" + ["id"]=> + int(3) + } + } +} +string(33) "Verify the wrapped function works" diff --git a/appsec/tests/extension/generate_backtrace_07.phpt b/appsec/tests/extension/generate_backtrace_07.phpt new file mode 100644 index 0000000000..307d01327d --- /dev/null +++ b/appsec/tests/extension/generate_backtrace_07.phpt @@ -0,0 +1,81 @@ +--TEST-- +Functions are fully qualified names +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +--INI-- +extension=ddtrace.so +--FILE-- +two($param01, "other"); + } + } +} + +namespace { + include __DIR__ . '/inc/ddtrace_version.php'; + + DDTrace\start_span(); + $root = DDTrace\active_span(); + + $class = new Some\Package\Foo(); + $class->one("foo01"); +} +?> +--EXPECTF-- +array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(7) "some id" + ["frames"]=> + array(3) { + [0]=> + array(4) { + ["line"]=> + int(12) + ["function"]=> + string(23) "Some\Package\Foo::three" + ["file"]=> + string(25) "generate_backtrace_07.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(17) + ["function"]=> + string(21) "Some\Package\Foo::two" + ["file"]=> + string(25) "generate_backtrace_07.php" + ["id"]=> + int(1) + } + [2]=> + array(4) { + ["line"]=> + int(29) + ["function"]=> + string(21) "Some\Package\Foo::one" + ["file"]=> + string(25) "generate_backtrace_07.php" + ["id"]=> + int(2) + } + } +} diff --git a/appsec/tests/extension/report_backtrace_01.phpt b/appsec/tests/extension/report_backtrace_01.phpt new file mode 100644 index 0000000000..f4624e3263 --- /dev/null +++ b/appsec/tests/extension/report_backtrace_01.phpt @@ -0,0 +1,70 @@ +--TEST-- +Report backtrace +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +bool(true) +array(1) { + ["exploit"]=> + array(1) { + [0]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(7) "some id" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_01.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(18) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_01.php" + ["id"]=> + int(1) + } + } + } + } +} \ No newline at end of file diff --git a/appsec/tests/extension/report_backtrace_02.phpt b/appsec/tests/extension/report_backtrace_02.phpt new file mode 100644 index 0000000000..83571791af --- /dev/null +++ b/appsec/tests/extension/report_backtrace_02.phpt @@ -0,0 +1,106 @@ +--TEST-- +DD_APPSEC_MAX_STACK_TRACES by default is 2 so only two backtraces are reported +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +array(1) { + ["_dd.stack"]=> + array(1) { + ["exploit"]=> + array(2) { + [0]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo01" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_02.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(20) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_02.php" + ["id"]=> + int(1) + } + } + } + [1]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo02" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_02.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(21) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_02.php" + ["id"]=> + int(1) + } + } + } + } + } +} diff --git a/appsec/tests/extension/report_backtrace_03.phpt b/appsec/tests/extension/report_backtrace_03.phpt new file mode 100644 index 0000000000..71a046b74a --- /dev/null +++ b/appsec/tests/extension/report_backtrace_03.phpt @@ -0,0 +1,139 @@ +--TEST-- +DD_APPSEC_MAX_STACK_TRACES can be configured +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_APPSEC_MAX_STACK_TRACES=3 +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +array(1) { + ["_dd.stack"]=> + array(1) { + ["exploit"]=> + array(3) { + [0]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo01" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_03.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(20) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_03.php" + ["id"]=> + int(1) + } + } + } + [1]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo02" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_03.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(21) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_03.php" + ["id"]=> + int(1) + } + } + } + [2]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo03" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_03.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(22) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_03.php" + ["id"]=> + int(1) + } + } + } + } + } +} diff --git a/appsec/tests/extension/report_backtrace_04.phpt b/appsec/tests/extension/report_backtrace_04.phpt new file mode 100644 index 0000000000..5f749c4d1d --- /dev/null +++ b/appsec/tests/extension/report_backtrace_04.phpt @@ -0,0 +1,139 @@ +--TEST-- +DD_APPSEC_MAX_STACK_TRACES can be set to unlimited with 0 +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_APPSEC_MAX_STACK_TRACES=0 +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +array(1) { + ["_dd.stack"]=> + array(1) { + ["exploit"]=> + array(3) { + [0]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo01" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_04.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(20) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_04.php" + ["id"]=> + int(1) + } + } + } + [1]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo02" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_04.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(21) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_04.php" + ["id"]=> + int(1) + } + } + } + [2]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(5) "foo03" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(13) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_04.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(22) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_04.php" + ["id"]=> + int(1) + } + } + } + } + } +} diff --git a/appsec/tests/extension/report_backtrace_05.phpt b/appsec/tests/extension/report_backtrace_05.phpt new file mode 100644 index 0000000000..4dc58ce567 --- /dev/null +++ b/appsec/tests/extension/report_backtrace_05.phpt @@ -0,0 +1,83 @@ +--TEST-- +Trace are reported when helper indicates so +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +--INI-- +extension=ddtrace.so +datadog.appsec.enabled=1 +--FILE-- + '1234']]], []])), +]); + +function two($param01, $param02) +{ + push_address("irrelevant", ["some" => "params", "more" => "parameters"]); +} + +function one($param01) +{ + two($param01, "other"); +} + +rinit(); + +DDTrace\start_span(); +$root = DDTrace\active_span(); +one("foo"); + +DDTrace\close_span(0); + +$span = dd_trace_serialize_closed_spans(); +$meta_struct = $span[0]["meta_struct"]; + +var_dump(decode_msgpack($meta_struct["_dd.stack"])); +DDTrace\flush(); + +?> +--EXPECTF-- +array(1) { + ["exploit"]=> + array(1) { + [0]=> + array(3) { + ["language"]=> + string(3) "php" + ["id"]=> + string(4) "1234" + ["frames"]=> + array(2) { + [0]=> + array(4) { + ["line"]=> + int(20) + ["function"]=> + string(3) "two" + ["file"]=> + string(23) "report_backtrace_05.php" + ["id"]=> + int(0) + } + [1]=> + array(4) { + ["line"]=> + int(27) + ["function"]=> + string(3) "one" + ["file"]=> + string(23) "report_backtrace_05.php" + ["id"]=> + int(1) + } + } + } + } +} \ No newline at end of file diff --git a/appsec/tests/extension/report_backtrace_06.phpt b/appsec/tests/extension/report_backtrace_06.phpt new file mode 100644 index 0000000000..d5d1222907 --- /dev/null +++ b/appsec/tests/extension/report_backtrace_06.phpt @@ -0,0 +1,33 @@ +--TEST-- +DD_APPSEC_STACK_TRACE_ENABLED can be disabled +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_APPSEC_STACK_TRACE_ENABLED=0 +--INI-- +extension=ddtrace.so +--FILE-- + +--EXPECTF-- +bool(false) diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/model/Span.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/model/Span.groovy index 3c43efd2d4..f55c7f83f6 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/model/Span.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/model/Span.groovy @@ -22,4 +22,5 @@ class Span { String type Map meta Map metrics + Map meta_struct } diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy index e53bb3bab2..2f0a815046 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy @@ -1,6 +1,7 @@ package com.datadog.appsec.php.integration import com.datadog.appsec.php.docker.AppSecContainer +import com.datadog.appsec.php.mock_agent.MsgpackHelper import com.datadog.appsec.php.model.Span import com.datadog.appsec.php.model.Trace import org.junit.jupiter.api.Test @@ -8,6 +9,8 @@ import org.testcontainers.containers.Container import java.net.http.HttpRequest import java.net.http.HttpResponse +import org.msgpack.core.MessageUnpacker +import org.msgpack.core.MessagePack import static com.datadog.appsec.php.test.JsonMatcher.matchesJson import static java.net.http.HttpResponse.BodyHandlers.ofString @@ -148,6 +151,15 @@ trait CommonTests { assertThat appsecJson, matchesJson(expJson, false, true) } + Span assert_blocked_span(Span span) { + assert span.metrics."_dd.appsec.enabled" == 1.0d + assert span.metrics."_dd.appsec.waf.duration" > 0.0d + assert span.meta."_dd.appsec.event_rules.version" != '' + assert span.meta."appsec.blocked" == "true" + + return span + } + @Test void 'test blocking'() { // Set ip which is blocked @@ -159,10 +171,41 @@ trait CommonTests { } Span span = trace.first() - assert span.metrics."_dd.appsec.enabled" == 1.0d - assert span.metrics."_dd.appsec.waf.duration" > 0.0d - assert span.meta."_dd.appsec.event_rules.version" != '' - assert span.meta."appsec.blocked" == "true" + + this.assert_blocked_span(span) + } + + @Test + void 'test blocking and stack generation'() { + HttpRequest req = container.buildReq('/generate_stack.php?id=user2020').GET().build() + def trace = container.traceFromRequest(req, ofString()) { HttpResponse re -> + assert re.statusCode() == 403 + assert re.body().contains('blocked') + } + + Span span = trace.first() + assert_blocked_span(span) + + InputStream stream = new ByteArrayInputStream( span.meta_struct."_dd.stack".decodeBase64() ) + MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(stream) + List stacks = [] + stacks << MsgpackHelper.unpackSingle(unpacker) + Object exploit = stacks.first().exploit.first() + + assert exploit.language == "php" + assert exploit.id ==~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/ + assert exploit.frames[0].file == "generate_stack.php" + assert exploit.frames[0].function == "one" + assert exploit.frames[0].id == 0 + assert exploit.frames[0].line == 8 + assert exploit.frames[1].file == "generate_stack.php" + assert exploit.frames[1].function == "two" + assert exploit.frames[1].id == 1 + assert exploit.frames[1].line == 12 + assert exploit.frames[2].file == "generate_stack.php" + assert exploit.frames[2].function == "three" + assert exploit.frames[2].id == 2 + assert exploit.frames[2].line == 15 } @Test diff --git a/appsec/tests/integration/src/test/waf/recommended.json b/appsec/tests/integration/src/test/waf/recommended.json index e4ff0748ce..f06f7f29fc 100644 --- a/appsec/tests/integration/src/test/waf/recommended.json +++ b/appsec/tests/integration/src/test/waf/recommended.json @@ -31,6 +31,32 @@ }, { "id": "blk-001-002", + "name": "Block user with stack", + "tags": { + "type": "block_user", + "category": "security_response" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "usr.id" + } + ], + "data": "blocked_users" + }, + "operator": "exact_match" + } + ], + "transformers": [], + "on_match": [ + "stack_trace", + "block" + ] + }, + { + "id": "blk-001-003", "name": "Block User Addresses", "tags": { "type": "block_user", diff --git a/appsec/tests/integration/src/test/www/base/public/generate_stack.php b/appsec/tests/integration/src/test/www/base/public/generate_stack.php new file mode 100644 index 0000000000..ab1cfa26f7 --- /dev/null +++ b/appsec/tests/integration/src/test/www/base/public/generate_stack.php @@ -0,0 +1,17 @@ + Date: Thu, 22 Aug 2024 11:21:58 +0200 Subject: [PATCH 035/103] feat: Symfony Messenger Integration (#2749) * Add contribution * tests: Add `testAsyncLuckyNumberNotification` * tests: Add `testAsyncFailure` + feat: add `snapshotFromTraces` * tests: activate middleware tracing * tests: Ignore variable fields * tests: Symfony Messenger v6 +-w/ redis-messenger * feat: Support Symfony Messenger 5 * tests: Symfony Messenger v4.4 + load only for Symfony Messenger 4.4+ * tests: Replace /home/circleci/app by /home/circleci/datadog * noise * tests: Skip some tests * tests: Add missing migration * Skip 5.2 test in PHP 8.3 * fix: `messenger:consume` command detection * Clean dead code * fix: typo * tests: Fix Orphans Removal * tests: Refactor * fix: Ensure no console trace is reported as a trace * tests: Require ext-redis and replace localhost:9999 by localhost * tests: Change db.name * tests: Change redis version * tests: Change redis benchmark sversion to 5.3.10 * tests: update_test_agent_session_token * tests: Revert pre-update-cmd * tests: Use doctrine-messenger everywhere --- ext/configuration.h | 3 + ext/integrations/integrations.c | 5 + ext/integrations/integrations.h | 77 ++-- .../Symfony/SymfonyIntegration.php | 16 +- .../SymfonyMessenger/DDTraceStamp.php | 20 + .../SymfonyMessengerIntegration.php | 348 +++++++++++++++ tests/Common/SnapshotTestTrait.php | 43 ++ tests/Common/TracerTestTrait.php | 8 +- tests/Frameworks/Symfony/Version_4_4/.env | 7 + .../Symfony/Version_4_4/composer.json | 1 + .../config/packages/messenger.yaml | 15 + .../migrations/Version20240717085117.php | 31 ++ .../src/Controller/LuckyController.php | 35 ++ .../src/Message/LuckyNumberNotification.php | 12 + .../LuckyNumberNotificationHandler.php | 19 + tests/Frameworks/Symfony/Version_5_2/.env | 17 +- .../Symfony/Version_5_2/composer.json | 2 + .../config/packages/messenger.yaml | 18 + .../migrations/Version20240712130658.php | 31 ++ .../src/Controller/LuckyController.php | 34 ++ .../src/Message/LuckyNumberNotification.php | 12 + .../LuckyNumberNotificationHandler.php | 19 + tests/Frameworks/Symfony/Version_6_2/.env | 7 + .../Symfony/Version_6_2/composer.json | 2 + .../config/packages/messenger.yaml | 24 + .../migrations/Version20240822083531.php | 31 ++ .../src/Controller/LuckyController.php | 29 ++ .../src/Message/LuckyNumberNotification.php | 8 + .../LuckyNumberNotificationHandler.php | 20 + tests/Frameworks/Symfony/Version_7_0/.env | 7 + .../Symfony/Version_7_0/composer.json | 2 + .../config/packages/messenger.yaml | 28 ++ .../Version_7_0/config/routes/security.yaml | 3 + .../migrations/Version20240711115224.php | 31 ++ .../src/Controller/LuckyController.php | 29 ++ .../src/Message/LuckyNumberNotification.php | 8 + .../LuckyNumberNotificationHandler.php | 19 + .../Symfony/V4_4/MessengerTest.php | 88 ++++ .../Symfony/V5_2/MessengerTest.php | 89 ++++ .../Symfony/V6_2/MessengerTest.php | 90 ++++ .../Symfony/V7_0/MessengerTest.php | 87 ++++ ...4_4.messenger_test.test_async_failure.json | 382 ++++++++++++++++ ...nger_test.test_async_failure_consumer.json | 312 +++++++++++++ ...4_4.messenger_test.test_async_success.json | 382 ++++++++++++++++ ...nger_test.test_async_success_consumer.json | 294 +++++++++++++ ...5_2.messenger_test.test_async_failure.json | 370 ++++++++++++++++ ...nger_test.test_async_failure_consumer.json | 390 +++++++++++++++++ ...5_2.messenger_test.test_async_success.json | 370 ++++++++++++++++ ...nger_test.test_async_success_consumer.json | 294 +++++++++++++ ...6_2.messenger_test.test_async_failure.json | 370 ++++++++++++++++ ...nger_test.test_async_failure_consumer.json | 313 +++++++++++++ ...6_2.messenger_test.test_async_success.json | 370 ++++++++++++++++ ...nger_test.test_async_success_consumer.json | 295 +++++++++++++ ...7_0.messenger_test.test_async_failure.json | 391 +++++++++++++++++ ...nger_test.test_async_failure_consumer.json | 413 ++++++++++++++++++ ...7_0.messenger_test.test_async_success.json | 391 +++++++++++++++++ ...nger_test.test_async_success_consumer.json | 295 +++++++++++++ 57 files changed, 6963 insertions(+), 44 deletions(-) create mode 100644 src/DDTrace/Integrations/SymfonyMessenger/DDTraceStamp.php create mode 100644 src/DDTrace/Integrations/SymfonyMessenger/SymfonyMessengerIntegration.php create mode 100644 tests/Frameworks/Symfony/Version_4_4/config/packages/messenger.yaml create mode 100644 tests/Frameworks/Symfony/Version_4_4/migrations/Version20240717085117.php create mode 100644 tests/Frameworks/Symfony/Version_4_4/src/Controller/LuckyController.php create mode 100644 tests/Frameworks/Symfony/Version_4_4/src/Message/LuckyNumberNotification.php create mode 100644 tests/Frameworks/Symfony/Version_4_4/src/MessageHandler/LuckyNumberNotificationHandler.php create mode 100644 tests/Frameworks/Symfony/Version_5_2/config/packages/messenger.yaml create mode 100644 tests/Frameworks/Symfony/Version_5_2/migrations/Version20240712130658.php create mode 100644 tests/Frameworks/Symfony/Version_5_2/src/Controller/LuckyController.php create mode 100644 tests/Frameworks/Symfony/Version_5_2/src/Message/LuckyNumberNotification.php create mode 100644 tests/Frameworks/Symfony/Version_5_2/src/MessageHandler/LuckyNumberNotificationHandler.php create mode 100644 tests/Frameworks/Symfony/Version_6_2/config/packages/messenger.yaml create mode 100644 tests/Frameworks/Symfony/Version_6_2/migrations/Version20240822083531.php create mode 100644 tests/Frameworks/Symfony/Version_6_2/src/Controller/LuckyController.php create mode 100644 tests/Frameworks/Symfony/Version_6_2/src/Message/LuckyNumberNotification.php create mode 100644 tests/Frameworks/Symfony/Version_6_2/src/MessageHandler/LuckyNumberNotificationHandler.php create mode 100644 tests/Frameworks/Symfony/Version_7_0/config/packages/messenger.yaml create mode 100644 tests/Frameworks/Symfony/Version_7_0/config/routes/security.yaml create mode 100644 tests/Frameworks/Symfony/Version_7_0/migrations/Version20240711115224.php create mode 100644 tests/Frameworks/Symfony/Version_7_0/src/Controller/LuckyController.php create mode 100644 tests/Frameworks/Symfony/Version_7_0/src/Message/LuckyNumberNotification.php create mode 100644 tests/Frameworks/Symfony/Version_7_0/src/MessageHandler/LuckyNumberNotificationHandler.php create mode 100644 tests/Integrations/Symfony/V4_4/MessengerTest.php create mode 100644 tests/Integrations/Symfony/V5_2/MessengerTest.php create mode 100644 tests/Integrations/Symfony/V6_2/MessengerTest.php create mode 100644 tests/Integrations/Symfony/V7_0/MessengerTest.php create mode 100644 tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure.json create mode 100644 tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure_consumer.json create mode 100644 tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success.json create mode 100644 tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success_consumer.json create mode 100644 tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure.json create mode 100644 tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure_consumer.json create mode 100644 tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success.json create mode 100644 tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success_consumer.json create mode 100644 tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure.json create mode 100644 tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure_consumer.json create mode 100644 tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success.json create mode 100644 tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success_consumer.json create mode 100644 tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure.json create mode 100644 tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure_consumer.json create mode 100644 tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success.json create mode 100644 tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success_consumer.json diff --git a/ext/configuration.h b/ext/configuration.h index 3bf8ccfdd7..a44c507efe 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -128,7 +128,10 @@ enum ddtrace_sampling_rules_format { CONFIG(BOOL, DD_TRACE_REPORT_HOSTNAME, "false") \ CONFIG(BOOL, DD_TRACE_FLUSH_COLLECT_CYCLES, "false") \ CONFIG(BOOL, DD_TRACE_LARAVEL_QUEUE_DISTRIBUTED_TRACING, "true") \ + CONFIG(BOOL, DD_TRACE_SYMFONY_MESSENGER_DISTRIBUTED_TRACING, "true") \ + CONFIG(BOOL, DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES, "false") \ CONFIG(BOOL, DD_TRACE_REMOVE_ROOT_SPAN_LARAVEL_QUEUE, "true") \ + CONFIG(BOOL, DD_TRACE_REMOVE_ROOT_SPAN_SYMFONY_MESSENGER, "true") \ CONFIG(BOOL, DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS, "false") \ CONFIG(SET, DD_TRACE_RESOURCE_URI_FRAGMENT_REGEX, "") \ CONFIG(SET, DD_TRACE_RESOURCE_URI_MAPPING_INCOMING, "") \ diff --git a/ext/integrations/integrations.c b/ext/integrations/integrations.c index 259d5910b8..76b0babdb9 100644 --- a/ext/integrations/integrations.c +++ b/ext/integrations/integrations.c @@ -420,6 +420,11 @@ void ddtrace_integrations_minit(void) { DD_SET_UP_DEFERRED_LOADING_BY_METHOD(DDTRACE_INTEGRATION_SYMFONY, "Drupal\\Core\\DrupalKernel", "__construct", "DDTrace\\Integrations\\Symfony\\SymfonyIntegration"); + DD_SET_UP_DEFERRED_LOADING_BY_METHOD(DDTRACE_INTEGRATION_SYMFONYMESSENGER, "Symfony\\Component\\Messenger\\Worker", "__construct", + "DDTrace\\Integrations\\SymfonyMessenger\\SymfonyMessengerIntegration"); + DD_SET_UP_DEFERRED_LOADING_BY_METHOD(DDTRACE_INTEGRATION_SYMFONYMESSENGER, "Symfony\\Component\\Messenger\\MessageBusInterface", "dispatch", + "DDTrace\\Integrations\\SymfonyMessenger\\SymfonyMessengerIntegration"); + DD_SET_UP_DEFERRED_LOADING_BY_FUNCTION(DDTRACE_INTEGRATION_SQLSRV, "sqlsrv_connect", "DDTrace\\Integrations\\SQLSRV\\SQLSRVIntegration"); diff --git a/ext/integrations/integrations.h b/ext/integrations/integrations.h index 9ca0c4cae7..75e2a4ec8b 100644 --- a/ext/integrations/integrations.h +++ b/ext/integrations/integrations.h @@ -9,45 +9,46 @@ #define DD_TRACE_INTEGRATION_LOADED 1 #define DD_TRACE_INTEGRATION_NOT_AVAILABLE 2 -#define DDTRACE_LONGEST_INTEGRATION_NAME_LEN 13 // "zendframework" FTW! +#define DDTRACE_LONGEST_INTEGRATION_NAME_LEN 16 // "symfonymessenger" FTW! -#define DD_INTEGRATIONS \ - INTEGRATION(AMQP, "amqp") \ - INTEGRATION(CAKEPHP, "cakephp") \ - INTEGRATION(CODEIGNITER, "codeigniter") \ - INTEGRATION(EXEC, "exec") \ - INTEGRATION(CURL, "curl") \ - INTEGRATION(DRUPAL, "drupal") \ - INTEGRATION(ELASTICSEARCH, "elasticsearch") \ - INTEGRATION(ELOQUENT, "eloquent") \ - INTEGRATION(FRANKENPHP, "frankenphp") \ - INTEGRATION(GUZZLE, "guzzle") \ - INTEGRATION(LAMINAS, "laminas") \ - INTEGRATION(LARAVEL, "laravel") \ - INTEGRATION(LARAVELQUEUE, "laravelqueue") \ - INTEGRATION(LOGS, "logs", "false", DD_LOGS_INJECTION) \ - INTEGRATION(LUMEN, "lumen") \ - INTEGRATION(MAGENTO, "magento") \ - INTEGRATION(MEMCACHE, "memcache") \ - INTEGRATION(MEMCACHED, "memcached") \ - INTEGRATION(MONGO, "mongo") \ - INTEGRATION(MONGODB, "mongodb") \ - INTEGRATION(MYSQLI, "mysqli") \ - INTEGRATION(NETTE, "nette") \ - INTEGRATION(OPENAI, "openai") \ - INTEGRATION(PCNTL, "pcntl") \ - INTEGRATION(PDO, "pdo") \ - INTEGRATION(PHPREDIS, "phpredis") \ - INTEGRATION(PREDIS, "predis") \ - INTEGRATION(PSR18, "psr18") \ - INTEGRATION(ROADRUNNER, "roadrunner") \ - INTEGRATION(SQLSRV, "sqlsrv") \ - INTEGRATION(SLIM, "slim") \ - INTEGRATION(SWOOLE, "swoole") \ - INTEGRATION(SYMFONY, "symfony") \ - INTEGRATION(WEB, "web") \ - INTEGRATION(WORDPRESS, "wordpress") \ - INTEGRATION(YII, "yii") \ +#define DD_INTEGRATIONS \ + INTEGRATION(AMQP, "amqp") \ + INTEGRATION(CAKEPHP, "cakephp") \ + INTEGRATION(CODEIGNITER, "codeigniter") \ + INTEGRATION(EXEC, "exec") \ + INTEGRATION(CURL, "curl") \ + INTEGRATION(DRUPAL, "drupal") \ + INTEGRATION(ELASTICSEARCH, "elasticsearch") \ + INTEGRATION(ELOQUENT, "eloquent") \ + INTEGRATION(FRANKENPHP, "frankenphp") \ + INTEGRATION(GUZZLE, "guzzle") \ + INTEGRATION(LAMINAS, "laminas") \ + INTEGRATION(LARAVEL, "laravel") \ + INTEGRATION(LARAVELQUEUE, "laravelqueue") \ + INTEGRATION(LOGS, "logs", "false", DD_LOGS_INJECTION) \ + INTEGRATION(LUMEN, "lumen") \ + INTEGRATION(MAGENTO, "magento") \ + INTEGRATION(MEMCACHE, "memcache") \ + INTEGRATION(MEMCACHED, "memcached") \ + INTEGRATION(MONGO, "mongo") \ + INTEGRATION(MONGODB, "mongodb") \ + INTEGRATION(MYSQLI, "mysqli") \ + INTEGRATION(NETTE, "nette") \ + INTEGRATION(OPENAI, "openai") \ + INTEGRATION(PCNTL, "pcntl") \ + INTEGRATION(PDO, "pdo") \ + INTEGRATION(PHPREDIS, "phpredis") \ + INTEGRATION(PREDIS, "predis") \ + INTEGRATION(PSR18, "psr18") \ + INTEGRATION(ROADRUNNER, "roadrunner") \ + INTEGRATION(SQLSRV, "sqlsrv") \ + INTEGRATION(SLIM, "slim") \ + INTEGRATION(SWOOLE, "swoole") \ + INTEGRATION(SYMFONY, "symfony") \ + INTEGRATION(SYMFONYMESSENGER, "symfonymessenger") \ + INTEGRATION(WEB, "web") \ + INTEGRATION(WORDPRESS, "wordpress") \ + INTEGRATION(YII, "yii") \ INTEGRATION(ZENDFRAMEWORK, "zendframework") #define INTEGRATION(id, ...) DDTRACE_INTEGRATION_##id, diff --git a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php index be854b939a..c633513ec0 100644 --- a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php +++ b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php @@ -9,10 +9,8 @@ use DDTrace\Tag; use DDTrace\Type; use DDTrace\Util\Normalizer; -use DDTrace\Util\Versions; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\KernelEvents; -use function DDTrace\install_hook; class SymfonyIntegration extends Integration { @@ -258,6 +256,18 @@ function ($This, $scope, $args) { return false; } + $commandName = $this->getName(); + + if (\dd_trace_env_config('DD_TRACE_REMOVE_ROOT_SPAN_SYMFONY_MESSENGER') + && $commandName === 'messenger:consume' + ) { + \DDTrace\set_priority_sampling(DD_TRACE_PRIORITY_SAMPLING_AUTO_REJECT); + \dd_trace_close_all_spans_and_flush(); + ini_set("datadog.trace.auto_flush_enabled", 1); + ini_set("datadog.trace.generate_root_span", 0); + return false; + } + $namespace = \get_class($this); if (strpos($namespace, DrupalIntegration::NAME) !== false) { $integration->frameworkPrefix = DrupalIntegration::NAME; @@ -266,7 +276,7 @@ function ($This, $scope, $args) { } $span->name = 'symfony.console.command.run'; - $span->resource = $this->getName() ?: $span->name; + $span->resource = $commandName ?: $span->name; $span->service = \ddtrace_config_app_name($integration->frameworkPrefix); $span->type = Type::CLI; $span->meta['symfony.console.command.class'] = \get_class($this); diff --git a/src/DDTrace/Integrations/SymfonyMessenger/DDTraceStamp.php b/src/DDTrace/Integrations/SymfonyMessenger/DDTraceStamp.php new file mode 100644 index 0000000000..87233d882a --- /dev/null +++ b/src/DDTrace/Integrations/SymfonyMessenger/DDTraceStamp.php @@ -0,0 +1,20 @@ +headers = $headers; + } + + public function getHeaders(): array + { + return $this->headers; + } +} diff --git a/src/DDTrace/Integrations/SymfonyMessenger/SymfonyMessengerIntegration.php b/src/DDTrace/Integrations/SymfonyMessenger/SymfonyMessengerIntegration.php new file mode 100644 index 0000000000..afc521f5d2 --- /dev/null +++ b/src/DDTrace/Integrations/SymfonyMessenger/SymfonyMessengerIntegration.php @@ -0,0 +1,348 @@ +setSpanAttributes($span, 'symfony.messenger.dispatch', null, $retval ?? $args[0], null, null, true); + if ($exception) { + // Worker::handleMessage() will catch the exception. We need to manually attach it to the root span. + \DDTrace\root_span()->exception = $exception; + } + } + ); + + // Attach current context to Envelope before sender sends it to remote queue + install_hook( + 'Symfony\Component\Messenger\Transport\Sender\SenderInterface::send', + function (HookData $hook) { + /** @var \Symfony\Component\Messenger\Envelope $envelope */ + $envelope = $hook->args[0]; + + if (\ddtrace_config_distributed_tracing_enabled()) { + $ddTraceStamp = $envelope->last(DDTraceStamp::class); + + // Add distributed tracing stamp only if not already on the envelope + if ($ddTraceStamp === null) { + $tracingHeaders = \DDTrace\generate_distributed_tracing_headers(); + $hook->overrideArguments([ + $envelope->with(new DDTraceStamp($tracingHeaders)) + ]); + } + } + } + ); + + trace_method( + 'Symfony\Component\Messenger\Transport\TransportInterface', + 'send', + function (SpanData $span, array $args, $envelope) use ($integration) { + $integration->setSpanAttributes($span, 'symfony.messenger.send', null, $envelope ?? $args[0], null, 'send', true); + } + ); + + trace_method( + 'Symfony\Component\Messenger\Worker', + 'handleMessage', + [ + 'prehook' => function (SpanData $span, array $args) use ($integration) { + /** @var \Symfony\Component\Messenger\Envelope $envelope */ + $envelope = $args[0]; + /** @var string|ReceiverInterface $transportName */ + $transportName = $args[1]; + if (\is_object($transportName)) { + $transportName = \get_class($transportName); + } + + $integration->setSpanAttributes( + $span, + 'symfony.messenger.consume', + null, + $envelope, + $transportName, + 'receive' + ); + + $ddTraceStamp = $envelope->last(DDTraceStamp::class); + if ($ddTraceStamp instanceof DDTraceStamp) { + $tracingHeaders = $ddTraceStamp->getHeaders(); + if (\dd_trace_env_config('DD_TRACE_SYMFONY_MESSENGER_DISTRIBUTED_TRACING')) { + \DDTrace\consume_distributed_tracing_headers($tracingHeaders); + } else { + $span->links[] = \DDTrace\SpanLink::fromHeaders($tracingHeaders); + } + } + }, + 'posthook' => function (SpanData $span) use ($integration) { + if ($span->exception !== null) { + // Used by Logs Correlation to track the origin of an exception + ObjectKVStore::put( + $span->exception, + 'exception_trace_identifiers', + [ + 'trace_id' => \DDTrace\logs_correlation_trace_id(), + 'span_id' => \dd_trace_peek_span_id() + ] + ); + } + }, + 'recurse' => true, + ] + ); + + $callHandlerExists = \method_exists('Symfony\Component\Messenger\Middleware\HandleMessageMiddleware', 'callHandler'); + if ($callHandlerExists) { + // Symfony Messenger 6.2+ + hook_method( + 'Symfony\Component\Messenger\Middleware\HandleMessageMiddleware', + 'callHandler', + function ($This, $scope, $args) use ($integration) { + $message = $args[1]; + install_hook($args[0], function (HookData $hook) use ($integration, $message) { + $integration->setSpanAttributes($hook->span(), 'symfony.messenger.handle', \get_class($this), $message, false, 'process'); + remove_hook($hook->id); + }); + } + ); + } else { + hook_method( + 'Symfony\Component\Messenger\Middleware\HandleMessageMiddleware', + '__construct', + function ($This, $scope, $args) { + /** @var HandlersLocatorInterface $handlersLocator */ + $handlersLocator = $args[0]; + ObjectKVStore::put($This, 'handlersLocator', $handlersLocator); + } + ); + + hook_method( + 'Symfony\Component\Messenger\Middleware\HandleMessageMiddleware', + 'handle', + function ($This, $scope, $args) use ($integration) { + $envelope = $args[0]; + $handlersLocator = ObjectKVStore::get($This, 'handlersLocator'); + $message = $envelope->getMessage(); + foreach ($handlersLocator->getHandlers($envelope) as $handlerDescriptor) { + if ($integration->messageHasAlreadyBeenHandled($envelope, $handlerDescriptor)) { + continue; + } + + $handler = $handlerDescriptor->getHandler(); + install_hook($handler, function (HookData $hook) use ($integration, $message) { + $integration->setSpanAttributes($hook->span(), 'symfony.messenger.handle', \get_class($this), $message, false, 'process'); + remove_hook($hook->id); + }); + } + } + ); + } + + if (dd_trace_env_config('DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES')) { + $handleFn = function (SpanData $span, array $args) use ($integration) { + $integration->setSpanAttributes($span, 'symfony.messenger.middleware', \get_class($this), $args[0]); + }; + + trace_method( + 'Symfony\Component\Messenger\Middleware\MiddlewareInterface', + 'handle', + [ + 'posthook' => $handleFn, + 'recurse' => true + ] + ); + + // Symfony Messenger 6.2+ + trace_method( + 'Symfony\Component\Messenger\Middleware\HandleMessageMiddleware', + 'handle', + [ + 'posthook' => $handleFn, + 'recurse' => true + ] + ); + } + + return Integration::LOADED; + } + + public function messageHasAlreadyBeenHandled(Envelope $envelope, HandlerDescriptor $handlerDescriptor): bool + { + $some = array_filter($envelope + ->all(HandledStamp::class), function (HandledStamp $stamp) use ($handlerDescriptor) { + return $stamp->getHandlerName() === $handlerDescriptor->getName(); + }); + + return \count($some) > 0; + } + + public function setSpanAttributes( + SpanData $span, + string $name, + $resource = null, + $envelopeOrMessage = null, + $transportName = null, + $operation = null, + bool $addStampsInformation = false + ) { + if ($envelopeOrMessage instanceof Envelope) { + $this->resolveMetadataFromEnvelope($span, $envelopeOrMessage, $resource, $transportName, $operation, $addStampsInformation); + } else { + $this->tryResolveMetadataFromMessage($span, $envelopeOrMessage, $resource, $transportName, $operation); + } + + $span->name = $name; + $span->service = \ddtrace_config_app_name('symfony'); + $span->type = 'queue'; + $span->meta[Tag::MQ_SYSTEM] = 'symfony'; + $span->meta[Tag::MQ_DESTINATION_KIND] = 'queue'; + $span->meta[Tag::COMPONENT] = SymfonyMessengerIntegration::NAME; + } + + public function resolveMetadataFromEnvelope( + SpanData $span, + Envelope $envelope, + $resource = null, + $transportName = null, + $operation = null, + bool $addStampsInformation = false + ) { + $busStamp = $envelope->last(BusNameStamp::class); + $consumedByWorkerStamp = $envelope->last(ConsumedByWorkerStamp::class); + $delayStamp = $envelope->last(DelayStamp::class); + $handledStamp = $envelope->last(HandledStamp::class); + $receivedStamp = $envelope->last(ReceivedStamp::class); + $redeliveryStamp = $envelope->last(RedeliveryStamp::class); + $sentStamp = $envelope->last(SentStamp::class); + $transportMessageIdStamp = $envelope->last(TransportMessageIdStamp::class); + + $messageName = \get_class($envelope->getMessage()); + $transportName = $sentStamp + ? $sentStamp->getSenderAlias() + : ($receivedStamp ? $receivedStamp->getTransportName() : $transportName); + $senderClass = $sentStamp ? $sentStamp->getSenderClass() : null; + $transportMessageId = $transportMessageIdStamp ? $transportMessageIdStamp->getId() : null; + + // amazon-sqs-messenger doesn't add TransportMessageIdStamp to the envelope + if (\class_exists(AmazonSqsReceivedStamp::class) + && ($amazonSqsReceivedStamp = $envelope->last(AmazonSqsReceivedStamp::class)) + ) { + $transportMessageId = $amazonSqsReceivedStamp->getId(); + } + + $stamps = []; + if ($addStampsInformation) { + foreach ($envelope->all() as $stampFqcn => $instances) { + $stamps[$stampFqcn] = \count($instances); + } + } + + if ($operation !== 'receive' && $operation !== 'send' && ($consumedByWorkerStamp || $receivedStamp)) { + $operation = 'process'; + } + + $metadata = [ + 'messaging.symfony.bus' => $busStamp ? $busStamp->getBusName() : null, + 'messaging.symfony.handler' => $handledStamp ? $handledStamp->getHandlerName() : null, + 'messaging.symfony.message' => $messageName, + 'messaging.symfony.redelivered_at' => $redeliveryStamp ? $redeliveryStamp->getRedeliveredAt()->format('Y-m-d\TH:i:sP') : null, + 'messaging.symfony.sender' => $senderClass, + Tag::MQ_DESTINATION => $transportName, + Tag::MQ_MESSAGE_ID => $transportMessageId, + Tag::MQ_OPERATION => $operation, + Tag::SPAN_KIND => $this->determineSpanKind($operation), + ]; + + $metrics = [ + 'messaging.symfony.delay' => $delayStamp ? $delayStamp->getDelay() : null, + 'messaging.symfony.retry_count' => $redeliveryStamp ? $redeliveryStamp->getRetryCount() : null, + 'messaging.symfony.stamps' => $stamps, + ]; + + if (empty($resource)) { + if (empty($transportName)) { + $resource = $messageName; + } elseif ($operation === 'send') { + $resource = "$messageName -> $transportName"; + } elseif ($operation === 'receive' || $receivedStamp) { + $resource = "$transportName -> $messageName"; + } else { + $resource = "$messageName -> $transportName"; + } + } + $span->resource = $resource; + $span->meta = \array_merge($span->meta, \array_filter($metadata)); + $span->metrics = \array_merge($span->metrics, \array_filter($metrics)); + } + + public function tryResolveMetadataFromMessage(SpanData $span, $message, $resource, $transportName, $operation) { + if ($message) { + $messageName = \get_class($message); + $resource = $resource ?? $messageName; + $span->meta['messaging.symfony.message'] = $messageName; + } + + if ($resource) { + $span->resource = $resource; + } + if ($transportName) { + $span->meta[Tag::MQ_DESTINATION] = $transportName; + } + if ($operation) { + $span->meta[Tag::MQ_OPERATION] = $operation; + } + + $spanKind = $this->determineSpanKind($operation); + if ($spanKind) { + $span->meta[Tag::SPAN_KIND] = $spanKind; + } + } + + public function determineSpanKind($operation) { + switch ($operation) { + case 'receive': + return Tag::SPAN_KIND_VALUE_CONSUMER; + case 'send': + return Tag::SPAN_KIND_VALUE_PRODUCER; + default: + return null; // Internal operation is implicit + } + } +} diff --git a/tests/Common/SnapshotTestTrait.php b/tests/Common/SnapshotTestTrait.php index 9f0838e315..59d8656dde 100644 --- a/tests/Common/SnapshotTestTrait.php +++ b/tests/Common/SnapshotTestTrait.php @@ -366,4 +366,47 @@ public function isolateTracerSnapshot( self::putEnv('DD_TRACE_SHUTDOWN_TIMEOUT'); self::putEnv('DD_TRACE_AGENT_RETRIES'); } + + public function snapshotFromTraces( + $traces, + $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'meta.error.stack', 'meta._dd.p.tid'], + $tokenSubstitute = null, + $ignoreSampledAway = false + ) { + $token = $tokenSubstitute ?: $this->generateToken(); + $this->startSnapshotSession($token); + $originalToken = ini_get("datadog.trace.agent_test_session_token"); + update_test_agent_session_token($token); + + if ($ignoreSampledAway) { + $traces = $this->ignoreSampledTraces($traces); + } + + $this->sendTracesToTestAgent($traces); + + $this->stopAndCompareSnapshotSession($token, $fieldsToIgnore, \count($traces)); + update_test_agent_session_token($originalToken); + } + + protected function ignoreSampledTraces($traces) { + $filteredSpans = []; + $sampledTraceIDs = []; + foreach ($traces as $trace) { + foreach ($trace as $span) { + if (isset($span['metrics']['_sampling_priority_v1']) && $span['metrics']['_sampling_priority_v1'] === 0) { + $sampledTraceIDs[$span['trace_id']] = true; + } + } + } + + foreach ($traces as $trace) { + foreach ($trace as $span) { + if (!isset($sampledTraceIDs[$span['trace_id']])) { + $filteredSpans[] = $span; + } + } + } + + return [$filteredSpans]; + } } diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index 8da57c21c3..4ce8e6fe7d 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -112,7 +112,7 @@ public function sendTracesToTestAgent($traces) curl_close($curl); // Output the response for debugging purposes - // echo $response; + //echo $response; } /** @@ -261,7 +261,11 @@ public function executeCli($scriptPath, $customEnvs = [], $customInis = [], $arg )); $script = escapeshellarg($scriptPath); - $arguments = escapeshellarg($arguments); + if (\is_string($arguments)) { + $arguments = escapeshellarg($arguments); + } elseif (\is_array($arguments)) { + $arguments = implode(' ', array_map('escapeshellarg', $arguments)); + } $commandToExecute = "$envs " . PHP_BINARY . " $inis $script $arguments"; if ($withOutput) { $ret = (string) `$commandToExecute 2>&1`; diff --git a/tests/Frameworks/Symfony/Version_4_4/.env b/tests/Frameworks/Symfony/Version_4_4/.env index 3a3d8e3d2e..9df636ff8c 100644 --- a/tests/Frameworks/Symfony/Version_4_4/.env +++ b/tests/Frameworks/Symfony/Version_4_4/.env @@ -31,3 +31,10 @@ APP_SECRET=3758dd8072ced4110d2ae1858da9b1a2 # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7 ###< doctrine/doctrine-bundle ### + +###> symfony/messenger ### +# Choose one of the transports below +MESSENGER_TRANSPORT_DSN=doctrine://default +# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages +# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages +###< symfony/messenger ### diff --git a/tests/Frameworks/Symfony/Version_4_4/composer.json b/tests/Frameworks/Symfony/Version_4_4/composer.json index 64d0977864..b3132c6a7f 100644 --- a/tests/Frameworks/Symfony/Version_4_4/composer.json +++ b/tests/Frameworks/Symfony/Version_4_4/composer.json @@ -23,6 +23,7 @@ "symfony/http-client": "4.4.*", "symfony/intl": "4.4.*", "symfony/mailer": "4.4.*", + "symfony/messenger": "4.4.*", "symfony/monolog-bundle": "^3.1", "symfony/process": "4.4.*", "symfony/property-access": "4.4.*", diff --git a/tests/Frameworks/Symfony/Version_4_4/config/packages/messenger.yaml b/tests/Frameworks/Symfony/Version_4_4/config/packages/messenger.yaml new file mode 100644 index 0000000000..892bcafbbe --- /dev/null +++ b/tests/Frameworks/Symfony/Version_4_4/config/packages/messenger.yaml @@ -0,0 +1,15 @@ +framework: + messenger: + # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. + # failure_transport: failed + + transports: + # https://symfony.com/doc/current/messenger.html#transport-configuration + async: '%env(MESSENGER_TRANSPORT_DSN)%' + # failed: 'doctrine://default?queue_name=failed' + # sync: 'sync://' + + routing: + # Route your messages to the transports + # 'App\Message\YourMessage': async + 'App\Message\LuckyNumberNotification': async diff --git a/tests/Frameworks/Symfony/Version_4_4/migrations/Version20240717085117.php b/tests/Frameworks/Symfony/Version_4_4/migrations/Version20240717085117.php new file mode 100644 index 0000000000..e8717015b6 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_4_4/migrations/Version20240717085117.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, headers LONGTEXT CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, queue_name VARCHAR(190) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E016BA31DB (delivered_at), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E0FB7336F0 (queue_name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE messenger_messages'); + } +} diff --git a/tests/Frameworks/Symfony/Version_4_4/src/Controller/LuckyController.php b/tests/Frameworks/Symfony/Version_4_4/src/Controller/LuckyController.php new file mode 100644 index 0000000000..f9405bb43b --- /dev/null +++ b/tests/Frameworks/Symfony/Version_4_4/src/Controller/LuckyController.php @@ -0,0 +1,35 @@ +dispatch(new LuckyNumberNotification($number)); + + return new Response("$number"); + } + + /** + * @Route("/lucky/fail", name="lucky_fail") + + */ + public function fail(MessageBusInterface $bus): Response + { + $bus->dispatch(new LuckyNumberNotification(101)); + + return new Response("101"); + } +} diff --git a/tests/Frameworks/Symfony/Version_4_4/src/Message/LuckyNumberNotification.php b/tests/Frameworks/Symfony/Version_4_4/src/Message/LuckyNumberNotification.php new file mode 100644 index 0000000000..2912d401cb --- /dev/null +++ b/tests/Frameworks/Symfony/Version_4_4/src/Message/LuckyNumberNotification.php @@ -0,0 +1,12 @@ +content = $content; + } +} diff --git a/tests/Frameworks/Symfony/Version_4_4/src/MessageHandler/LuckyNumberNotificationHandler.php b/tests/Frameworks/Symfony/Version_4_4/src/MessageHandler/LuckyNumberNotificationHandler.php new file mode 100644 index 0000000000..b4d99444d1 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_4_4/src/MessageHandler/LuckyNumberNotificationHandler.php @@ -0,0 +1,19 @@ +content > 100 || $message->content < 0) { + throw new UnrecoverableMessageHandlingException("Number is out of bounds"); + } + + echo "Received number: {$message->content}\n"; + } +} diff --git a/tests/Frameworks/Symfony/Version_5_2/.env b/tests/Frameworks/Symfony/Version_5_2/.env index 67da29e1ef..6c79bc6b83 100644 --- a/tests/Frameworks/Symfony/Version_5_2/.env +++ b/tests/Frameworks/Symfony/Version_5_2/.env @@ -16,4 +16,19 @@ ###> symfony/framework-bundle ### APP_ENV=dev APP_SECRET=c8db676eda45ca0ed2cd13c0df87072e -###< symfony/framework-bundle ### \ No newline at end of file +###< symfony/framework-bundle ### +###> doctrine/doctrine-bundle ### +# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url +# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml +# +# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" +# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8&charset=utf8mb4" +DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" +###< doctrine/doctrine-bundle ### + +###> symfony/messenger ### +# Choose one of the transports below +# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages +# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages +MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 +###< symfony/messenger ### diff --git a/tests/Frameworks/Symfony/Version_5_2/composer.json b/tests/Frameworks/Symfony/Version_5_2/composer.json index 21be2382a9..13b3edcc34 100644 --- a/tests/Frameworks/Symfony/Version_5_2/composer.json +++ b/tests/Frameworks/Symfony/Version_5_2/composer.json @@ -17,7 +17,9 @@ "symfony/flex": "^1.3.1", "symfony/form": "5.2.*", "symfony/framework-bundle": "5.2.*", + "symfony/messenger": "5.2.*", "symfony/security-bundle": "5.2.*", + "symfony/serializer": "5.2.*", "symfony/twig-bundle": "5.2.*", "symfony/validator": "5.2.*", "symfony/yaml": "5.2.*" diff --git a/tests/Frameworks/Symfony/Version_5_2/config/packages/messenger.yaml b/tests/Frameworks/Symfony/Version_5_2/config/packages/messenger.yaml new file mode 100644 index 0000000000..270670b194 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_5_2/config/packages/messenger.yaml @@ -0,0 +1,18 @@ +framework: + messenger: + # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. + failure_transport: failed + + transports: + # https://symfony.com/doc/current/messenger.html#transport-configuration + async: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%' + retry_strategy: + max_retries: 0 + failed: 'doctrine://default?queue_name=failed' + # sync: 'sync://' + + routing: + # Route your messages to the transports + # 'App\Message\YourMessage': async + 'App\Message\LuckyNumberNotification': async diff --git a/tests/Frameworks/Symfony/Version_5_2/migrations/Version20240712130658.php b/tests/Frameworks/Symfony/Version_5_2/migrations/Version20240712130658.php new file mode 100644 index 0000000000..f51ca99c1b --- /dev/null +++ b/tests/Frameworks/Symfony/Version_5_2/migrations/Version20240712130658.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE messenger_messages'); + } +} diff --git a/tests/Frameworks/Symfony/Version_5_2/src/Controller/LuckyController.php b/tests/Frameworks/Symfony/Version_5_2/src/Controller/LuckyController.php new file mode 100644 index 0000000000..3637825701 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_5_2/src/Controller/LuckyController.php @@ -0,0 +1,34 @@ +dispatch(new LuckyNumberNotification($number)); + + return new Response("$number"); + } + + /** + * @Route("/lucky/fail", name="lucky_fail") + + */ + public function fail(MessageBusInterface $bus): Response + { + $bus->dispatch(new LuckyNumberNotification(101)); + + return new Response("101"); + } +} diff --git a/tests/Frameworks/Symfony/Version_5_2/src/Message/LuckyNumberNotification.php b/tests/Frameworks/Symfony/Version_5_2/src/Message/LuckyNumberNotification.php new file mode 100644 index 0000000000..2912d401cb --- /dev/null +++ b/tests/Frameworks/Symfony/Version_5_2/src/Message/LuckyNumberNotification.php @@ -0,0 +1,12 @@ +content = $content; + } +} diff --git a/tests/Frameworks/Symfony/Version_5_2/src/MessageHandler/LuckyNumberNotificationHandler.php b/tests/Frameworks/Symfony/Version_5_2/src/MessageHandler/LuckyNumberNotificationHandler.php new file mode 100644 index 0000000000..b4d99444d1 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_5_2/src/MessageHandler/LuckyNumberNotificationHandler.php @@ -0,0 +1,19 @@ +content > 100 || $message->content < 0) { + throw new UnrecoverableMessageHandlingException("Number is out of bounds"); + } + + echo "Received number: {$message->content}\n"; + } +} diff --git a/tests/Frameworks/Symfony/Version_6_2/.env b/tests/Frameworks/Symfony/Version_6_2/.env index 0fb400362e..798c34092b 100644 --- a/tests/Frameworks/Symfony/Version_6_2/.env +++ b/tests/Frameworks/Symfony/Version_6_2/.env @@ -27,3 +27,10 @@ APP_SECRET=c8db676eda45ca0ed2cd13c0df87072e # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8" ###< doctrine/doctrine-bundle ### + +###> symfony/messenger ### +# Choose one of the transports below +# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages +#MESSENGER_TRANSPORT_DSN=redis://redis_integration:6379/messages +MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 +###< symfony/messenger ### diff --git a/tests/Frameworks/Symfony/Version_6_2/composer.json b/tests/Frameworks/Symfony/Version_6_2/composer.json index aeaa94cd72..3772cf2aa1 100644 --- a/tests/Frameworks/Symfony/Version_6_2/composer.json +++ b/tests/Frameworks/Symfony/Version_6_2/composer.json @@ -12,10 +12,12 @@ "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.15", "symfony/console": "6.2.*", + "symfony/doctrine-messenger": "6.2.*", "symfony/dotenv": "6.2.*", "symfony/flex": "^1.17.1", "symfony/form": "6.2.*", "symfony/framework-bundle": "6.2.*", + "symfony/messenger": "^6.2", "symfony/monolog-bundle": "^3.8", "symfony/security-bundle": "6.2.*", "symfony/twig-bundle": "6.2.*", diff --git a/tests/Frameworks/Symfony/Version_6_2/config/packages/messenger.yaml b/tests/Frameworks/Symfony/Version_6_2/config/packages/messenger.yaml new file mode 100644 index 0000000000..460d08674a --- /dev/null +++ b/tests/Frameworks/Symfony/Version_6_2/config/packages/messenger.yaml @@ -0,0 +1,24 @@ +framework: + messenger: + # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. + # failure_transport: failed + + transports: + # https://symfony.com/doc/current/messenger.html#transport-configuration + async: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%' + failed: 'doctrine://default?queue_name=failed' + # sync: 'sync://' + + routing: + # Route your messages to the transports + # 'App\Message\YourMessage': async + 'App\Message\LuckyNumberNotification': async + +# when@test: +# framework: +# messenger: +# transports: +# # replace with your transport name here (e.g., my_transport: 'in-memory://') +# # For more Messenger testing tools, see https://github.com/zenstruck/messenger-test +# async: 'in-memory://' diff --git a/tests/Frameworks/Symfony/Version_6_2/migrations/Version20240822083531.php b/tests/Frameworks/Symfony/Version_6_2/migrations/Version20240822083531.php new file mode 100644 index 0000000000..e1cfd5eb83 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_6_2/migrations/Version20240822083531.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE messenger_messages'); + } +} diff --git a/tests/Frameworks/Symfony/Version_6_2/src/Controller/LuckyController.php b/tests/Frameworks/Symfony/Version_6_2/src/Controller/LuckyController.php new file mode 100644 index 0000000000..f82efd38cd --- /dev/null +++ b/tests/Frameworks/Symfony/Version_6_2/src/Controller/LuckyController.php @@ -0,0 +1,29 @@ +dispatch(new LuckyNumberNotification($number)); + + return new Response("$number"); + } + + #[Route('/lucky/fail', 'lucky_fail')] + public function fail(MessageBusInterface $bus): Response + { + $bus->dispatch(new LuckyNumberNotification(101)); + + return new Response("101"); + } +} diff --git a/tests/Frameworks/Symfony/Version_6_2/src/Message/LuckyNumberNotification.php b/tests/Frameworks/Symfony/Version_6_2/src/Message/LuckyNumberNotification.php new file mode 100644 index 0000000000..93c28e3527 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_6_2/src/Message/LuckyNumberNotification.php @@ -0,0 +1,8 @@ +content > 100 || $message->content < 0) { + throw new UnrecoverableMessageHandlingException("Number is out of bounds"); + } + + echo "Received number: {$message->content}\n"; + } +} diff --git a/tests/Frameworks/Symfony/Version_7_0/.env b/tests/Frameworks/Symfony/Version_7_0/.env index 4dbed61591..4c4d4e3ac6 100644 --- a/tests/Frameworks/Symfony/Version_7_0/.env +++ b/tests/Frameworks/Symfony/Version_7_0/.env @@ -28,3 +28,10 @@ APP_SECRET=7b46ee8a78f39224283035fe148d0a79 # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8" ###< doctrine/doctrine-bundle ### + +###> symfony/messenger ### +# Choose one of the transports below +# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages +# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages +MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 +###< symfony/messenger ### diff --git a/tests/Frameworks/Symfony/Version_7_0/composer.json b/tests/Frameworks/Symfony/Version_7_0/composer.json index 0f8033def5..539329c62b 100644 --- a/tests/Frameworks/Symfony/Version_7_0/composer.json +++ b/tests/Frameworks/Symfony/Version_7_0/composer.json @@ -12,10 +12,12 @@ "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^2.17", "symfony/console": "7.0.*", + "symfony/doctrine-messenger": "^7.1", "symfony/dotenv": "7.0.*", "symfony/flex": "^2", "symfony/form": "7.0.*", "symfony/framework-bundle": "7.0.*", + "symfony/messenger": "^7.1", "symfony/monolog-bundle": "^3.10", "symfony/runtime": "7.0.*", "symfony/security-bundle": "7.0.*", diff --git a/tests/Frameworks/Symfony/Version_7_0/config/packages/messenger.yaml b/tests/Frameworks/Symfony/Version_7_0/config/packages/messenger.yaml new file mode 100644 index 0000000000..74adb8c6ff --- /dev/null +++ b/tests/Frameworks/Symfony/Version_7_0/config/packages/messenger.yaml @@ -0,0 +1,28 @@ +framework: + messenger: + # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. + failure_transport: failed + + transports: + # https://symfony.com/doc/current/messenger.html#transport-configuration + async: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%' + options: + queue_name: high + retry_strategy: + max_retries: 0 + failed: 'doctrine://default?queue_name=failed' + sync: 'sync://' + + routing: + # Route your messages to the transports + 'App\Message\LuckyNumberNotification': async + + +# when@test: +# framework: +# messenger: +# transports: +# # replace with your transport name here (e.g., my_transport: 'in-memory://') +# # For more Messenger testing tools, see https://github.com/zenstruck/messenger-test +# async: 'in-memory://' diff --git a/tests/Frameworks/Symfony/Version_7_0/config/routes/security.yaml b/tests/Frameworks/Symfony/Version_7_0/config/routes/security.yaml new file mode 100644 index 0000000000..f853be15cf --- /dev/null +++ b/tests/Frameworks/Symfony/Version_7_0/config/routes/security.yaml @@ -0,0 +1,3 @@ +_security_logout: + resource: security.route_loader.logout + type: service diff --git a/tests/Frameworks/Symfony/Version_7_0/migrations/Version20240711115224.php b/tests/Frameworks/Symfony/Version_7_0/migrations/Version20240711115224.php new file mode 100644 index 0000000000..b8145fbb96 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_7_0/migrations/Version20240711115224.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', available_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', delivered_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE messenger_messages'); + } +} diff --git a/tests/Frameworks/Symfony/Version_7_0/src/Controller/LuckyController.php b/tests/Frameworks/Symfony/Version_7_0/src/Controller/LuckyController.php new file mode 100644 index 0000000000..f82efd38cd --- /dev/null +++ b/tests/Frameworks/Symfony/Version_7_0/src/Controller/LuckyController.php @@ -0,0 +1,29 @@ +dispatch(new LuckyNumberNotification($number)); + + return new Response("$number"); + } + + #[Route('/lucky/fail', 'lucky_fail')] + public function fail(MessageBusInterface $bus): Response + { + $bus->dispatch(new LuckyNumberNotification(101)); + + return new Response("101"); + } +} diff --git a/tests/Frameworks/Symfony/Version_7_0/src/Message/LuckyNumberNotification.php b/tests/Frameworks/Symfony/Version_7_0/src/Message/LuckyNumberNotification.php new file mode 100644 index 0000000000..93c28e3527 --- /dev/null +++ b/tests/Frameworks/Symfony/Version_7_0/src/Message/LuckyNumberNotification.php @@ -0,0 +1,8 @@ +content > 100 || $message->content < 0) { + throw new \OutOfBoundsException("Number is out of bounds"); + } + + echo "Received number: {$message->content}\n"; + } +} diff --git a/tests/Integrations/Symfony/V4_4/MessengerTest.php b/tests/Integrations/Symfony/V4_4/MessengerTest.php new file mode 100644 index 0000000000..dc387c2169 --- /dev/null +++ b/tests/Integrations/Symfony/V4_4/MessengerTest.php @@ -0,0 +1,88 @@ + '1', + 'DD_TRACE_CLI_ENABLED' => '1', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_DEBUG' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_PHPREDIS_ENABLED' => 'false' // We are NOT testing the phpredis integration + ]); + } + + public function testAsyncSuccess() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky number', '/lucky/number'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_DEBUG' => 'true', + ], [], ['messenger:consume', 'async', '--limit=1']); + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v4_4.messenger_test.test_async_success_consumer', + true + ); + } + + public function testAsyncFailure() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky fail', '/lucky/fail'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + ], [], ['messenger:consume', 'async', '--limit=1']); + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v4_4.messenger_test.test_async_failure_consumer', + true + ); + } +} diff --git a/tests/Integrations/Symfony/V5_2/MessengerTest.php b/tests/Integrations/Symfony/V5_2/MessengerTest.php new file mode 100644 index 0000000000..814be69070 --- /dev/null +++ b/tests/Integrations/Symfony/V5_2/MessengerTest.php @@ -0,0 +1,89 @@ + '1', + 'DD_TRACE_CLI_ENABLED' => '1', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_DEBUG' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_PHPREDIS_ENABLED' => 'false' // We are NOT testing the phpredis integration + ]); + } + + public function testAsyncSuccess() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky number', '/lucky/number'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_DEBUG' => 'true', + ], [], ['messenger:consume', 'async', '--limit=1']); + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v5_2.messenger_test.test_async_success_consumer', + true + ); + } + + public function testAsyncFailure() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky fail', '/lucky/fail'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + ], [], ['messenger:consume', 'async', '--limit=1']); + + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v5_2.messenger_test.test_async_failure_consumer', + true + ); + } +} diff --git a/tests/Integrations/Symfony/V6_2/MessengerTest.php b/tests/Integrations/Symfony/V6_2/MessengerTest.php new file mode 100644 index 0000000000..bc801867ec --- /dev/null +++ b/tests/Integrations/Symfony/V6_2/MessengerTest.php @@ -0,0 +1,90 @@ + '1', + 'DD_TRACE_CLI_ENABLED' => '1', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_DEBUG' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_PHPREDIS_ENABLED' => 'false' // We are NOT testing the phpredis integration + ]); + } + + public function testAsyncSuccess() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky number', '/lucky/number'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_DEBUG' => 'true', + 'DD_TRACE_PHPREDIS_ENABLED' => 'false' // We are NOT testing the phpredis integration + ], [], ['mess:cons', 'async', '--limit=1']); + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v6_2.messenger_test.test_async_success_consumer', + true + ); + } + + public function testAsyncFailure() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky fail', '/lucky/fail'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_PHPREDIS_ENABLED' => 'false' // We are NOT testing the phpredis integration + ], [], ['messenger:consume', 'async', '--limit=1']); + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v6_2.messenger_test.test_async_failure_consumer', + true + ); + } +} diff --git a/tests/Integrations/Symfony/V7_0/MessengerTest.php b/tests/Integrations/Symfony/V7_0/MessengerTest.php new file mode 100644 index 0000000000..839c2339e5 --- /dev/null +++ b/tests/Integrations/Symfony/V7_0/MessengerTest.php @@ -0,0 +1,87 @@ + '1', + 'DD_TRACE_CLI_ENABLED' => '1', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_DEBUG' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + ]); + } + + public function testAsyncSuccess() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky number', '/lucky/number'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + 'DD_TRACE_DEBUG' => 'true', + ], [], ['messenger:consume', 'async', '--limit=1']); + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v7_0.messenger_test.test_async_success_consumer', + true + ); + } + + public function testAsyncFailure() + { + $this->tracesFromWebRequestSnapshot(function () { + $spec = GetSpec::create('Lucky fail', '/lucky/fail'); + $this->call($spec); + }, self::FIELDS_TO_IGNORE); + + list($consumerTraces) = $this->inCli(self::getConsoleScript(), [ + 'DD_TRACE_CLI_ENABLED' => 'true', + 'DD_TRACE_EXEC_ENABLED' => 'false', + 'DD_SERVICE' => 'symfony_messenger_test', + 'DD_TRACE_REMOVE_AUTOINSTRUMENTATION_ORPHANS' => 'true', + 'DD_TRACE_SYMFONY_MESSENGER_MIDDLEWARES' => 'true', + ], [], ['messenger:consume', 'async', '--limit=1']); + + $this->snapshotFromTraces( + $consumerTraces, + self::FIELDS_TO_IGNORE, + 'tests.integrations.symfony.v7_0.messenger_test.test_async_failure_consumer', + true + ); + } +} diff --git a/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure.json b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure.json new file mode 100644 index 0000000000..3acc322d5a --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure.json @@ -0,0 +1,382 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_fail", + "trace_id": 0, + "span_id": 1, + "parent_id": 7459318832667968177, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "669786ad00000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/fail", + "runtime-id": "f776fad2-f39f-4b4f-81e6-9faafebfaaee", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@fail", + "symfony.route.name": "lucky_fail" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::fail", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 20, + "parent_id": 19, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 21, + "parent_id": 19, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 22, + "parent_id": 21, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 21, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 24, + "parent_id": 21, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.security.authentication.success", + "service": "symfony_messenger_test", + "resource": "symfony.security.authentication.success", + "trace_id": 0, + "span_id": 13, + "parent_id": 10, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure_consumer.json b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure_consumer.json new file mode 100644 index 0000000000..f5d5703f3f --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_failure_consumer.json @@ -0,0 +1,312 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 3836038585860194322, + "type": "queue", + "error": 1, + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "669786ad00000000", + "component": "symfonymessenger", + "error.message": "Uncaught Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/src/MessageHandler/LuckyNumberNotificationHandler.php:14", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(65): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#20 {main}\n\nNext Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82\nStack trace:\n#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException", + "messaging.destination": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "898812cf-eb15-4cac-a04e-fcd31464a1bc", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineReceivedStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 8, + "parent_id": 7, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 8, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 10, + "parent_id": 9, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 11, + "parent_id": 10, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 12, + "parent_id": 11, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:82", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/src/MessageHandler/LuckyNumberNotificationHandler.php:14", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(65): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(79): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(67): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(42): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/MessageBus.php(80): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/TraceableMessageBus.php(41): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\TraceableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(119): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Worker.php(82): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(211): Symfony\\Component\\Messenger\\Worker->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Command/Command.php(255): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(1039): Symfony\\Component\\Console\\Command\\Command->run()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(97): Symfony\\Component\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(275): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/framework-bundle/Console/Application.php(83): Symfony\\Component\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/vendor/symfony/console/Application.php(149): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_4_4/bin/console(42): Symfony\\Component\\Console\\Application->run()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success.json b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success.json new file mode 100644 index 0000000000..00c70e16f6 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success.json @@ -0,0 +1,382 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_number", + "trace_id": 0, + "span_id": 1, + "parent_id": 5696153891440035761, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "6697868300000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/number", + "runtime-id": "f776fad2-f39f-4b4f-81e6-9faafebfaaee", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@number", + "symfony.route.name": "lucky_number" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::number", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 20, + "parent_id": 19, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 21, + "parent_id": 19, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 22, + "parent_id": 21, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 21, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 24, + "parent_id": 21, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.security.authentication.success", + "service": "symfony_messenger_test", + "resource": "symfony.security.authentication.success", + "trace_id": 0, + "span_id": 13, + "parent_id": 10, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success_consumer.json b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success_consumer.json new file mode 100644 index 0000000000..3477157027 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v4_4.messenger_test.test_async_success_consumer.json @@ -0,0 +1,294 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 9153271162721253303, + "type": "queue", + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "6697868300000000", + "component": "symfonymessenger", + "messaging.destination": "Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineTransport", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "8242009e-441e-484d-b32b-0ababcffc2ae", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\HandledStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Transport\\Doctrine\\DoctrineReceivedStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 8, + "parent_id": 7, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 8, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 10, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 11, + "parent_id": 10, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 12, + "parent_id": 11, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\StackMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony44", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure.json b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure.json new file mode 100644 index 0000000000..0b776583b6 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure.json @@ -0,0 +1,370 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_fail", + "trace_id": 0, + "span_id": 1, + "parent_id": 17193270540770397716, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "6696288c00000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/fail", + "runtime-id": "1705ac20-fbbf-4e9f-b538-5524ce6a6530", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@fail", + "symfony.route.name": "lucky_fail" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::fail", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 20, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 21, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 22, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure_consumer.json b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure_consumer.json new file mode 100644 index 0000000000..be3ba5ffcc --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_failure_consumer.json @@ -0,0 +1,390 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 11493428454241304998, + "type": "queue", + "error": 1, + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "66966bf000000000", + "component": "symfonymessenger", + "error.message": "Uncaught Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/src/MessageHandler/LuckyNumberNotificationHandler.php:14", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(63): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#19 {main}\n\nNext Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80\nStack trace:\n#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException", + "messaging.destination": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "75c5d263-021f-463a-a643-473dd0d1066c", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 7, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:80", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/src/MessageHandler/LuckyNumberNotificationHandler.php:14", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(63): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(74): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/MessageBus.php(77): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(114): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Worker.php(77): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(197): Symfony\\Component\\Messenger\\Worker->run()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Command/Command.php(256): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(989): Symfony\\Component\\Console\\Command\\Command->run()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(96): Symfony\\Component\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(290): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/framework-bundle/Console/Application.php(82): Symfony\\Component\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/vendor/symfony/console/Application.php(166): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_5_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#19 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 8, + "parent_id": 4, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "3", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.redelivered_at": "2024-07-16T12:47:55+00:00", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\DelayStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ErrorDetailsStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\RedeliveryStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentToFailureTransportStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 2 + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 10, + "parent_id": 8, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 11, + "parent_id": 8, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "DELETE FROM messenger_messages WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "DELETE FROM messenger_messages WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success.json b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success.json new file mode 100644 index 0000000000..cb40b1ceb0 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success.json @@ -0,0 +1,370 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_number", + "trace_id": 0, + "span_id": 1, + "parent_id": 10088477474525139052, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "6696287200000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/number", + "runtime-id": "1705ac20-fbbf-4e9f-b538-5524ce6a6530", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@number", + "symfony.route.name": "lucky_number" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::number", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 20, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 21, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 22, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success_consumer.json b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success_consumer.json new file mode 100644 index 0000000000..d72b62ce4d --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v5_2.messenger_test.test_async_success_consumer.json @@ -0,0 +1,294 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 4317723020732676646, + "type": "queue", + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "66966bd500000000", + "component": "symfonymessenger", + "messaging.destination": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "4a7cf583-c1ad-4d0d-84a0-61badf8e3764", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\HandledStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 8, + "parent_id": 7, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 8, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 10, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 11, + "parent_id": 10, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 12, + "parent_id": 11, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\StackMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "DELETE FROM messenger_messages WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "DELETE FROM messenger_messages WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony52", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure.json b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure.json new file mode 100644 index 0000000000..6d321a309c --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure.json @@ -0,0 +1,370 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_fail", + "trace_id": 0, + "span_id": 1, + "parent_id": 4742951813910861142, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66c6f8e000000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/fail", + "runtime-id": "d07cb84d-0e78-4f60-bbe6-25954de69a8a", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@fail", + "symfony.route.name": "lucky_fail" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::fail", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 20, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 21, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 22, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure_consumer.json b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure_consumer.json new file mode 100644 index 0000000000..c157937452 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_failure_consumer.json @@ -0,0 +1,313 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 17504473594943785929, + "type": "queue", + "error": 1, + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "66c6f8e000000000", + "component": "symfonymessenger", + "error.message": "Uncaught Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/src/MessageHandler/LuckyNumberNotificationHandler.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(157): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(96): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->callHandler()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#18 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#19 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#20 {main}\n\nNext Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129\nStack trace:\n#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "7421040e-f494-41ed-bd29-c4e60aa1d4e3", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\AckStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 8, + "parent_id": 7, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 8, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 10, + "parent_id": 9, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 11, + "parent_id": 10, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 12, + "parent_id": 11, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:129", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#18 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException: Number is out of bounds in /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/src/MessageHandler/LuckyNumberNotificationHandler.php:15", + "error.stack": "#0 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(157): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(96): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->callHandler()\n#2 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(77): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#3 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#4 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#5 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#6 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#7 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(40): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#8 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#9 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#10 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(161): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#11 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Worker.php(110): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#12 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(229): Symfony\\Component\\Messenger\\Worker->run()\n#13 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Command/Command.php(312): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#14 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(1040): Symfony\\Component\\Console\\Command\\Command->run()\n#15 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(88): Symfony\\Component\\Console\\Application->doRunCommand()\n#16 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(314): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#17 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/framework-bundle/Console/Application.php(77): Symfony\\Component\\Console\\Application->doRun()\n#18 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/vendor/symfony/console/Application.php(168): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#19 /home/circleci/app/tests/Frameworks/Symfony/Version_6_2/bin/console(43): Symfony\\Component\\Console\\Application->run()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\UnrecoverableMessageHandlingException", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success.json b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success.json new file mode 100644 index 0000000000..8756997fe9 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success.json @@ -0,0 +1,370 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_number", + "trace_id": 0, + "span_id": 1, + "parent_id": 1821185153506234818, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66c6f8cf00000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/number", + "runtime-id": "d07cb84d-0e78-4f60-bbe6-25954de69a8a", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@number", + "symfony.route.name": "lucky_number" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::number", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 20, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 21, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 22, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success_consumer.json b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success_consumer.json new file mode 100644 index 0000000000..c87d201249 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v6_2.messenger_test.test_async_success_consumer.json @@ -0,0 +1,295 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 2618824753639377523, + "type": "queue", + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "66c6f8cf00000000", + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "3f23a876-79a6-4ac2-b1e9-5e25bed8870f", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\AckStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\HandledStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 8, + "parent_id": 7, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 8, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 10, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 11, + "parent_id": 10, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 12, + "parent_id": 11, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\StackMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony62", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure.json b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure.json new file mode 100644 index 0000000000..cb4b9bcd71 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure.json @@ -0,0 +1,391 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_fail", + "trace_id": 0, + "span_id": 1, + "parent_id": 8370649148870068342, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66962c1d00000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/fail", + "runtime-id": "b0f51f16-e8af-4b82-9b0b-9c9ee45b6023", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@fail", + "symfony.route.name": "lucky_fail" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::fail", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 20, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 21, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 22, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "PDO.commit", + "service": "pdo", + "resource": "PDO.commit", + "trace_id": 0, + "span_id": 24, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure_consumer.json b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure_consumer.json new file mode 100644 index 0000000000..d619b31782 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_failure_consumer.json @@ -0,0 +1,413 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 17593395603432814175, + "type": "queue", + "error": 1, + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "66962c1d00000000", + "component": "symfonymessenger", + "error.message": "Uncaught OutOfBoundsException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/src/MessageHandler/LuckyNumberNotificationHandler.php:14", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(152): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(91): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->callHandler()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#20 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#21 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#22 {main}\n\nNext Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124\nStack trace:\n#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "OutOfBoundsException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "69f83ee5-2540-4254-8e9e-06ccaa49330e", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\AckStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 7, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 9, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown Symfony\\Component\\Messenger\\Exception\\HandlerFailedException: Handling \"App\\Message\\LuckyNumberNotification\" failed: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:124", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#20 {main}", + "error.type": "Symfony\\Component\\Messenger\\Exception\\HandlerFailedException", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "2", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "error": 1, + "meta": { + "component": "symfonymessenger", + "error.message": "Thrown OutOfBoundsException: Number is out of bounds in /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/src/MessageHandler/LuckyNumberNotificationHandler.php:14", + "error.stack": "#0 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(152): App\\MessageHandler\\LuckyNumberNotificationHandler->__invoke()\n#1 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(91): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->callHandler()\n#2 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(71): Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware->handle()\n#3 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware->handle()\n#4 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware->handle()\n#5 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(41): Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#6 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(35): Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#7 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Middleware/TraceableMiddleware.php(36): Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware->handle()\n#8 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/MessageBus.php(70): Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware->handle()\n#9 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/RoutableMessageBus.php(54): Symfony\\Component\\Messenger\\MessageBus->dispatch()\n#10 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(162): Symfony\\Component\\Messenger\\RoutableMessageBus->dispatch()\n#11 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Worker.php(109): Symfony\\Component\\Messenger\\Worker->handleMessage()\n#12 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php(244): Symfony\\Component\\Messenger\\Worker->run()\n#13 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Command/Command.php(279): Symfony\\Component\\Messenger\\Command\\ConsumeMessagesCommand->execute()\n#14 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(1049): Symfony\\Component\\Console\\Command\\Command->run()\n#15 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(125): Symfony\\Component\\Console\\Application->doRunCommand()\n#16 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(318): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRunCommand()\n#17 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/framework-bundle/Console/Application.php(79): Symfony\\Component\\Console\\Application->doRun()\n#18 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/console/Application.php(169): Symfony\\Bundle\\FrameworkBundle\\Console\\Application->doRun()\n#19 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php(49): Symfony\\Component\\Console\\Application->run()\n#20 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/vendor/autoload_runtime.php(29): Symfony\\Component\\Runtime\\Runner\\Symfony\\ConsoleApplicationRunner->run()\n#21 /home/circleci/datadog/tests/Frameworks/Symfony/Version_7_0/bin/console(11): require_once()\n#22 {main}", + "error.type": "OutOfBoundsException", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageFailedEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 8, + "parent_id": 4, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "3", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.redelivered_at": "2024-07-16T08:15:31+00:00", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\AckStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\DelayStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ErrorDetailsStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\RedeliveryStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentToFailureTransportStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 2 + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 10, + "parent_id": 8, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 11, + "parent_id": 8, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }, + { + "name": "PDO.commit", + "service": "pdo", + "resource": "PDO.commit", + "trace_id": 0, + "span_id": 12, + "parent_id": 8, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success.json b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success.json new file mode 100644 index 0000000000..9831de4280 --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success.json @@ -0,0 +1,391 @@ +[[ + { + "name": "symfony.request", + "service": "symfony_messenger_test", + "resource": "lucky_number", + "trace_id": 0, + "span_id": 1, + "parent_id": 1531364475549898567, + "type": "web", + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66962c0800000000", + "component": "symfony", + "http.method": "GET", + "http.status_code": "200", + "http.url": "http://localhost/lucky/number", + "runtime-id": "b0f51f16-e8af-4b82-9b0b-9c9ee45b6023", + "span.kind": "server", + "symfony.route.action": "App\\Controller\\LuckyController@number", + "symfony.route.name": "lucky_number" + }, + "metrics": { + "_sampling_priority_v1": 1.0 + } + }, + { + "name": "symfony.httpkernel.kernel.handle", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony", + "span.kind": "server" + } + }, + { + "name": "symfony.httpkernel.kernel.boot", + "service": "symfony_messenger_test", + "resource": "App\\Kernel", + "trace_id": 0, + "span_id": 4, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.handle", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.handle", + "trace_id": 0, + "span_id": 5, + "parent_id": 2, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.request", + "trace_id": 0, + "span_id": 6, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller", + "trace_id": 0, + "span_id": 7, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.controller_arguments", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.controller_arguments", + "trace_id": 0, + "span_id": 8, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.controller", + "service": "symfony_messenger_test", + "resource": "App\\Controller\\LuckyController::number", + "trace_id": 0, + "span_id": 9, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 12, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 14, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 16, + "parent_id": 15, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 17, + "parent_id": 16, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 18, + "parent_id": 17, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\SendMessageToTransportsEvent", + "trace_id": 0, + "span_id": 19, + "parent_id": 18, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.send", + "service": "symfony_messenger_test", + "resource": "App\\Message\\LuckyNumberNotification -> async", + "trace_id": 0, + "span_id": 20, + "parent_id": 18, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "send", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.symfony.sender": "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineTransport", + "messaging.system": "symfony", + "span.kind": "producer" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\SentStamp": 1.0, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1.0 + } + }, + { + "name": "PDO.__construct", + "service": "pdo", + "resource": "PDO.__construct", + "trace_id": 0, + "span_id": 21, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 22, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "INSERT INTO messenger_messages (body, headers, queue_name, created_at, available_at) VALUES(?, ?, ?, ?, ?)", + "trace_id": 0, + "span_id": 23, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1.0 + } + }, + { + "name": "PDO.commit", + "service": "pdo", + "resource": "PDO.commit", + "trace_id": 0, + "span_id": 24, + "parent_id": 20, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "symfony.kernel.response", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.response", + "trace_id": 0, + "span_id": 10, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.finish_request", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.finish_request", + "trace_id": 0, + "span_id": 11, + "parent_id": 5, + "type": "web", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.kernel.terminate", + "service": "symfony_messenger_test", + "resource": "symfony.kernel.terminate", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "web", + "meta": { + "component": "symfony" + } + }]] diff --git a/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success_consumer.json b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success_consumer.json new file mode 100644 index 0000000000..5a3d171b3b --- /dev/null +++ b/tests/snapshots/tests.integrations.symfony.v7_0.messenger_test.test_async_success_consumer.json @@ -0,0 +1,295 @@ +[[ + { + "name": "symfony.messenger.consume", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 1, + "parent_id": 15105530756925232482, + "type": "queue", + "meta": { + "_dd.p.dm": "0", + "_dd.p.tid": "66962c0800000000", + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "receive", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony", + "runtime-id": "183d43c7-590d-426c-bb3d-719483290c51", + "span.kind": "consumer" + }, + "metrics": { + "_sampling_priority_v1": 1 + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageReceivedEvent", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "symfony.messenger.dispatch", + "service": "symfony_messenger_test", + "resource": "async -> App\\Message\\LuckyNumberNotification", + "trace_id": 0, + "span_id": 3, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + }, + "metrics": { + "messaging.symfony.stamps.DDTrace\\Integrations\\SymfonyMessenger\\DDTraceStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Bridge\\Doctrine\\Transport\\DoctrineReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\AckStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\BusNameStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ConsumedByWorkerStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\HandledStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\ReceivedStamp": 1, + "messaging.symfony.stamps.Symfony\\Component\\Messenger\\Stamp\\TransportMessageIdStamp": 1 + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\TraceableMiddleware", + "trace_id": 0, + "span_id": 7, + "parent_id": 3, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\AddBusNameStampMiddleware", + "trace_id": 0, + "span_id": 8, + "parent_id": 7, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\RejectRedeliveredMessageMiddleware", + "trace_id": 0, + "span_id": 9, + "parent_id": 8, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\DispatchAfterCurrentBusMiddleware", + "trace_id": 0, + "span_id": 10, + "parent_id": 9, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\FailedMessageProcessingMiddleware", + "trace_id": 0, + "span_id": 11, + "parent_id": 10, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware", + "trace_id": 0, + "span_id": 12, + "parent_id": 11, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware", + "trace_id": 0, + "span_id": 13, + "parent_id": 12, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.handle", + "service": "symfony_messenger_test", + "resource": "App\\MessageHandler\\LuckyNumberNotificationHandler", + "trace_id": 0, + "span_id": 14, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination_kind": "queue", + "messaging.operation": "process", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.messenger.middleware", + "service": "symfony_messenger_test", + "resource": "Symfony\\Component\\Messenger\\Middleware\\StackMiddleware", + "trace_id": 0, + "span_id": 15, + "parent_id": 13, + "type": "queue", + "meta": { + "component": "symfonymessenger", + "messaging.destination": "async", + "messaging.destination_kind": "queue", + "messaging.message_id": "1", + "messaging.operation": "process", + "messaging.symfony.bus": "messenger.bus.default", + "messaging.symfony.handler": "App\\MessageHandler\\LuckyNumberNotificationHandler::__invoke", + "messaging.symfony.message": "App\\Message\\LuckyNumberNotification", + "messaging.system": "symfony" + } + }, + { + "name": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "service": "symfony_messenger_test", + "resource": "symfony.Symfony\\Component\\Messenger\\Event\\WorkerMessageHandledEvent", + "trace_id": 0, + "span_id": 4, + "parent_id": 1, + "type": "queue", + "meta": { + "component": "symfony" + } + }, + { + "name": "PDO.prepare", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 5, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + } + }, + { + "name": "PDOStatement.execute", + "service": "pdo", + "resource": "UPDATE messenger_messages SET delivered_at = ? WHERE id = ?", + "trace_id": 0, + "span_id": 6, + "parent_id": 1, + "type": "sql", + "meta": { + "_dd.base_service": "symfony_messenger_test", + "component": "pdo", + "db.charset": "utf8mb4", + "db.engine": "mysql", + "db.name": "symfony70", + "db.system": "mysql", + "db.user": "test", + "out.host": "mysql_integration", + "out.port": "3306", + "span.kind": "client" + }, + "metrics": { + "db.row_count": 1 + } + }]] From 5f1390b21a11fb710819c950c91ebed9bc2068ee Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 22 Aug 2024 14:56:28 +0200 Subject: [PATCH 036/103] Fix verify_centos CI jobs + remove unused job verify_centos_6 + fix plesk install test (#2804) * Fix install of the epel repo as the URL is dead * Remove unused job verify_centos_6 * Looks like plesk replaced mysql with mariadb --- .circleci/continue_config.yml | 65 +++---------------- dockerfiles/verify_packages/Makefile | 3 - dockerfiles/verify_packages/centos/install.sh | 2 +- .../installer/test_install_on_plesk.sh | 2 +- 4 files changed, 10 insertions(+), 62 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index aa9362a979..806bd0ada9 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -296,16 +296,16 @@ commands: shell: "<<# parameters.is_windows >>bash.exe<>" command: | git config --global gc.auto 0 - + export CIRCLE_REPOSITORY_URL="${CIRCLE_REPOSITORY_URL/git@github.com:/https://github.com/}" - + APP_DIR="$HOME/datadog" if [ -e "$APP_DIR/.git" ] ; then echo 'Fetching into existing repository' existing_repo='true' cd "$APP_DIR" git remote set-url origin "$CIRCLE_REPOSITORY_URL" || true - + echo 'Fetching from remote repository' retry_count=3 if [ -n "$CIRCLE_TAG" ]; then @@ -324,7 +324,7 @@ commands: cd "$APP_DIR" git clone --no-checkout "$CIRCLE_REPOSITORY_URL" . fi - + if [ -n "$CIRCLE_TAG" ]; then echo 'Checking out tag' git checkout --force "$CIRCLE_TAG" @@ -337,7 +337,7 @@ commands: git checkout --force -B "$CIRCLE_BRANCH" "$CIRCLE_SHA1" git --no-pager log --no-color -n 1 --format='HEAD is now at %h %s' fi - + echo 'Updating submodules' # we don't need appsec submodules on windows <<# parameters.is_windows >>git submodule update --init libdatadog<> @@ -427,7 +427,7 @@ commands: command: | set +x cd << parameters.directory >> - if [[ '<< parameters.job >>' != "${LAST_ARTIFACTS_JOB:-}" ]]; then + if [[ '<< parameters.job >>' != "${LAST_ARTIFACTS_JOB:-}" ]]; then job_id=$(curl -X GET "https://circleci.com/api/v2/workflow/$CIRCLE_WORKFLOW_ID/job" -H "Accept: application/json" | grep -Eo '\{[^}]*"<< parameters.job >>"[^}]*' | grep -Eo '"job_number":[^,]+' | tail -c +14) export LAST_ARTIFACTS_RESULT=$(curl -X GET "https://circleci.com/api/v2/project/github/DataDog/dd-trace-php/$job_id/artifacts" -H "Accept: application/json") export LAST_ARTIFACTS_JOB='<< parameters.job >>' @@ -2224,55 +2224,6 @@ jobs: name: Validate centos package command: << parameters.configuration >> ./dockerfiles/verify_packages/verify.sh - verify_centos_6: - working_directory: ~/datadog - resource_class: small - parameters: - install_type: - type: string - # Possible values: php_installer, native_package - default: php_installer - machine: - image: ubuntu-2004:2023.04.2 - steps: - - restore_cache: - keys: - - source-v1-{{ .Branch }}-{{ .Revision }} - - run: - name: Install git - command: | - yum update -y - yum install -y git - - git_checkout - - run: mkdir -p build/packages - - when: - condition: - equal: [php_installer, << parameters.install_type >>] - steps: - - fetch_job_artifact: - job: "package extension" - artifact: 'dd-library-php-[^"]+x86_64-linux-gnu.tar.gz' - directory: build/packages - - fetch_job_artifact: - job: "package extension" - artifact: 'datadog-setup.php' - directory: build/packages - - when: - condition: - equal: [native_package, << parameters.install_type >>] - steps: - - fetch_job_artifact: - job: "package extension" - artifact: |- - datadog-php-tracer-[^"]+'"$(uname -m)"'.rpm - directory: build/packages - - run: mkdir -p test-results - - run: - name: Test installing packages on target systems - command: make -f dockerfiles/verify_packages/Makefile INSTALL_TYPE=<< parameters.install_type >> verify_centos_6 - - store_test_results: - path: test-results - verify_debian: working_directory: ~/datadog resource_class: small @@ -3183,7 +3134,7 @@ jobs: - run: name: cargo fetch command: | - SUDO=$(! command -v sudo >/dev/null || echo "sudo -E") + SUDO=$(! command -v sudo >/dev/null || echo "sudo -E") # On occasion, we've observed the .package-cache being in the cache. # If it's there, it will cause commands to stall, waiting for the file to be released. if [ -e '/rust/cargo/.package-cache' ] ; then @@ -3193,7 +3144,7 @@ jobs: $SUDO mkdir /.cargo $SUDO chmod 777 /.cargo - + $SUDO cargo fetch -v --target << parameters.triplet >> $SUDO chmod -R 777 '/rust/cargo' diff --git a/dockerfiles/verify_packages/Makefile b/dockerfiles/verify_packages/Makefile index 536037b8d0..1b68c99c0a 100644 --- a/dockerfiles/verify_packages/Makefile +++ b/dockerfiles/verify_packages/Makefile @@ -51,9 +51,6 @@ verify_centos: centos \ dockerfiles/verify_packages/verify.sh -# Centos 6 is verified using the existing images due to difficulties building it after the repos have been disabled. -verify_centos_6: $(CENTOS6_PHP_VERSIONS) - verify_tar_root: docker-compose run --rm \ -e PHP_VERSION="$(PHP_VERSION)" \ diff --git a/dockerfiles/verify_packages/centos/install.sh b/dockerfiles/verify_packages/centos/install.sh index 5ae9145b52..c54d9bb6c2 100644 --- a/dockerfiles/verify_packages/centos/install.sh +++ b/dockerfiles/verify_packages/centos/install.sh @@ -16,7 +16,7 @@ function do_retry() { } # Enable epel repo -do_retry rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-${OS_VERSION}.noarch.rpm +do_retry yum install -y epel-release # Installing pre-requisites do_retry yum install -y wget nginx httpd diff --git a/dockerfiles/verify_packages/installer/test_install_on_plesk.sh b/dockerfiles/verify_packages/installer/test_install_on_plesk.sh index 4874428407..e659fa2c10 100644 --- a/dockerfiles/verify_packages/installer/test_install_on_plesk.sh +++ b/dockerfiles/verify_packages/installer/test_install_on_plesk.sh @@ -2,7 +2,7 @@ set -e -service mysql start +service mariadb start plesk installer --select-release-current --install-component php8.1 From 4b2f6db2de899ca159e127586b2c9f105954fa94 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Thu, 22 Aug 2024 17:06:56 +0200 Subject: [PATCH 037/103] only warn on missing `json` extension (#2805) --- datadog-setup.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/datadog-setup.php b/datadog-setup.php index 2e8a34b120..f011399f47 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -1103,6 +1103,11 @@ function check_php_ext_prerequisite_or_exit($binary, $extName) { $extensions = shell_exec(escapeshellarg($binary) . " -m"); + // See: https://github.com/DataDog/dd-trace-php/issues/2787 + if ($extensions === null || $extensions === false || strpos($extensions, '[PHP Modules]') === false) { + echo "WARNING: The output of '$binary -m' could not be reliably checked. Please make sure you have the PHP extension '$extName' installed.\n"; + return; + } if (!in_array($extName, array_map("trim", explode("\n", $extensions)))) { print_error_and_exit("Required PHP extension '$extName' not found.\n"); } From bc59d209bc5b27ab2c48d1c14898f87e25aa00c1 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Fri, 23 Aug 2024 12:54:35 +0100 Subject: [PATCH 038/103] installer: remove appsec.helper_launch; fix descs --- datadog-setup.php | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/datadog-setup.php b/datadog-setup.php index f011399f47..a54bad752b 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -2260,27 +2260,12 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'arbitrary file name to which the messages will be appended', ], ], - [ - 'name' => 'datadog.appsec.helper_launch', - 'default' => 'On', - 'commented' => true, - 'description' => [ - 'The dd-appsec extension communicates with a helper process via UNIX sockets.', - 'This setting determines whether the extension should try to launch the daemon', - 'in case it cannot obtain a connection.', - 'If this is disabled, the helper should be launched through some other method.', - 'The extension expects the helper to run under the same user as the process', - 'where PHP is running, and will verify it.', - ], - ], [ 'name' => 'datadog.appsec.helper_path', 'default' => $appsecHelperPath, 'commented' => false, 'description' => [ - 'If ddappsec.helper_launch is enabled, this setting determines which binary', - 'the extension should try to load in the sidecar.', - 'Only relevant if ddappsec.helper_launch is enabled.', + 'The path to the shared library the appsec extension loads in the sidecar.', 'This ini setting is configured by the installer', ], ], @@ -2299,9 +2284,8 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'commented' => true, 'description' => [ 'The location to the UNIX socket that extension uses to communicate with the', - 'helper and the lock file that the extension processes will use to', - 'synchronize the launching of the helper.', - 'Only relevant if datadog.appsec.helper_launch is enabled', + 'helper inside sidecar and to the lock file. Ultimately, the paths include' + 'the version of the extension and pid/gid.', ], ], [ @@ -2318,8 +2302,8 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'default' => 'info', 'commented' => true, 'description' => [ - 'The verbosity of the logging of the appsec helper loaded in the sidecar. ', - 'Valid values are trace, debug, info, warn, err, critical and off', + 'The verbosity of the logging of the appsec helper loaded in the sidecar. ', + 'Valid values are trace, debug, info, warn, err, critical and off', ], ], [ From 8b54f4224a75dc294d58cbc750d0efff0cee3c3a Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 26 Aug 2024 12:41:23 +0200 Subject: [PATCH 039/103] Fix compilation after alpine container upgrade (#2811) * Fix compilation after alpine container upgrade Signed-off-by: Bob Weinand * Upgrade Rust version to 1.76.0 Signed-off-by: Bob Weinand * Fix dangling-pointer issue * Fix rust compilation on Alpine * Add test_session_token for two new tests Signed-off-by: Bob Weinand --------- Signed-off-by: Bob Weinand Co-authored-by: iamluc --- .circleci/continue_config.yml | 4 ++++ dockerfiles/ci/alpine_compile_extension/base.Dockerfile | 3 ++- dockerfiles/ci/buster/Dockerfile | 6 +++--- dockerfiles/ci/centos/7/base.Dockerfile | 8 ++++---- ext/compatibility.h | 2 ++ ext/ip_extraction.c | 3 +++ .../request-replayer/dd_trace_exception_span_event.phpt | 2 ++ tests/ext/request-replayer/dd_trace_span_event.phpt | 2 ++ zend_abstract_interface/symbols/call.c | 2 +- zend_abstract_interface/symbols/lookup.c | 2 ++ 10 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 806bd0ada9..14e5dbaa11 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3025,6 +3025,10 @@ jobs: - run: name: Compile sidecar command: | + # Workaround "error: failed to run custom build command for `aws-lc-sys v0.20.0`" + cargo install --force --locked bindgen-cli + export PATH="/root/.cargo/bin:$PATH" + SHARED=1 PROFILE=tracer-release host_os=linux-musl ./compile_rust.sh cp -v ${CARGO_TARGET_DIR:-target}/tracer-release/libddtrace_php.a libddtrace_php_$(uname -m)-alpine.a objcopy --compress-debug-sections ${CARGO_TARGET_DIR:-target}/tracer-release/libddtrace_php.so libddtrace_php_$(uname -m)-alpine.so diff --git a/dockerfiles/ci/alpine_compile_extension/base.Dockerfile b/dockerfiles/ci/alpine_compile_extension/base.Dockerfile index d21404f3a4..d78150c5a0 100644 --- a/dockerfiles/ci/alpine_compile_extension/base.Dockerfile +++ b/dockerfiles/ci/alpine_compile_extension/base.Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.18 +FROM alpine:3.19 RUN mkdir -p /app WORKDIR /app @@ -13,6 +13,7 @@ RUN set -eux; \ g++ \ gcc \ make \ + cmake \ build-base \ curl-dev \ libedit-dev \ diff --git a/dockerfiles/ci/buster/Dockerfile b/dockerfiles/ci/buster/Dockerfile index 9c70530fa4..89a99918d6 100644 --- a/dockerfiles/ci/buster/Dockerfile +++ b/dockerfiles/ci/buster/Dockerfile @@ -261,9 +261,9 @@ RUN set -eux; \ chown -R circleci:circleci /opt; # rust sha256sum generated locally after verifying it with sha256 -ARG RUST_VERSION="1.71.1" -ARG RUST_SHA256_ARM="c7cf230c740a62ea1ca6a4304d955c286aea44e3c6fc960b986a8c2eeea4ec3f" -ARG RUST_SHA256_X86="34778d1cda674990dfc0537bc600066046ae9cb5d65a07809f7e7da31d4689c4" +ARG RUST_VERSION="1.76.0" +ARG RUST_SHA256_ARM="2e8313421e8fb673efdf356cdfdd4bc16516f2610d4f6faa01327983104c05a0" +ARG RUST_SHA256_X86="9d589d2036b503cc45ecc94992d616fb3deec074deb36cacc2f5c212408f7399" # Mount a cache into /rust/cargo if you want to pre-fetch packages or something ENV CARGO_HOME=/rust/cargo ENV RUSTUP_HOME=/rust/rustup diff --git a/dockerfiles/ci/centos/7/base.Dockerfile b/dockerfiles/ci/centos/7/base.Dockerfile index 5463714668..80edfaa49b 100644 --- a/dockerfiles/ci/centos/7/base.Dockerfile +++ b/dockerfiles/ci/centos/7/base.Dockerfile @@ -160,9 +160,9 @@ RUN source scl_source enable devtoolset-7 \ && rm -fr "$FILENAME" "${FILENAME%.tar.gz}" "protobuf-${PROTOBUF_VERSION}" # rust sha256sum generated locally after verifying it with sha256 -ARG RUST_VERSION="1.71.1" -ARG RUST_SHA256_ARM="c7cf230c740a62ea1ca6a4304d955c286aea44e3c6fc960b986a8c2eeea4ec3f" -ARG RUST_SHA256_X86="34778d1cda674990dfc0537bc600066046ae9cb5d65a07809f7e7da31d4689c4" +ARG RUST_VERSION="1.76.0" +ARG RUST_SHA256_ARM="2e8313421e8fb673efdf356cdfdd4bc16516f2610d4f6faa01327983104c05a0" +ARG RUST_SHA256_X86="9d589d2036b503cc45ecc94992d616fb3deec074deb36cacc2f5c212408f7399" # Mount a cache into /rust/cargo if you want to pre-fetch packages or something ENV CARGO_HOME=/rust/cargo ENV RUSTUP_HOME=/rust/rustup @@ -183,7 +183,7 @@ RUN source scl_source enable devtoolset-7 \ # now install PHP specific dependencies RUN set -eux; \ - rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm; \ + yum install -y epel-release; \ yum update -y; \ yum install -y \ re2c \ diff --git a/ext/compatibility.h b/ext/compatibility.h index b284d2f718..3686198b03 100644 --- a/ext/compatibility.h +++ b/ext/compatibility.h @@ -325,6 +325,8 @@ static zend_always_inline void zend_array_release(zend_array *array) } } +#define ZEND_UNREACHABLE() ZEND_ASSUME(0) + #define ZEND_ARG_SEND_MODE(arg_info) (arg_info)->pass_by_reference #define zend_value_error zend_type_error #endif diff --git a/ext/ip_extraction.c b/ext/ip_extraction.c index 5aacaa9f0b..9e04f302dd 100644 --- a/ext/ip_extraction.c +++ b/ext/ip_extraction.c @@ -235,6 +235,9 @@ static struct extract_res dd_parse_multiple_maybe_port(zend_string *zvalue, ipad do { for (; value < end && *value == ' '; value++) { } + if (end - value < 0) { + ZEND_UNREACHABLE(); + } const char *comma = memchr(value, ',', end - value); const char *end_cur = comma ? comma : end; ipaddr cur; diff --git a/tests/ext/request-replayer/dd_trace_exception_span_event.phpt b/tests/ext/request-replayer/dd_trace_exception_span_event.phpt index 645dfef29e..105670b243 100644 --- a/tests/ext/request-replayer/dd_trace_exception_span_event.phpt +++ b/tests/ext/request-replayer/dd_trace_exception_span_event.phpt @@ -9,6 +9,8 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_AUTO_FLUSH_ENABLED=1 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 +--INI-- +datadog.trace.agent_test_session_token=dd_trace_exception_span_event --FILE-- = 70300 if (table == EG(function_table) && (func = zend_fetch_function_str(key.ptr, key.len))) { + ZEND_ASSERT(pointer == true); result = &resultzv; ZVAL_PTR(result, func); } else From 73480426016db356926120465febc65e2b60e5cd Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 26 Aug 2024 12:43:34 +0200 Subject: [PATCH 040/103] Fix error message for not loaded/not available (#2810) It was the wrong way round. Signed-off-by: Bob Weinand --- ext/integrations/integrations.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/integrations/integrations.c b/ext/integrations/integrations.c index 76b0babdb9..ed53f75b94 100644 --- a/ext/integrations/integrations.c +++ b/ext/integrations/integrations.c @@ -124,10 +124,10 @@ static void dd_invoke_integration_loader_and_unhook_posthook(zend_ulong invocati LOG(DEBUG, "Loaded integration %s", ZSTR_VAL(aux->classname)); break; case DD_TRACE_INTEGRATION_NOT_LOADED: - LOG(DEBUG, "Integration %s not available. New attempts WILL NOT be performed.", ZSTR_VAL(aux->classname)); + LOG(DEBUG, "Integration %s not loaded, possibly unsupported version. New attempts WILL NOT be performed.", ZSTR_VAL(aux->classname)); break; case DD_TRACE_INTEGRATION_NOT_AVAILABLE: - LOG(DEBUG, "Integration {name} not loaded. New attempts might be performed.", ZSTR_VAL(aux->classname)); + LOG(DEBUG, "Integration {name} not available. New attempts might be performed.", ZSTR_VAL(aux->classname)); unload_hooks = false; break; default: From 81a708f4af7e0448ef667bbbe978fad876cd1ce4 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 26 Aug 2024 23:12:10 +0200 Subject: [PATCH 041/103] perf(profiling): remove profiler mutex (#2714) * remove mutex * cleanup timeline handling * refactor: to make safety conditions easier to see --------- Co-authored-by: Levi Morrison --- profiling/src/allocation.rs | 5 +- profiling/src/exception.rs | 4 +- profiling/src/lib.rs | 35 +++-------- profiling/src/pcntl.rs | 18 +++--- profiling/src/profiling/mod.rs | 106 ++++++++++++++++++++++++++++----- profiling/src/timeline.rs | 58 +++++++----------- profiling/src/wall_time.rs | 4 +- 7 files changed, 132 insertions(+), 98 deletions(-) diff --git a/profiling/src/allocation.rs b/profiling/src/allocation.rs index 4ade967867..8c05881a94 100644 --- a/profiling/src/allocation.rs +++ b/profiling/src/allocation.rs @@ -2,7 +2,8 @@ use crate::bindings::{ self as zend, datadog_php_install_handler, datadog_php_zif_handler, ddog_php_prof_copy_long_into_zval, }; -use crate::{PROFILER, PROFILER_NAME, REQUEST_LOCALS}; +use crate::profiling::Profiler; +use crate::{PROFILER_NAME, REQUEST_LOCALS}; use lazy_static::lazy_static; use libc::{c_char, c_int, c_void, size_t}; use log::{debug, error, trace, warn}; @@ -94,7 +95,7 @@ impl AllocationProfilingStats { self.next_sampling_interval(); - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { // Safety: execute_data was provided by the engine, and the profiler doesn't mutate it. unsafe { profiler.collect_allocations( diff --git a/profiling/src/exception.rs b/profiling/src/exception.rs index 71da753b06..5f35f3647d 100644 --- a/profiling/src/exception.rs +++ b/profiling/src/exception.rs @@ -1,5 +1,5 @@ +use crate::profiling::Profiler; use crate::zend::{self, zend_execute_data, zend_generator, zval, InternalFunctionHandler}; -use crate::PROFILER; use crate::REQUEST_LOCALS; use log::{error, info}; use rand::rngs::ThreadRng; @@ -83,7 +83,7 @@ impl ExceptionProfilingStats { self.next_sampling_interval(); - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { // Safety: execute_data was provided by the engine, and the profiler doesn't mutate it. unsafe { profiler.collect_exception( diff --git a/profiling/src/lib.rs b/profiling/src/lib.rs index 75e82af14d..02cb66a8e8 100644 --- a/profiling/src/lib.rs +++ b/profiling/src/lib.rs @@ -41,16 +41,12 @@ use std::ffi::CStr; use std::ops::Deref; use std::os::raw::c_int; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; -use std::sync::{Arc, Mutex, Once}; +use std::sync::{Arc, Once}; use std::time::{Duration, Instant}; use uuid::Uuid; use crate::zend::datadog_sapi_globals_request_info; -/// The global profiler. Profiler gets made during the first rinit after an -/// minit, and is destroyed on mshutdown. -static PROFILER: Mutex> = Mutex::new(None); - /// Name of the profiling module and zend_extension. Must not contain any /// interior null bytes and must be null terminated. static PROFILER_NAME: &[u8] = b"datadog-profiling\0"; @@ -508,18 +504,7 @@ extern "C" fn rinit(_type: c_int, _module_number: c_int) -> ZendResult { return ZendResult::Success; } - // reminder: this cannot be done in minit because of Apache forking model - { - /* It would be nice if this could be cheaper. OnceCell would be cheaper - * but it doesn't quite fit the model, as going back to uninitialized - * requires either a &mut or .take(), and neither works for us (unless - * we go for unsafe, which is what we are trying to avoid). - */ - let mut profiler = PROFILER.lock().unwrap(); - if profiler.is_none() { - *profiler = Some(Profiler::new(system_settings)) - } - }; + Profiler::init(system_settings); if system_settings.profiling_enabled { REQUEST_LOCALS.with(|cell| { @@ -556,7 +541,7 @@ extern "C" fn rinit(_type: c_int, _module_number: c_int) -> ZendResult { return; } - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { let interrupt = VmInterrupt { interrupt_count_ptr: &locals.interrupt_count as *const AtomicU32, engine_ptr: locals.vm_interrupt_addr, @@ -612,7 +597,7 @@ extern "C" fn rshutdown(_type: c_int, _module_number: c_int) -> ZendResult { // wall-time is not expected to ever be disabled, except in testing, // and we don't need to optimize for that. if system_settings.profiling_enabled { - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { let interrupt = VmInterrupt { interrupt_count_ptr: &locals.interrupt_count, engine_ptr: locals.vm_interrupt_addr, @@ -822,10 +807,7 @@ extern "C" fn mshutdown(_type: c_int, _module_number: c_int) -> ZendResult { #[cfg(feature = "exception_profiling")] exception::exception_profiling_mshutdown(); - let mut profiler = PROFILER.lock().unwrap(); - if let Some(profiler) = profiler.as_mut() { - profiler.stop(Duration::from_secs(1)); - } + Profiler::stop(Duration::from_secs(1)); ZendResult::Success } @@ -859,10 +841,7 @@ extern "C" fn shutdown(_extension: *mut ZendExtension) { #[cfg(debug_assertions)] trace!("shutdown({:p})", _extension); - let mut profiler = PROFILER.lock().unwrap(); - if let Some(profiler) = profiler.take() { - profiler.shutdown(Duration::from_secs(2)); - } + Profiler::shutdown(Duration::from_secs(2)); // SAFETY: calling in shutdown before zai config is shutdown, and after // all configuration is done being accessed. Well... in the happy-path, @@ -891,7 +870,7 @@ fn notify_trace_finished(local_root_span_id: u64, span_type: Cow, resource: return; } - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { let message = LocalRootSpanResourceMessage { local_root_span_id, resource: resource.into_owned(), diff --git a/profiling/src/pcntl.rs b/profiling/src/pcntl.rs index 8a26c76be3..d0e230fb4a 100644 --- a/profiling/src/pcntl.rs +++ b/profiling/src/pcntl.rs @@ -3,11 +3,10 @@ use crate::bindings::{ datadog_php_install_handler, datadog_php_zif_handler, zend_execute_data, zend_long, zval, InternalFunctionHandler, }; -use crate::{config, PROFILER}; +use crate::{config, Profiler}; use ddcommon::cstr; use log::{error, warn}; use std::ffi::CStr; -use std::mem::forget; use std::ptr; static mut PCNTL_FORK_HANDLER: InternalFunctionHandler = None; @@ -62,14 +61,13 @@ unsafe fn handle_fork( // Hold mutexes across the handler. If there are any spurious wakeups by // the threads while the fork is occurring, they cannot acquire locks // since this thread holds them, preventing a deadlock situation. - let mut profiler_lock = PROFILER.lock().unwrap(); - if let Some(profiler) = profiler_lock.as_ref() { - profiler.fork_prepare(); + if let Some(profiler) = Profiler::get() { + let _ = profiler.fork_prepare(); } match dispatch(handler, execute_data, return_value) { Ok(ForkId::Parent) => { - if let Some(profiler) = profiler_lock.as_ref() { + if let Some(profiler) = Profiler::get() { profiler.post_fork_parent(); } return; @@ -77,7 +75,7 @@ unsafe fn handle_fork( Ok(ForkId::Child) => { /* fallthrough */ } Err(ForkError::NullRetval) => { // Skip error message if no profiler. - if profiler_lock.is_some() { + if Profiler::get().is_some() { error!( "Failed to read return value of forking function. A crash or deadlock may occur." ); @@ -87,7 +85,7 @@ unsafe fn handle_fork( Err(ForkError::ZvalType(r#type)) => { // Skip error message if no profiler. - if profiler_lock.is_some() { + if Profiler::get().is_some() { warn!( "Return type of forking function was unexpected: {type}. A crash or deadlock may occur." ); @@ -101,9 +99,7 @@ unsafe fn handle_fork( // 2. Something went wrong, and disable it to be safe. // And then leak the old profiler. Its drop method is not safe to run in // these situations. - if let Some(profiler) = profiler_lock.take() { - forget(profiler); - } + Profiler::kill(); alloc_prof_rshutdown(); diff --git a/profiling/src/profiling/mod.rs b/profiling/src/profiling/mod.rs index f03b609eb6..f98fee3484 100644 --- a/profiling/src/profiling/mod.rs +++ b/profiling/src/profiling/mod.rs @@ -25,11 +25,13 @@ use datadog_profiling::api::{ }; use datadog_profiling::exporter::Tag; use datadog_profiling::internal::Profile as InternalProfile; -use log::{debug, error, info, trace, warn}; +use log::{debug, info, trace, warn}; +use once_cell::sync::OnceCell; use std::borrow::Cow; use std::collections::HashMap; use std::hash::Hash; use std::intrinsics::transmute; +use std::mem::forget; use std::num::NonZeroI64; use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering}; use std::sync::{Arc, Barrier}; @@ -56,6 +58,10 @@ pub const NO_TIMESTAMP: i64 = 0; // magnitude for the capacity. const UPLOAD_CHANNEL_CAPACITY: usize = 8; +/// The global profiler. Profiler gets made during the first rinit after an +/// minit, and is destroyed on mshutdown. +static mut PROFILER: OnceCell = OnceCell::new(); + /// Order this array this way: /// 1. Always enabled types. /// 2. On by default types. @@ -510,7 +516,25 @@ pub enum UploadMessage { #[cfg(feature = "timeline")] const COW_EVAL: Cow = Cow::Borrowed("[eval]"); +const DDPROF_TIME: &str = "ddprof_time"; +const DDPROF_UPLOAD: &str = "ddprof_upload"; + impl Profiler { + /// Will initialize the `PROFILER` OnceCell and makes sure that only one thread will do so. + pub fn init(system_settings: &mut SystemSettings) { + // SAFETY: the `get_or_init` access is a thread-safe API, and the + // PROFILER is not being mutated outside single-threaded phases such + // as minit/mshutdown. + unsafe { PROFILER.get_or_init(|| Profiler::new(system_settings)) }; + } + + pub fn get() -> Option<&'static Profiler> { + // SAFETY: the `get` access is a thread-safe API, and the PROFILER is + // not being mutated outside single-threaded phases such as minit and + // mshutdown. + unsafe { PROFILER.get() } + } + pub fn new(system_settings: &mut SystemSettings) -> Self { let fork_barrier = Arc::new(Barrier::new(3)); let interrupt_manager = Arc::new(InterruptManager::new()); @@ -532,21 +556,19 @@ impl Profiler { Utc::now(), ); - let ddprof_time = "ddprof_time"; - let ddprof_upload = "ddprof_upload"; let sample_types_filter = SampleTypeFilter::new(system_settings); Profiler { fork_barrier, interrupt_manager, message_sender, upload_sender, - time_collector_handle: thread_utils::spawn(ddprof_time, move || { + time_collector_handle: thread_utils::spawn(DDPROF_TIME, move || { time_collector.run(); - trace!("thread {ddprof_time} complete, shutting down"); + trace!("thread {DDPROF_TIME} complete, shutting down"); }), - uploader_handle: thread_utils::spawn(ddprof_upload, move || { + uploader_handle: thread_utils::spawn(DDPROF_UPLOAD, move || { uploader.run(); - trace!("thread {ddprof_upload} complete, shutting down"); + trace!("thread {DDPROF_UPLOAD} complete, shutting down"); }), should_join: AtomicBool::new(true), sample_types_filter, @@ -576,22 +598,26 @@ impl Profiler { } /// Call before a fork, on the thread of the parent process that will fork. - pub fn fork_prepare(&self) { - // Send the message to the uploader first, as it has a longer worst- + pub fn fork_prepare(&self) -> anyhow::Result<()> { + // Send the message to the uploader first, as it has a longer worst // case time to wait. let uploader_result = self.upload_sender.send(UploadMessage::Pause); let profiler_result = self.message_sender.send(ProfilerMessage::Pause); - // todo: handle fails more gracefully, but it's tricky to sync 3 - // threads, any of which could have crashed or be delayed. This - // could also deadlock. match (uploader_result, profiler_result) { (Ok(_), Ok(_)) => { self.fork_barrier.wait(); + Ok(()) + } + (Err(err), Ok(_)) => { + anyhow::bail!("failed to prepare {DDPROF_UPLOAD} thread for forking: {err}") } - (_, _) => { - error!("failed to prepare the profiler for forking, a deadlock could occur") + (Ok(_), Err(err)) => { + anyhow::bail!("failed to prepare {DDPROF_TIME} thread for forking: {err}") } + (Err(_), Err(_)) => anyhow::bail!( + "failed to prepare both {DDPROF_UPLOAD} and {DDPROF_TIME} threads for forking" + ), } } @@ -622,7 +648,16 @@ impl Profiler { /// Note that you must call [Profiler::shutdown] afterwards; it's two /// parts of the same operation. It's split so you (or other extensions) /// can do something while the other threads finish up. - pub fn stop(&mut self, timeout: Duration) { + pub fn stop(timeout: Duration) { + // SAFETY: the `get_mut` access is a thread-safe API, and the PROFILER + // is not being mutated outside single-threaded phases such as minit + // and mshutdown. + if let Some(profiler) = unsafe { PROFILER.get_mut() } { + profiler.join_and_drop_sender(timeout); + } + } + + pub fn join_and_drop_sender(&mut self, timeout: Duration) { debug!("Stopping profiler."); let sent = match self @@ -653,7 +688,18 @@ impl Profiler { /// Completes the shutdown process; to start it, call [Profiler::stop] /// before calling [Profiler::shutdown]. /// Note the timeout is per thread, and there may be multiple threads. - pub fn shutdown(self, timeout: Duration) { + /// + /// Safety: only safe to be called in `SHUTDOWN`/`MSHUTDOWN` phase + pub fn shutdown(timeout: Duration) { + // SAFETY: the `take` access is a thread-safe API, and the PROFILER is + // not being mutated outside single-threaded phases such as minit and + // mshutdown. + if let Some(profiler) = unsafe { PROFILER.take() } { + profiler.join_collector_and_uploader(timeout); + } + } + + pub fn join_collector_and_uploader(self, timeout: Duration) { if self.should_join.load(Ordering::SeqCst) { thread_utils::join_timeout( self.time_collector_handle, @@ -672,6 +718,34 @@ impl Profiler { } } + /// Throws away the profiler and moves it to uninitialized. + /// + /// In a forking situation, the currently active profiler may not be valid + /// because it has join handles and other state shared by other threads, + /// and threads are not copied when the process is forked. + /// Additionally, if we've hit certain other issues like not being able to + /// determine the return type of the pcntl_fork function, we don't know if + /// we're the parent or child. + /// So, we throw away the current profiler and forget it, which avoids + /// running the destructor. Yes, this will leak some memory. + /// + /// # Safety + /// Must be called when no other thread is using the PROFILER object. That + /// includes this thread in some kind of recursive manner. + pub unsafe fn kill() { + // SAFETY: see this function's safety conditions. + if let Some(mut profiler) = PROFILER.take() { + // Drop some things to reduce memory. + profiler.interrupt_manager = Arc::new(InterruptManager::new()); + profiler.message_sender = crossbeam_channel::bounded(0).0; + profiler.upload_sender = crossbeam_channel::bounded(0).0; + + // But we're not 100% sure everything is safe to drop, notably the + // join handles, so we leak the rest. + forget(profiler) + } + } + /// Collect a stack sample with elapsed wall time. Collects CPU time if /// it's enabled and available. pub fn collect_time(&self, execute_data: *mut zend_execute_data, interrupt_count: u32) { diff --git a/profiling/src/timeline.rs b/profiling/src/timeline.rs index f0e8928526..596c887a14 100644 --- a/profiling/src/timeline.rs +++ b/profiling/src/timeline.rs @@ -1,10 +1,11 @@ +use crate::profiling::Profiler; use crate::zend::{ self, zai_str_from_zstr, zend_execute_data, zend_get_executed_filename_ex, zval, InternalFunctionHandler, }; -use crate::{PROFILER, REQUEST_LOCALS}; +use crate::REQUEST_LOCALS; use libc::c_char; -use log::{error, trace, warn}; +use log::{error, trace}; use std::cell::RefCell; use std::ffi::CStr; use std::ptr; @@ -45,13 +46,12 @@ impl State { } } -#[inline] -fn try_sleeping_fn( +fn sleeping_fn( func: unsafe extern "C" fn(execute_data: *mut zend_execute_data, return_value: *mut zval), execute_data: *mut zend_execute_data, return_value: *mut zval, state: State, -) -> anyhow::Result<()> { +) { let timeline_enabled = REQUEST_LOCALS.with(|cell| { cell.try_borrow() .map(|locals| locals.system_settings().profiling_timeline_enabled) @@ -60,7 +60,7 @@ fn try_sleeping_fn( if !timeline_enabled { unsafe { func(execute_data, return_value) }; - return Ok(()); + return; } let start = Instant::now(); @@ -74,34 +74,18 @@ fn try_sleeping_fn( let duration = start.elapsed(); - // > Returns an Err if earlier is later than self, and the error contains - // > how far from self the time is. - // This shouldn't ever happen (now is always later than the epoch) but in - // case it does, short-circuit the function. - let now = SystemTime::now().duration_since(UNIX_EPOCH)?; - - match PROFILER.lock() { - Ok(guard) => { - // If the profiler isn't there, it's probably just not enabled. - if let Some(profiler) = guard.as_ref() { - let now = now.as_nanos() as i64; - let duration = duration.as_nanos() as i64; - profiler.collect_idle(now, duration, state.as_str()) - } - } - Err(err) => anyhow::bail!("profiler mutex: {err:#}"), + // This shouldn't ever happen (now is always later than the epoch) + let now = SystemTime::now().duration_since(UNIX_EPOCH); + + if now.is_err() { + return; } - Ok(()) -} -fn sleeping_fn( - func: unsafe extern "C" fn(execute_data: *mut zend_execute_data, return_value: *mut zval), - execute_data: *mut zend_execute_data, - return_value: *mut zval, - state: State, -) { - if let Err(err) = try_sleeping_fn(func, execute_data, return_value, state) { - warn!("error creating profiling timeline sample for an internal function: {err:#}"); + if let Some(profiler) = Profiler::get() { + // Safety: `unwrap` can be unchecked, as we checked for `is_err()` + let now = unsafe { now.unwrap_unchecked().as_nanos() } as i64; + let duration = duration.as_nanos() as i64; + profiler.collect_idle(now, duration, state.as_str()); } } @@ -234,7 +218,7 @@ pub unsafe fn timeline_rinit() { return; } - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { profiler.collect_idle( // Safety: checked for `is_err()` above SystemTime::now() @@ -293,7 +277,7 @@ pub(crate) unsafe fn timeline_mshutdown() { return; } - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { profiler.collect_idle( // Safety: checked for `is_err()` above SystemTime::now() @@ -357,7 +341,7 @@ unsafe extern "C" fn ddog_php_prof_compile_string( duration.as_nanos(), ); - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { profiler.collect_compile_string( // Safety: checked for `is_err()` above now.unwrap().as_nanos() as i64, @@ -423,7 +407,7 @@ unsafe extern "C" fn ddog_php_prof_compile_file( duration.as_nanos(), ); - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { profiler.collect_compile_file( // Safety: checked for `is_err()` above now.unwrap().as_nanos() as i64, @@ -493,7 +477,7 @@ unsafe extern "C" fn ddog_php_prof_gc_collect_cycles() -> i32 { duration.as_nanos() ); - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { cfg_if::cfg_if! { if #[cfg(php_gc_status)] { profiler.collect_garbage_collection( diff --git a/profiling/src/wall_time.rs b/profiling/src/wall_time.rs index 2635092b92..42f7277843 100644 --- a/profiling/src/wall_time.rs +++ b/profiling/src/wall_time.rs @@ -5,7 +5,7 @@ use crate::bindings::{ zend_execute_data, zend_execute_internal, zend_interrupt_function, zval, VmInterruptFn, ZEND_ACC_CALL_VIA_TRAMPOLINE, }; -use crate::{zend, PROFILER, REQUEST_LOCALS}; +use crate::{profiling::Profiler, zend, REQUEST_LOCALS}; use std::mem::MaybeUninit; use std::sync::atomic::Ordering; @@ -55,7 +55,7 @@ pub extern "C" fn ddog_php_prof_interrupt_function(execute_data: *mut zend_execu return; } - if let Some(profiler) = PROFILER.lock().unwrap().as_ref() { + if let Some(profiler) = Profiler::get() { // Safety: execute_data was provided by the engine, and the profiler doesn't mutate it. profiler.collect_time(execute_data, interrupt_count); } From 5c8a7420d5c27c09d6055b414a1e7b9ff17b5555 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Tue, 27 Aug 2024 13:59:45 +0200 Subject: [PATCH 042/103] Add crashtracker (#2763) * Init crashtracker * Check crashtracker init result * Update bindings * Connect to the right unix socket * Update bindings + try to fix pecl package * Update libdatadog + rebase * Update libdatadog * Fix pecl package * Cleanup * Fix tests * Test the crashtracker * Update libdatadog * Update libdatadog * Fix CI * Enable the Crashtracker only if the telemetry is enabled * Fix ddog_sidecar_session_set_config arguments * Try to make crashtracker test more stable * Try to fix compilation on Windows * Try workaround * Fix rebase * Windows does not like array of size 0 * Try to increase the sidecar startup delay * Spam a few ddtrace_ffi_try() calls to avoid leaking memory on sidecar error Signed-off-by: Bob Weinand * Update libdatadog Signed-off-by: Bob Weinand * Fix test skip comment + enable it on Alpine * Reduce the number of Rust crates required in the pecl package * Run test_extension_ci with strace Signed-off-by: Bob Weinand * Upgrade Rust version to 1.76.0 Signed-off-by: Bob Weinand * Remove debug strace Signed-off-by: Bob Weinand * Fix crashtracker test Signed-off-by: Bob Weinand * Turn DD_LOG_BACKTRACE on by default * Fix memory leak * Update libdatadog once more --------- Signed-off-by: Bob Weinand Co-authored-by: Bob Weinand --- Cargo.lock | 272 ++++++++++++- Makefile | 11 +- cbindgen.toml | 2 +- components-rs/Cargo.toml | 1 + components-rs/common.h | 333 ++++++++++++++++ components-rs/crashtracker.h | 436 +++++++++++++++++++++ components-rs/lib.rs | 1 + components-rs/sidecar.h | 42 +- ext/configuration.h | 2 +- ext/hook/uhook.c | 35 +- ext/sidecar.c | 80 ++-- ext/sidecar.h | 2 + ext/signals.c | 61 ++- ext/telemetry.c | 24 +- libdatadog | 2 +- tests/ext/crashtracker_segfault.phpt | 70 ++++ tests/ext/segfault_backtrace_disabled.phpt | 2 + tests/ext/segfault_backtrace_enabled.phpt | 2 - tests/ext/telemetry/config.phpt | 2 + tests/ext/telemetry/simple.phpt | 3 +- 20 files changed, 1317 insertions(+), 66 deletions(-) create mode 100644 components-rs/crashtracker.h create mode 100644 tests/ext/crashtracker_segfault.phpt diff --git a/Cargo.lock b/Cargo.lock index 6f23e30ea3..23f5190da5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli", + "gimli 0.28.1", ] [[package]] @@ -548,6 +548,27 @@ version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" +[[package]] +name = "blazesym" +version = "0.2.0-rc.0" +source = "git+https://github.com/libbpf/blazesym.git?rev=v0.2.0-rc.0#2f393f66a448f46ea71889e81a8866799762463d" +dependencies = [ + "cpp_demangle", + "gimli 0.30.0", + "libc 0.2.154", + "miniz_oxide", + "rustc-demangle", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "blocking" version = "1.6.1" @@ -1025,6 +1046,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpu-time" version = "1.0.0" @@ -1035,6 +1065,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc 0.2.154", +] + [[package]] name = "crc32fast" version = "1.4.0" @@ -1139,6 +1178,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "csv" version = "1.3.0" @@ -1211,6 +1260,45 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "datadog-crashtracker" +version = "0.0.1" +dependencies = [ + "anyhow", + "backtrace", + "blazesym", + "chrono", + "ddcommon 0.0.1", + "ddtelemetry", + "http 0.2.11", + "hyper 0.14.28", + "libc 0.2.154", + "nix 0.27.1", + "os_info", + "page_size", + "portable-atomic", + "rand 0.8.5", + "serde", + "serde_json", + "tempfile", + "tokio", + "uuid", +] + +[[package]] +name = "datadog-crashtracker-ffi" +version = "0.0.1" +dependencies = [ + "anyhow", + "build_common", + "datadog-crashtracker", + "ddcommon 0.0.1", + "ddcommon-ffi", + "hyper 0.14.28", + "symbolic-common", + "symbolic-demangle", +] + [[package]] name = "datadog-ddsketch" version = "0.0.1" @@ -1220,6 +1308,14 @@ dependencies = [ "protoc-bin-vendored", ] +[[package]] +name = "datadog-dynamic-configuration" +version = "0.0.1" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "datadog-ipc" version = "0.1.0" @@ -1325,19 +1421,48 @@ dependencies = [ "tokio-util 0.7.10", ] +[[package]] +name = "datadog-remote-config" +version = "0.0.1" +dependencies = [ + "anyhow", + "base64 0.21.7", + "datadog-dynamic-configuration", + "datadog-trace-protobuf", + "ddcommon 0.0.1", + "futures", + "futures-util", + "http 0.2.11", + "hyper 0.14.28", + "lazy_static", + "manual_future", + "serde", + "serde_json", + "sha2", + "time", + "tokio", + "tokio-util 0.7.10", + "tracing", + "uuid", +] + [[package]] name = "datadog-sidecar" version = "0.0.1" dependencies = [ "anyhow", "arrayref", + "base64 0.21.7", "bincode", "bytes", "cadence", "chrono", "console-subscriber", + "datadog-crashtracker", + "datadog-dynamic-configuration", "datadog-ipc", "datadog-ipc-macros", + "datadog-remote-config", "datadog-sidecar-macros", "datadog-trace-normalization", "datadog-trace-protobuf", @@ -1364,7 +1489,9 @@ dependencies = [ "rmp-serde", "sendfd", "serde", + "serde_json", "serde_with", + "sha2", "simd-json", "spawn_worker", "sys-info", @@ -1385,6 +1512,7 @@ name = "datadog-sidecar-ffi" version = "0.0.1" dependencies = [ "datadog-ipc", + "datadog-remote-config", "datadog-sidecar", "datadog-trace-utils", "ddcommon 0.0.1", @@ -1437,6 +1565,7 @@ dependencies = [ "bolero", "bolero-generator", "bytes", + "cargo-platform", "cargo_metadata", "criterion", "datadog-trace-normalization", @@ -1522,6 +1651,7 @@ dependencies = [ "crossbeam-queue", "ddcommon 0.0.1", "hyper 0.14.28", + "serde", ] [[package]] @@ -1570,6 +1700,7 @@ dependencies = [ "anyhow", "cbindgen", "const-str", + "datadog-crashtracker-ffi", "datadog-sidecar", "datadog-sidecar-ffi", "ddcommon 0.0.1", @@ -1591,6 +1722,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1618,6 +1758,16 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dirs" version = "5.0.1" @@ -1834,6 +1984,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "fastrand" version = "1.9.0" @@ -2035,6 +2191,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check 0.9.4", +] + [[package]] name = "getrandom" version = "0.2.12" @@ -2054,6 +2220,17 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "gimli" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9" +dependencies = [ + "fallible-iterator", + "indexmap 2.2.6", + "stable_deref_trait", +] + [[package]] name = "glibc_version" version = "0.1.2" @@ -2863,6 +3040,15 @@ dependencies = [ "rustix 0.38.31", ] +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc 0.2.154", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -2931,6 +3117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -2960,6 +3147,15 @@ dependencies = [ "tempdir", ] +[[package]] +name = "msvc-demangler" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4c25a3bb7d880e8eceab4822f3141ad0700d20f025991c1f03bd3d00219a5fc" +dependencies = [ + "bitflags 2.4.2", +] + [[package]] name = "multimap" version = "0.8.3" @@ -3182,6 +3378,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "os_str_bytes" version = "6.6.1" @@ -3456,6 +3663,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +dependencies = [ + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -4322,6 +4538,17 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -4353,6 +4580,12 @@ dependencies = [ "libc 0.2.154", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "simd-json" version = "0.13.8" @@ -4451,6 +4684,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -4505,6 +4744,30 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "symbolic-common" +version = "12.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71297dc3e250f7dbdf8adb99e235da783d690f5819fdeb4cce39d9cfb0aca9f1" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "424fa2c9bf2c862891b9cfd354a752751a6730fd838a4691e7f6c2c7957b9daf" +dependencies = [ + "cpp_demangle", + "msvc-demangler", + "rustc-demangle", + "symbolic-common", +] + [[package]] name = "syn" version = "1.0.109" @@ -5101,6 +5364,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicase" version = "2.7.0" @@ -5168,6 +5437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "getrandom", + "serde", ] [[package]] diff --git a/Makefile b/Makefile index 7e582fe2b4..456ac3ac21 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ RUN_TESTS_CMD := REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJECT_ROOT) USE_TRACKE C_FILES = $(shell find components components-rs ext src/dogstatsd zend_abstract_interface -name '*.c' -o -name '*.h' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_FILES = $(shell find tests/ext -name '*.php*' -o -name '*.inc' -o -name '*.json' -o -name 'CONFLICTS' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) -RUST_FILES = $(BUILD_DIR)/Cargo.toml $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{alloc,build-common,ddcommon,ddcommon-ffi,ddsketch,ddtelemetry,ddtelemetry-ffi,ipc,sidecar,sidecar-ffi,spawn_worker,tools/{cc_utils,sidecar_mockgen},trace-*,Cargo.toml} -type f \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/ipc/build.rs" -not -path "*/sidecar-ffi/build.rs") +RUST_FILES = $(BUILD_DIR)/Cargo.toml $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{alloc,build-common,crashtracker,crashtracker-ffi,ddcommon,ddcommon-ffi,ddsketch,ddtelemetry,ddtelemetry-ffi,dynamic-configuration,ipc,remote-config,sidecar,sidecar-ffi,spawn_worker,tools/{cc_utils,sidecar_mockgen},trace-*,Cargo.toml} -type f \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/ipc/build.rs" -not -path "*/sidecar-ffi/build.rs") ALL_OBJECT_FILES = $(C_FILES) $(RUST_FILES) $(BUILD_DIR)/Makefile TEST_OPCACHE_FILES = $(shell find tests/opcache -name '*.php*' -o -name '.gitkeep' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_STUB_FILES = $(shell find tests/ext -type d -name 'stubs' -exec find '{}' -type f \; | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) @@ -381,9 +381,9 @@ clang_format_fix: cbindgen: remove_cbindgen generate_cbindgen remove_cbindgen: - rm -f components-rs/ddtrace.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h + rm -f components-rs/ddtrace.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h -generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h +generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h ( \ $(command rustup && echo run nightly --) cbindgen --crate ddtrace-php \ --config cbindgen.toml \ @@ -398,11 +398,14 @@ generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h componen $(command rustup && echo run nightly --) cbindgen --crate datadog-sidecar-ffi \ --config sidecar-ffi/cbindgen.toml \ --output $(PROJECT_ROOT)/components-rs/sidecar.h; \ + $(command rustup && echo run nightly --) cbindgen --crate datadog-crashtracker-ffi \ + --config crashtracker-ffi/cbindgen.toml \ + --output $(PROJECT_ROOT)/components-rs/crashtracker.h; \ if test -d $(PROJECT_ROOT)/tmp; then \ mkdir -pv "$(BUILD_DIR)"; \ export CARGO_TARGET_DIR="$(BUILD_DIR)/target"; \ fi; \ - cargo run -p tools -- $(PROJECT_ROOT)/components-rs/common.h $(PROJECT_ROOT)/components-rs/ddtrace.h $(PROJECT_ROOT)/components-rs/telemetry.h $(PROJECT_ROOT)/components-rs/sidecar.h \ + cargo run -p tools -- $(PROJECT_ROOT)/components-rs/common.h $(PROJECT_ROOT)/components-rs/ddtrace.h $(PROJECT_ROOT)/components-rs/telemetry.h $(PROJECT_ROOT)/components-rs/sidecar.h $(PROJECT_ROOT)/components-rs/crashtracker.h \ ) cbindgen_binary: diff --git a/cbindgen.toml b/cbindgen.toml index 8a17207ed6..7bc3f5b99e 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -25,4 +25,4 @@ rename_variants = "ScreamingSnakeCase" [parse] parse_deps = true -include = ["ddcommon", "ddtelemetry", "ddcommon-ffi", "ddtelemetry-ffi", "datadog-sidecar", "datadog-ipc", "uuid"] +include = ["ddcommon", "ddtelemetry", "ddcommon-ffi", "ddtelemetry-ffi", "datadog-crashtracker-ffi", "datadog-sidecar", "datadog-ipc", "uuid"] diff --git a/components-rs/Cargo.toml b/components-rs/Cargo.toml index ea315a94e9..8b58a28dd0 100644 --- a/components-rs/Cargo.toml +++ b/components-rs/Cargo.toml @@ -14,6 +14,7 @@ ddtelemetry = { path = "../libdatadog/ddtelemetry" } ddtelemetry-ffi = { path = "../libdatadog/ddtelemetry-ffi", default-features = false } datadog-sidecar = { path = "../libdatadog/sidecar" } datadog-sidecar-ffi = { path = "../libdatadog/sidecar-ffi" } +datadog-crashtracker-ffi = { path = "../libdatadog/crashtracker-ffi", default-features = false, features = ["collector"] } spawn_worker = { path = "../libdatadog/spawn_worker" } anyhow = { version = "1.0" } const-str = "0.5.6" diff --git a/components-rs/common.h b/components-rs/common.h index c1ab4e455b..e0e28bea30 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -377,6 +377,46 @@ typedef struct ddog_ContextKey { enum ddog_MetricType _1; } ddog_ContextKey; +#define ddog_MultiTargetFetcher_DEFAULT_CLIENTS_LIMIT 100 + +typedef enum ddog_RemoteConfigCapabilities { + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_ACTIVATION = 1, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_IP_BLOCKING = 2, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_DD_RULES = 3, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_EXCLUSIONS = 4, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_REQUEST_BLOCKING = 5, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RESPONSE_BLOCKING = 6, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_USER_BLOCKING = 7, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_RULES = 8, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_BLOCKING_RESPONSE = 9, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_TRUSTED_IPS = 10, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_API_SECURITY_SAMPLE_RATE = 11, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_SAMPLE_RATE = 12, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_LOGS_INJECTION = 13, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_HTTP_HEADER_TAGS = 14, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_CUSTOM_TAGS = 15, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_PROCESSOR_OVERRIDES = 16, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_DATA_SCANNERS = 17, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_EXCLUSION_DATA = 18, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_ENABLED = 19, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_DATA_STREAMS_ENABLED = 20, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SQLI = 21, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_LFI = 22, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SSRF = 23, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SHI = 24, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_XXE = 25, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_RCE = 26, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_NOSQLI = 27, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_XSS = 28, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_SAMPLE_RULES = 29, + DDOG_REMOTE_CONFIG_CAPABILITIES_CSM_ACTIVATION = 30, +} ddog_RemoteConfigCapabilities; + +typedef enum ddog_RemoteConfigProduct { + DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, + DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, +} ddog_RemoteConfigProduct; + typedef struct ddog_AgentRemoteConfigReader ddog_AgentRemoteConfigReader; typedef struct ddog_AgentRemoteConfigWriter_ShmHandle ddog_AgentRemoteConfigWriter_ShmHandle; @@ -389,6 +429,8 @@ typedef struct ddog_MappedMem_ShmHandle ddog_MappedMem_ShmHandle; */ typedef struct ddog_PlatformHandle_File ddog_PlatformHandle_File; +typedef struct ddog_RemoteConfigReader ddog_RemoteConfigReader; + /** * `RuntimeMetadata` is a struct that represents the runtime metadata of a language. */ @@ -411,6 +453,297 @@ typedef struct ddog_TracerHeaderTags { bool client_computed_stats; } ddog_TracerHeaderTags; +typedef enum ddog_crasht_DemangleOptions { + DDOG_CRASHT_DEMANGLE_OPTIONS_COMPLETE, + DDOG_CRASHT_DEMANGLE_OPTIONS_NAME_ONLY, +} ddog_crasht_DemangleOptions; + +typedef enum ddog_crasht_NormalizedAddressTypes { + DDOG_CRASHT_NORMALIZED_ADDRESS_TYPES_NONE = 0, + DDOG_CRASHT_NORMALIZED_ADDRESS_TYPES_ELF, +} ddog_crasht_NormalizedAddressTypes; + +/** + * This enum represents operations a the tracked library might be engaged in. + * Currently only implemented for profiling. + * The idea is that if a crash consistently occurs while a particular operation + * is ongoing, its likely related. + * + * In the future, we might also track wall-clock time of operations + * (or some statistical sampling thereof) using the same enum. + * + * NOTE: This enum is known to be non-exhaustive. Feel free to add new types + * as needed. + */ +typedef enum ddog_crasht_OpTypes { + DDOG_CRASHT_OP_TYPES_PROFILER_INACTIVE = 0, + DDOG_CRASHT_OP_TYPES_PROFILER_COLLECTING_SAMPLE, + DDOG_CRASHT_OP_TYPES_PROFILER_UNWINDING, + DDOG_CRASHT_OP_TYPES_PROFILER_SERIALIZING, + /** + * Dummy value to allow easier iteration + */ + DDOG_CRASHT_OP_TYPES_SIZE, +} ddog_crasht_OpTypes; + +/** + * Stacktrace collection occurs in the context of a crashing process. + * If the stack is sufficiently corruputed, it is possible (but unlikely), + * for stack trace collection itself to crash. + * We recommend fully enabling stacktrace collection, but having an environment + * variable to allow downgrading the collector. + */ +typedef enum ddog_crasht_StacktraceCollection { + /** + * Stacktrace collection occurs in the + */ + DDOG_CRASHT_STACKTRACE_COLLECTION_DISABLED, + DDOG_CRASHT_STACKTRACE_COLLECTION_WITHOUT_SYMBOLS, + DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, + DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER, +} ddog_crasht_StacktraceCollection; + +/** + * A generic result type for when a crashtracking operation may fail, + * but there's nothing to return in the case of success. + */ +typedef enum ddog_crasht_Result_Tag { + DDOG_CRASHT_RESULT_OK, + DDOG_CRASHT_RESULT_ERR, +} ddog_crasht_Result_Tag; + +typedef struct ddog_crasht_Result { + ddog_crasht_Result_Tag tag; + union { + struct { + /** + * Do not use the value of Ok. This value only exists to overcome + * Rust -> C code generation. + */ + bool ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_crasht_Result; + +typedef struct ddog_crasht_Slice_CharSlice { + /** + * Must be non-null and suitably aligned for the underlying type. + */ + const ddog_CharSlice *ptr; + /** + * The number of elements (not bytes) that `.ptr` points to. Must be less + * than or equal to [isize::MAX]. + */ + uintptr_t len; +} ddog_crasht_Slice_CharSlice; + +typedef struct ddog_crasht_Config { + struct ddog_crasht_Slice_CharSlice additional_files; + bool create_alt_stack; + /** + * The endpoint to send the crash report to (can be a file://). + * If None, the crashtracker will infer the agent host from env variables. + */ + const struct ddog_Endpoint *endpoint; + enum ddog_crasht_StacktraceCollection resolve_frames; + uint64_t timeout_secs; + bool wait_for_receiver; +} ddog_crasht_Config; + +typedef struct ddog_crasht_EnvVar { + ddog_CharSlice key; + ddog_CharSlice val; +} ddog_crasht_EnvVar; + +typedef struct ddog_crasht_Slice_EnvVar { + /** + * Must be non-null and suitably aligned for the underlying type. + */ + const struct ddog_crasht_EnvVar *ptr; + /** + * The number of elements (not bytes) that `.ptr` points to. Must be less + * than or equal to [isize::MAX]. + */ + uintptr_t len; +} ddog_crasht_Slice_EnvVar; + +typedef struct ddog_crasht_ReceiverConfig { + struct ddog_crasht_Slice_CharSlice args; + struct ddog_crasht_Slice_EnvVar env; + ddog_CharSlice path_to_receiver_binary; + /** + * Optional filename to forward stderr to (useful for logging/debugging) + */ + ddog_CharSlice optional_stderr_filename; + /** + * Optional filename to forward stdout to (useful for logging/debugging) + */ + ddog_CharSlice optional_stdout_filename; +} ddog_crasht_ReceiverConfig; + +typedef struct ddog_crasht_Metadata { + ddog_CharSlice library_name; + ddog_CharSlice library_version; + ddog_CharSlice family; + /** + * Should include "service", "environment", etc + */ + const struct ddog_Vec_Tag *tags; +} ddog_crasht_Metadata; + +typedef enum ddog_crasht_UsizeResult_Tag { + DDOG_CRASHT_USIZE_RESULT_OK, + DDOG_CRASHT_USIZE_RESULT_ERR, +} ddog_crasht_UsizeResult_Tag; + +typedef struct ddog_crasht_UsizeResult { + ddog_crasht_UsizeResult_Tag tag; + union { + struct { + uintptr_t ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_crasht_UsizeResult; + +/** + * Represents a CrashInfo. Do not access its member for any reason, only use + * the C API functions on this struct. + */ +typedef struct ddog_crasht_CrashInfo { + struct ddog_crasht_CrashInfo *inner; +} ddog_crasht_CrashInfo; + +/** + * Returned by [ddog_prof_Profile_new]. + */ +typedef enum ddog_crasht_CrashInfoNewResult_Tag { + DDOG_CRASHT_CRASH_INFO_NEW_RESULT_OK, + DDOG_CRASHT_CRASH_INFO_NEW_RESULT_ERR, +} ddog_crasht_CrashInfoNewResult_Tag; + +typedef struct ddog_crasht_CrashInfoNewResult { + ddog_crasht_CrashInfoNewResult_Tag tag; + union { + struct { + struct ddog_crasht_CrashInfo ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_crasht_CrashInfoNewResult; + +typedef struct ddog_crasht_SigInfo { + uint64_t signum; + ddog_CharSlice signame; +} ddog_crasht_SigInfo; + +typedef struct ddog_crasht_StackFrameNames { + struct ddog_Option_U32 colno; + ddog_CharSlice filename; + struct ddog_Option_U32 lineno; + ddog_CharSlice name; +} ddog_crasht_StackFrameNames; + +typedef struct ddog_crasht_Slice_StackFrameNames { + /** + * Must be non-null and suitably aligned for the underlying type. + */ + const struct ddog_crasht_StackFrameNames *ptr; + /** + * The number of elements (not bytes) that `.ptr` points to. Must be less + * than or equal to [isize::MAX]. + */ + uintptr_t len; +} ddog_crasht_Slice_StackFrameNames; + +typedef struct ddog_Slice_U8 { + /** + * Must be non-null and suitably aligned for the underlying type. + */ + const uint8_t *ptr; + /** + * The number of elements (not bytes) that `.ptr` points to. Must be less + * than or equal to [isize::MAX]. + */ + uintptr_t len; +} ddog_Slice_U8; + +/** + * Use to represent bytes -- does not need to be valid UTF-8. + */ +typedef struct ddog_Slice_U8 ddog_ByteSlice; + +typedef struct ddog_crasht_NormalizedAddress { + uint64_t file_offset; + ddog_ByteSlice build_id; + ddog_CharSlice path; + enum ddog_crasht_NormalizedAddressTypes typ; +} ddog_crasht_NormalizedAddress; + +typedef struct ddog_crasht_StackFrame { + ddog_CharSlice build_id; + uintptr_t ip; + uintptr_t module_base_address; + struct ddog_crasht_Slice_StackFrameNames names; + struct ddog_crasht_NormalizedAddress normalized_ip; + uintptr_t sp; + uintptr_t symbol_address; +} ddog_crasht_StackFrame; + +typedef struct ddog_crasht_Slice_StackFrame { + /** + * Must be non-null and suitably aligned for the underlying type. + */ + const struct ddog_crasht_StackFrame *ptr; + /** + * The number of elements (not bytes) that `.ptr` points to. Must be less + * than or equal to [isize::MAX]. + */ + uintptr_t len; +} ddog_crasht_Slice_StackFrame; + +/** + * Represents time since the Unix Epoch in seconds plus nanoseconds. + */ +typedef struct ddog_Timespec { + int64_t seconds; + uint32_t nanoseconds; +} ddog_Timespec; + +/** + * A wrapper for returning owned strings from FFI + */ +typedef struct ddog_crasht_StringWrapper { + /** + * This is a String stuffed into the vec. + */ + struct ddog_Vec_U8 message; +} ddog_crasht_StringWrapper; + +typedef enum ddog_crasht_StringWrapperResult_Tag { + DDOG_CRASHT_STRING_WRAPPER_RESULT_OK, + DDOG_CRASHT_STRING_WRAPPER_RESULT_ERR, +} ddog_crasht_StringWrapperResult_Tag; + +typedef struct ddog_crasht_StringWrapperResult { + ddog_crasht_StringWrapperResult_Tag tag; + union { + struct { + struct ddog_crasht_StringWrapper ok; + }; + struct { + struct ddog_Error err; + }; + }; +} ddog_crasht_StringWrapperResult; + #ifdef __cplusplus extern "C" { #endif // __cplusplus diff --git a/components-rs/crashtracker.h b/components-rs/crashtracker.h new file mode 100644 index 0000000000..8012dfcac4 --- /dev/null +++ b/components-rs/crashtracker.h @@ -0,0 +1,436 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + + +#ifndef DDOG_CRASHTRACKER_H +#define DDOG_CRASHTRACKER_H + +#pragma once + +#include +#include +#include +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * Cleans up after the crash-tracker: + * Unregister the crash handler, restore the previous handler (if any), and + * shut down the receiver. Note that the use of this function is optional: + * the receiver will automatically shutdown when the pipe is closed on program + * exit. + * + * # Preconditions + * This function assumes that the crashtracker has previously been + * initialized. + * # Safety + * Crash-tracking functions are not reentrant. + * No other crash-handler functions should be called concurrently. + * # Atomicity + * This function is not atomic. A crash during its execution may lead to + * unexpected crash-handling behaviour. + */ +DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_shutdown(void); + +/** + * Reinitialize the crash-tracking infrastructure after a fork. + * This should be one of the first things done after a fork, to minimize the + * chance that a crash occurs between the fork, and this call. + * In particular, reset the counters that track the profiler state machine, + * and start a new receiver to collect data from this fork. + * NOTE: An alternative design would be to have a 1:many sidecar listening on a + * socket instead of 1:1 receiver listening on a pipe, but the only real + * advantage would be to have fewer processes in `ps -a`. + * + * # Preconditions + * This function assumes that the crash-tracker has previously been + * initialized. + * # Safety + * Crash-tracking functions are not reentrant. + * No other crash-handler functions should be called concurrently. + * # Atomicity + * This function is not atomic. A crash during its execution may lead to + * unexpected crash-handling behaviour. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_update_on_fork(struct ddog_crasht_Config config, + struct ddog_crasht_ReceiverConfig receiver_config, + struct ddog_crasht_Metadata metadata); + +/** + * Initialize the crash-tracking infrastructure. + * + * # Preconditions + * None. + * # Safety + * Crash-tracking functions are not reentrant. + * No other crash-handler functions should be called concurrently. + * # Atomicity + * This function is not atomic. A crash during its execution may lead to + * unexpected crash-handling behaviour. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_init_with_receiver(struct ddog_crasht_Config config, + struct ddog_crasht_ReceiverConfig receiver_config, + struct ddog_crasht_Metadata metadata); + +/** + * Initialize the crash-tracking infrastructure, writing to an unix socket in case of crash. + * + * # Preconditions + * None. + * # Safety + * Crash-tracking functions are not reentrant. + * No other crash-handler functions should be called concurrently. + * # Atomicity + * This function is not atomic. A crash during its execution may lead to + * unexpected crash-handling behaviour. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_init_with_unix_socket(struct ddog_crasht_Config config, + ddog_CharSlice socket_path, + struct ddog_crasht_Metadata metadata); + +/** + * Resets all counters to 0. + * Expected to be used after a fork, to reset the counters on the child + * ATOMICITY: + * This is NOT ATOMIC. + * Should only be used when no conflicting updates can occur, + * e.g. after a fork but before profiling ops start on the child. + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_reset_counters(void); + +/** + * Atomically increments the count associated with `op`. + * Useful for tracking what operations were occuring when a crash occurred. + * + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_begin_op(enum ddog_crasht_OpTypes op); + +/** + * Atomically decrements the count associated with `op`. + * Useful for tracking what operations were occuring when a crash occurred. + * + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_end_op(enum ddog_crasht_OpTypes op); + +/** + * Resets all stored spans to 0. + * Expected to be used after a fork, to reset the spans on the child + * ATOMICITY: + * This is NOT ATOMIC. + * Should only be used when no conflicting updates can occur, + * e.g. after a fork but before profiling ops start on the child. + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_clear_span_ids(void); + +/** + * Resets all stored traces to 0. + * Expected to be used after a fork, to reset the traces on the child + * ATOMICITY: + * This is NOT ATOMIC. + * Should only be used when no conflicting updates can occur, + * e.g. after a fork but before profiling ops start on the child. + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_clear_trace_ids(void); + +/** + * Atomically registers an active traceId. + * Useful for tracking what operations were occurring when a crash occurred. + * 0 is reserved for "NoId" + * The set does not check for duplicates. Adding the same id twice is an error. + * + * Inputs: + * id: the 128 bit id, broken into 2 64 bit chunks (see note) + * + * Returns: + * Ok(handle) on success. The handle is needed to later remove the id; + * Err() on failure. The most likely cause of failure is that the underlying set is full. + * + * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 + * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html + * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * until we can upgrade. + * + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_UsizeResult ddog_crasht_insert_trace_id(uint64_t id_high, + uint64_t id_low); + +/** + * Atomically registers an active SpanId. + * Useful for tracking what operations were occurring when a crash occurred. + * 0 is reserved for "NoId". + * The set does not check for duplicates. Adding the same id twice is an error. + * + * Inputs: + * id: the 128 bit id, broken into 2 64 bit chunks (see note) + * + * Returns: + * Ok(handle) on success. The handle is needed to later remove the id; + * Err() on failure. The most likely cause of failure is that the underlying set is full. + * + * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 + * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html + * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * until we can upgrade. + * + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_UsizeResult ddog_crasht_insert_span_id(uint64_t id_high, + uint64_t id_low); + +/** + * Atomically removes a completed SpanId. + * Useful for tracking what operations were occurring when a crash occurred. + * 0 is reserved for "NoId" + * + * Inputs: + * id: the 128 bit id, broken into 2 64 bit chunks (see note) + * idx: The handle for the id, from a previous successful call to `insert_span_id`. + * Attempting to remove the same element twice is an error. + * Returns: + * `Ok` on success. + * `Err` on failure. If `id` is not found at `idx`, `Err` will be returned and the set will not + * be modified. + * + * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 + * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html + * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * until we can upgrade. + * + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_remove_span_id(uint64_t id_high, + uint64_t id_low, + uintptr_t idx); + +/** + * Atomically removes a completed TraceId. + * Useful for tracking what operations were occurring when a crash occurred. + * 0 is reserved for "NoId" + * + * Inputs: + * id: the 128 bit id, broken into 2 64 bit chunks (see note) + * idx: The handle for the id, from a previous successful call to `insert_span_id`. + * Attempting to remove the same element twice is an error. + * Returns: + * `Ok` on success. + * `Err` on failure. If `id` is not found at `idx`, `Err` will be returned and the set will not + * be modified. + * + * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 + * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html + * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * until we can upgrade. + * + * # Safety + * No safety concerns. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_remove_trace_id(uint64_t id_high, + uint64_t id_low, + uintptr_t idx); + +/** + * Create a new crashinfo, and returns an opaque reference to it. + * # Safety + * No safety issues. + */ +DDOG_CHECK_RETURN struct ddog_crasht_CrashInfoNewResult ddog_crasht_CrashInfo_new(void); + +/** + * # Safety + * The `crash_info` can be null, but if non-null it must point to a CrashInfo + * made by this module, which has not previously been dropped. + */ +void ddog_crasht_CrashInfo_drop(struct ddog_crasht_CrashInfo *crashinfo); + +/** + * Best effort attempt to normalize all `ip` on the stacktrace. + * `pid` must be the pid of the currently active process where the ips came from. + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_normalize_ips(struct ddog_crasht_CrashInfo *crashinfo, + uint32_t pid); + +/** + * Adds a "counter" variable, with the given value. Useful for determining if + * "interesting" operations were occurring when the crash did. + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + * `name` should be a valid reference to a utf8 encoded String. + * The string is copied into the crashinfo, so it does not need to outlive this + * call. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_add_counter(struct ddog_crasht_CrashInfo *crashinfo, + ddog_CharSlice name, + int64_t val); + +/** + * Adds the contents of "file" to the crashinfo + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + * `name` should be a valid reference to a utf8 encoded String. + * The string is copied into the crashinfo, so it does not need to outlive this + * call. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_add_file(struct ddog_crasht_CrashInfo *crashinfo, + ddog_CharSlice filename); + +/** + * Adds the tag with given "key" and "value" to the crashinfo + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + * `key` should be a valid reference to a utf8 encoded String. + * `value` should be a valid reference to a utf8 encoded String. + * The string is copied into the crashinfo, so it does not need to outlive this + * call. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_add_tag(struct ddog_crasht_CrashInfo *crashinfo, + ddog_CharSlice key, + ddog_CharSlice value); + +/** + * Sets the crashinfo metadata + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + * All references inside `metadata` must be valid. + * Strings are copied into the crashinfo, and do not need to outlive this call. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_set_metadata(struct ddog_crasht_CrashInfo *crashinfo, + struct ddog_crasht_Metadata metadata); + +/** + * Sets the crashinfo siginfo + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + * All references inside `metadata` must be valid. + * Strings are copied into the crashinfo, and do not need to outlive this call. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_set_siginfo(struct ddog_crasht_CrashInfo *crashinfo, + struct ddog_crasht_SigInfo siginfo); + +/** + * If `thread_id` is empty, sets `stacktrace` as the default stacktrace. + * Otherwise, adds an additional stacktrace with id "thread_id". + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + * All references inside `stacktraces` must be valid. + * Strings are copied into the crashinfo, and do not need to outlive this call. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_set_stacktrace(struct ddog_crasht_CrashInfo *crashinfo, + ddog_CharSlice thread_id, + struct ddog_crasht_Slice_StackFrame stacktrace); + +/** + * Sets the timestamp to the given unix timestamp + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_set_timestamp(struct ddog_crasht_CrashInfo *crashinfo, + struct ddog_Timespec ts); + +/** + * Sets the timestamp to the current time + * + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_set_timestamp_to_now(struct ddog_crasht_CrashInfo *crashinfo); + +/** + * Exports `crashinfo` to the backend at `endpoint` + * Note that we support the "file://" endpoint for local file output. + * # Safety + * `crashinfo` must be a valid pointer to a `CrashInfo` object. + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_CrashInfo_upload_to_endpoint(struct ddog_crasht_CrashInfo *crashinfo, + const struct ddog_Endpoint *endpoint); + +/** + * Demangles the string "name". + * If demangling fails, returns an empty string "" + * + * # Safety + * `name` should be a valid reference to a utf8 encoded String. + * The string is copied into the result, and does not need to outlive this call + */ +DDOG_CHECK_RETURN +struct ddog_crasht_StringWrapperResult ddog_crasht_demangle(ddog_CharSlice name, + enum ddog_crasht_DemangleOptions options); + +/** + * Receives data from a crash collector via a pipe on `stdin`, formats it into + * `CrashInfo` json, and emits it to the endpoint/file defined in `config`. + * + * At a high-level, this exists because doing anything in a + * signal handler is dangerous, so we fork a sidecar to do the stuff we aren't + * allowed to do in the handler. + * + * See comments in [crashtracker/lib.rs] for a full architecture description. + * # Safety + * No safety concerns + */ +DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_receiver_entry_point_stdin(void); + +/** + * Receives data from a crash collector via a pipe on `stdin`, formats it into + * `CrashInfo` json, and emits it to the endpoint/file defined in `config`. + * + * At a high-level, this exists because doing anything in a + * signal handler is dangerous, so we fork a sidecar to do the stuff we aren't + * allowed to do in the handler. + * + * See comments in [profiling/crashtracker/mod.rs] for a full architecture + * description. + * # Safety + * No safety concerns + */ +DDOG_CHECK_RETURN +struct ddog_crasht_Result ddog_crasht_receiver_entry_point_unix_socket(ddog_CharSlice socket_path); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif /* DDOG_CRASHTRACKER_H */ diff --git a/components-rs/lib.rs b/components-rs/lib.rs index 33a88b7cb3..1e70a825e2 100644 --- a/components-rs/lib.rs +++ b/components-rs/lib.rs @@ -13,6 +13,7 @@ use ddcommon::entity_id::{get_container_id, set_cgroup_file}; use ddcommon_ffi::CharSlice; use uuid::Uuid; +pub use datadog_crashtracker_ffi::*; pub use datadog_sidecar_ffi::*; use ddcommon_ffi::slice::AsBytes; pub use ddcommon_ffi::*; diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index bb36a55a31..259bff28a0 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -60,6 +60,21 @@ void ddog_agent_remote_config_reader_drop(struct ddog_AgentRemoteConfigReader*); void ddog_agent_remote_config_writer_drop(struct ddog_AgentRemoteConfigWriter_ShmHandle*); +struct ddog_RemoteConfigReader *ddog_remote_config_reader_for_endpoint(const ddog_CharSlice *language, + const ddog_CharSlice *tracer_version, + const struct ddog_Endpoint *endpoint, + ddog_CharSlice service_name, + ddog_CharSlice env_name, + ddog_CharSlice app_version, + const enum ddog_RemoteConfigProduct *remote_config_products, + uintptr_t remote_config_products_count, + const enum ddog_RemoteConfigCapabilities *remote_config_capabilities, + uintptr_t remote_config_capabilities_count); + +bool ddog_remote_config_read(struct ddog_RemoteConfigReader *reader, ddog_CharSlice *data); + +void ddog_remote_config_reader_drop(struct ddog_RemoteConfigReader*); + void ddog_sidecar_transport_drop(struct ddog_SidecarTransport*); /** @@ -127,7 +142,7 @@ ddog_MaybeError ddog_sidecar_telemetry_flushServiceData(struct ddog_SidecarTrans /** * Enqueues a list of actions to be performed. */ -ddog_MaybeError ddog_sidecar_telemetry_end(struct ddog_SidecarTransport **transport, +ddog_MaybeError ddog_sidecar_lifecycle_end(struct ddog_SidecarTransport **transport, const struct ddog_InstanceId *instance_id, const ddog_QueueId *queue_id); @@ -150,12 +165,19 @@ ddog_MaybeError ddog_sidecar_session_set_config(struct ddog_SidecarTransport **t ddog_CharSlice session_id, const struct ddog_Endpoint *agent_endpoint, const struct ddog_Endpoint *dogstatsd_endpoint, - uint64_t flush_interval_milliseconds, - uint64_t telemetry_heartbeat_interval_millis, + ddog_CharSlice language, + ddog_CharSlice tracer_version, + uint32_t flush_interval_milliseconds, + uint32_t telemetry_heartbeat_interval_millis, uintptr_t force_flush_size, uintptr_t force_drop_size, ddog_CharSlice log_level, - ddog_CharSlice log_path); + ddog_CharSlice log_path, + void *remote_config_notify_function, + const enum ddog_RemoteConfigProduct *remote_config_products, + uintptr_t remote_config_products_count, + const enum ddog_RemoteConfigCapabilities *remote_config_capabilities, + uintptr_t remote_config_capabilities_count); /** * Sends a trace to the sidecar via shared memory. @@ -174,6 +196,13 @@ ddog_MaybeError ddog_sidecar_send_trace_v04_bytes(struct ddog_SidecarTransport * ddog_CharSlice data, const struct ddog_TracerHeaderTags *tracer_header_tags); +ddog_MaybeError ddog_sidecar_set_remote_config_data(struct ddog_SidecarTransport **transport, + const struct ddog_InstanceId *instance_id, + const ddog_QueueId *queue_id, + ddog_CharSlice service_name, + ddog_CharSlice env_name, + ddog_CharSlice app_version); + /** * Dumps the current state of the sidecar. */ @@ -248,4 +277,9 @@ ddog_MaybeError ddog_sidecar_set_test_session_token(struct ddog_SidecarTransport void ddog_sidecar_reconnect(struct ddog_SidecarTransport **transport, struct ddog_SidecarTransport *(*factory)(void)); +/** + * Return the path of the crashtracker unix domain socket. + */ +ddog_CharSlice ddog_sidecar_get_crashtracker_unix_socket_path(void); + #endif /* DDOG_SIDECAR_H */ diff --git a/ext/configuration.h b/ext/configuration.h index a44c507efe..df5aa2c56b 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -162,7 +162,7 @@ enum ddtrace_sampling_rules_format { CONFIG(INT, DD_TRACE_AGENT_CONNECT_TIMEOUT, DD_CFG_EXPSTR(DD_TRACE_AGENT_CONNECT_TIMEOUT_VAL), \ .ini_change = zai_config_system_ini_change) \ CONFIG(INT, DD_TRACE_DEBUG_PRNG_SEED, "-1", .ini_change = ddtrace_reseed_seed_change) \ - CONFIG(BOOL, DD_LOG_BACKTRACE, "false") \ + CONFIG(BOOL, DD_LOG_BACKTRACE, "true") \ CONFIG(BOOL, DD_TRACE_GENERATE_ROOT_SPAN, "true", .ini_change = ddtrace_span_alter_root_span_config) \ CONFIG(INT, DD_TRACE_SPANS_LIMIT, "1000") \ CONFIG(BOOL, DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED, "true") \ diff --git a/ext/hook/uhook.c b/ext/hook/uhook.c index 9ea4071392..03a46b9813 100644 --- a/ext/hook/uhook.c +++ b/ext/hook/uhook.c @@ -721,10 +721,6 @@ PHP_FUNCTION(DDTrace_remove_hook) { zai_str scope = zai_str_from_zstr(def->scope); zai_str function = zai_str_from_zstr(def->function); if (location && ZSTR_LEN(location)) { - zend_string *lower = zend_string_tolower(location); - zai_hook_exclude_class(scope, function, id, lower); - zend_string_release(lower); - LOG(HOOK_TRACE, "Excluding class %s from hook %d at %s:%d on %s %s%s%s", ZSTR_VAL(location), id, @@ -733,9 +729,11 @@ PHP_FUNCTION(DDTrace_remove_hook) { def->scope ? ZSTR_VAL(def->scope) : "", def->scope ? "::" : "", def->file ? ZSTR_VAL(def->file) : ZSTR_VAL(def->function)); - } else { - zai_hook_remove(scope, function, id); + zend_string *lower = zend_string_tolower(location); + zai_hook_exclude_class(scope, function, id, lower); + zend_string_release(lower); + } else { LOG(HOOK_TRACE, "Removing hook %d at %s:%d on %s %s%s%s", id, zend_get_executed_filename(), zend_get_executed_lineno(), @@ -743,6 +741,8 @@ PHP_FUNCTION(DDTrace_remove_hook) { def->scope ? ZSTR_VAL(def->scope) : "", def->scope ? "::" : "", def->file ? ZSTR_VAL(def->file) : ZSTR_VAL(def->function)); + + zai_hook_remove(scope, function, id); } } else { if (location && ZSTR_LEN(location)) { @@ -750,15 +750,22 @@ PHP_FUNCTION(DDTrace_remove_hook) { zai_hook_exclude_class_resolved(def->install_address, id, lower); zend_string_release(lower); } else { - zai_hook_remove_resolved(def->install_address, id); + if (def->closure) { + const zend_function *closure = zend_get_closure_method_def(def->closure); + LOG(HOOK_TRACE, "Removing hook %d at %s:%d on %s %s%s%s", + id, + zend_get_executed_filename(), zend_get_executed_lineno(), + closure->common.scope ? "method" : "function", + closure->common.scope ? ZSTR_VAL(closure->common.scope->name) : "", + closure->common.scope ? "::" : "", + ZSTR_VAL(closure->common.function_name)); + } else { + LOG(HOOK_TRACE, "Removing hook %d at %s:%d", + id, + zend_get_executed_filename(), zend_get_executed_lineno()); + } - LOG(HOOK_TRACE, "Removing hook %d at %s:%d on %s %s%s%s", - id, - zend_get_executed_filename(), zend_get_executed_lineno(), - def->file ? "file" : (def->scope ? "method" : "function"), - def->scope ? ZSTR_VAL(def->scope) : "", - def->scope ? "::" : "", - def->file ? ZSTR_VAL(def->file) : ZSTR_VAL(def->function)); + zai_hook_remove_resolved(def->install_address, id); } } } diff --git a/ext/sidecar.c b/ext/sidecar.c index e2aeaa9cb8..5cb3df0ebc 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -22,14 +22,7 @@ static uint8_t dd_sidecar_formatted_session_id[36]; // Set the globals that stay unchanged in case of fork static void ddtrace_set_non_resettable_sidecar_globals(void) { ddtrace_format_runtime_id(&dd_sidecar_formatted_session_id); - - if (get_global_DD_TRACE_AGENTLESS() && ZSTR_LEN(get_global_DD_API_KEY())) { - ddtrace_endpoint = ddog_endpoint_from_api_key(dd_zend_string_to_CharSlice(get_global_DD_API_KEY())); - } else { - char *agent_url = ddtrace_agent_url(); - ddtrace_endpoint = ddog_endpoint_from_url((ddog_CharSlice) {.ptr = agent_url, .len = strlen(agent_url)}); - free(agent_url); - } + ddtrace_endpoint = ddtrace_sidecar_agent_endpoint(); if (ZSTR_LEN(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())) { ddog_endpoint_set_test_token(ddtrace_endpoint, dd_zend_string_to_CharSlice(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())); @@ -45,6 +38,9 @@ static void ddtrace_set_resettable_sidecar_globals(void) { ddtrace_sidecar_instance_id = ddog_sidecar_instanceId_build(session_id, runtime_id); } +const enum ddog_RemoteConfigProduct remote_config_products[1] = { DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING }; +const enum ddog_RemoteConfigCapabilities remote_config_capabilities[1] = { DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_ENABLED }; + ddog_SidecarTransport *dd_sidecar_connection_factory(void) { // Should not happen if (!ddtrace_endpoint) { @@ -82,13 +78,23 @@ ddog_SidecarTransport *dd_sidecar_connection_factory(void) { ddog_CharSlice session_id = (ddog_CharSlice) {.ptr = (char *) dd_sidecar_formatted_session_id, .len = sizeof(dd_sidecar_formatted_session_id)}; ddog_sidecar_session_set_config(&sidecar_transport, session_id, ddtrace_endpoint, dogstatsd_endpoint, + DDOG_CHARSLICE_C("php"), + DDOG_CHARSLICE_C(PHP_DDTRACE_VERSION), get_global_DD_TRACE_AGENT_FLUSH_INTERVAL(), // for historical reasons in seconds get_global_DD_TELEMETRY_HEARTBEAT_INTERVAL() * 1000, get_global_DD_TRACE_BUFFER_SIZE(), get_global_DD_TRACE_AGENT_STACK_BACKLOG() * get_global_DD_TRACE_AGENT_MAX_PAYLOAD_SIZE(), get_global_DD_TRACE_DEBUG() ? DDOG_CHARSLICE_C("debug") : dd_zend_string_to_CharSlice(get_global_DD_TRACE_LOG_LEVEL()), - (ddog_CharSlice){ .ptr = logpath, .len = strlen(logpath) }); + (ddog_CharSlice){ .ptr = logpath, .len = strlen(logpath) }, + + // Not used yet + NULL, + remote_config_products, + 0, + remote_config_capabilities, + 0 + ); ddog_endpoint_drop(dogstatsd_endpoint); @@ -163,7 +169,21 @@ void ddtrace_reset_sidecar_globals(void) { } } -static inline void ddtrace_sidecar_dogstatsd_push_tag(ddog_Vec_Tag *vec, ddog_CharSlice key, ddog_CharSlice value) { +ddog_Endpoint *ddtrace_sidecar_agent_endpoint(void) { + ddog_Endpoint *agent_endpoint; + + if (get_global_DD_TRACE_AGENTLESS() && ZSTR_LEN(get_global_DD_API_KEY())) { + agent_endpoint = ddog_endpoint_from_api_key(dd_zend_string_to_CharSlice(get_global_DD_API_KEY())); + } else { + char *agent_url = ddtrace_agent_url(); + agent_endpoint = ddog_endpoint_from_url((ddog_CharSlice) {.ptr = agent_url, .len = strlen(agent_url)}); + free(agent_url); + } + + return agent_endpoint; +} + +static inline void ddtrace_sidecar_push_tag(ddog_Vec_Tag *vec, ddog_CharSlice key, ddog_CharSlice value) { ddog_Vec_Tag_PushResult tag_result = ddog_Vec_Tag_push(vec, key, value); if (tag_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) { zend_string *msg = dd_CharSlice_to_zend_string(ddog_Error_message(&tag_result.err)); @@ -173,7 +193,7 @@ static inline void ddtrace_sidecar_dogstatsd_push_tag(ddog_Vec_Tag *vec, ddog_Ch } } -static void ddtrace_sidecar_dogstatsd_push_tags(ddog_Vec_Tag *vec, zval *tags) { +void ddtrace_sidecar_push_tags(ddog_Vec_Tag *vec, zval *tags) { // Global tags (https://github.com/DataDog/php-datadogstatsd/blob/0efdd1c38f6d3dd407efbb899ad1fd2e5cd18085/src/DogStatsd.php#L113-L125) ddtrace_span_data *span = ddtrace_active_span(); zend_string *env; @@ -183,12 +203,12 @@ static void ddtrace_sidecar_dogstatsd_push_tags(ddog_Vec_Tag *vec, zval *tags) { env = zend_string_copy(get_DD_ENV()); } if (ZSTR_LEN(env) > 0) { - ddtrace_sidecar_dogstatsd_push_tag(vec, DDOG_CHARSLICE_C("env"), dd_zend_string_to_CharSlice(env)); + ddtrace_sidecar_push_tag(vec, DDOG_CHARSLICE_C("env"), dd_zend_string_to_CharSlice(env)); } zend_string_release(env); zend_string *service = ddtrace_active_service_name(); if (ZSTR_LEN(service) > 0) { - ddtrace_sidecar_dogstatsd_push_tag(vec, DDOG_CHARSLICE_C("service"), dd_zend_string_to_CharSlice(service)); + ddtrace_sidecar_push_tag(vec, DDOG_CHARSLICE_C("service"), dd_zend_string_to_CharSlice(service)); } zend_string_release(service); zend_string *version; @@ -198,12 +218,12 @@ static void ddtrace_sidecar_dogstatsd_push_tags(ddog_Vec_Tag *vec, zval *tags) { version = zend_string_copy(get_DD_VERSION()); } if (ZSTR_LEN(version) > 0) { - ddtrace_sidecar_dogstatsd_push_tag(vec, DDOG_CHARSLICE_C("version"), dd_zend_string_to_CharSlice(version)); + ddtrace_sidecar_push_tag(vec, DDOG_CHARSLICE_C("version"), dd_zend_string_to_CharSlice(version)); } zend_string_release(version); if (ZSTR_LEN(get_DD_TRACE_AGENT_TEST_SESSION_TOKEN())) { - ddtrace_sidecar_dogstatsd_push_tag(vec, DDOG_CHARSLICE_C("x-datadog-test-session-token"), dd_zend_string_to_CharSlice(get_DD_TRACE_AGENT_TEST_SESSION_TOKEN())); + ddtrace_sidecar_push_tag(vec, DDOG_CHARSLICE_C("x-datadog-test-session-token"), dd_zend_string_to_CharSlice(get_DD_TRACE_AGENT_TEST_SESSION_TOKEN())); } // Specific tags @@ -219,7 +239,7 @@ static void ddtrace_sidecar_dogstatsd_push_tags(ddog_Vec_Tag *vec, zval *tags) { } zval value_str; ddtrace_convert_to_string(&value_str, tag_val); - ddtrace_sidecar_dogstatsd_push_tag(vec, dd_zend_string_to_CharSlice(key), dd_zend_string_to_CharSlice(Z_STR(value_str))); + ddtrace_sidecar_push_tag(vec, dd_zend_string_to_CharSlice(key), dd_zend_string_to_CharSlice(Z_STR(value_str))); zend_string_release(Z_STR(value_str)); } ZEND_HASH_FOREACH_END(); @@ -231,8 +251,9 @@ void ddtrace_sidecar_dogstatsd_count(zend_string *metric, zend_long value, zval } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); - ddtrace_sidecar_dogstatsd_push_tags(&vec, tags); - ddog_sidecar_dogstatsd_count(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec); + ddtrace_sidecar_push_tags(&vec, tags); + ddtrace_ffi_try("Failed sending dogstatsd count metric", + ddog_sidecar_dogstatsd_count(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } @@ -242,8 +263,9 @@ void ddtrace_sidecar_dogstatsd_distribution(zend_string *metric, double value, z } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); - ddtrace_sidecar_dogstatsd_push_tags(&vec, tags); - ddog_sidecar_dogstatsd_distribution(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec); + ddtrace_sidecar_push_tags(&vec, tags); + ddtrace_ffi_try("Failed sending dogstatsd distribution metric", + ddog_sidecar_dogstatsd_distribution(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } @@ -253,8 +275,9 @@ void ddtrace_sidecar_dogstatsd_gauge(zend_string *metric, double value, zval *ta } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); - ddtrace_sidecar_dogstatsd_push_tags(&vec, tags); - ddog_sidecar_dogstatsd_gauge(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec); + ddtrace_sidecar_push_tags(&vec, tags); + ddtrace_ffi_try("Failed sending dogstatsd gauge metric", + ddog_sidecar_dogstatsd_gauge(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } @@ -264,8 +287,9 @@ void ddtrace_sidecar_dogstatsd_histogram(zend_string *metric, double value, zval } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); - ddtrace_sidecar_dogstatsd_push_tags(&vec, tags); - ddog_sidecar_dogstatsd_histogram(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec); + ddtrace_sidecar_push_tags(&vec, tags); + ddtrace_ffi_try("Failed sending dogstatsd histogram metric", + ddog_sidecar_dogstatsd_histogram(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } @@ -275,8 +299,9 @@ void ddtrace_sidecar_dogstatsd_set(zend_string *metric, zend_long value, zval *t } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); - ddtrace_sidecar_dogstatsd_push_tags(&vec, tags); - ddog_sidecar_dogstatsd_set(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec); + ddtrace_sidecar_push_tags(&vec, tags); + ddtrace_ffi_try("Failed sending dogstatsd set metric", + ddog_sidecar_dogstatsd_set(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } @@ -284,7 +309,8 @@ bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value) { UNUSED(old_value); if (ddtrace_sidecar) { ddog_CharSlice session_id = (ddog_CharSlice) {.ptr = (char *) dd_sidecar_formatted_session_id, .len = sizeof(dd_sidecar_formatted_session_id)}; - ddog_sidecar_set_test_session_token(&ddtrace_sidecar, session_id, dd_zend_string_to_CharSlice(Z_STR_P(new_value))); + ddtrace_ffi_try("Failed updating test session token", + ddog_sidecar_set_test_session_token(&ddtrace_sidecar, session_id, dd_zend_string_to_CharSlice(Z_STR_P(new_value)))); } #ifndef _WIN32 ddtrace_coms_set_test_session_token(Z_STRVAL_P(new_value), Z_STRLEN_P(new_value)); diff --git a/ext/sidecar.h b/ext/sidecar.h index 37a0498beb..9123e2d171 100644 --- a/ext/sidecar.h +++ b/ext/sidecar.h @@ -9,6 +9,8 @@ void ddtrace_sidecar_setup(void); void ddtrace_sidecar_ensure_active(void); void ddtrace_sidecar_shutdown(void); void ddtrace_reset_sidecar_globals(void); +void ddtrace_sidecar_push_tags(ddog_Vec_Tag *vec, zval *tags); +ddog_Endpoint *ddtrace_sidecar_agent_endpoint(void); void ddtrace_sidecar_dogstatsd_count(zend_string *metric, zend_long value, zval *tags); void ddtrace_sidecar_dogstatsd_distribution(zend_string *metric, double value, zval *tags); diff --git a/ext/signals.c b/ext/signals.c index c90f91b990..c91ccfea32 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -15,9 +15,15 @@ #include "configuration.h" #include "ddtrace.h" +#include "sidecar.h" +#include "auto_flush.h" #include "ext/version.h" #include +#include +#include +#include + #if defined HAVE_EXECINFO_H && defined backtrace_size_t && defined HAVE_BACKTRACE #define DDTRACE_HAVE_BACKTRACE 1 #else @@ -31,6 +37,7 @@ // true globals; only modify in MINIT/MSHUTDOWN static stack_t ddtrace_altstack; static struct sigaction ddtrace_sigaction; +static ddog_CharSlice crashtracker_socket_path = {0}; #define MAX_STACK_SIZE 1024 #define MIN_STACKSZ 16384 // enough to hold void *array[MAX_STACK_SIZE] plus a couple kilobytes @@ -82,6 +89,49 @@ static void ddtrace_sigsegv_handler(int sig) { _Exit(128 + sig); } +static bool ddtrace_crashtracker_check_result(ddog_crasht_Result result, const char *msg) { + if (result.tag != DDOG_CRASHT_RESULT_OK) { + ddog_CharSlice error_msg = ddog_Error_message(&result.err); + LOG(ERROR, "%s : %.*s", msg, (int) error_msg.len, error_msg.ptr); + ddog_Error_drop(&result.err); + return false; + } + + return true; +} + +static void ddtrace_init_crashtracker() { + ddog_Endpoint *agent_endpoint = ddtrace_sidecar_agent_endpoint(); + crashtracker_socket_path = ddog_sidecar_get_crashtracker_unix_socket_path(); + + ddog_crasht_Config config = { + .endpoint = agent_endpoint, + .timeout_secs = 5, + }; + + ddog_Vec_Tag tags = ddog_Vec_Tag_new(); + ddtrace_sidecar_push_tags(&tags, NULL); + + const ddog_crasht_Metadata metadata = { + .library_name = DDOG_CHARSLICE_C_BARE("dd-trace-php"), + .library_version = DDOG_CHARSLICE_C_BARE(PHP_DDTRACE_VERSION), + .family = DDOG_CHARSLICE_C("php"), + .tags = &tags + }; + + ddtrace_crashtracker_check_result( + ddog_crasht_init_with_unix_socket( + config, + crashtracker_socket_path, + metadata + ), + "Cannot initialize CrashTracker" + ); + + ddog_endpoint_drop(agent_endpoint); + ddog_Vec_Tag_drop(tags); +} + void ddtrace_signals_first_rinit(void) { bool install_handler = get_DD_TRACE_HEALTH_METRICS_ENABLED(); @@ -107,10 +157,19 @@ void ddtrace_signals_first_rinit(void) { sigaction(SIGSEGV, &ddtrace_sigaction, NULL); } } + + if (get_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + ddtrace_init_crashtracker(); + } } } -void ddtrace_signals_mshutdown(void) { free(ddtrace_altstack.ss_sp); } +void ddtrace_signals_mshutdown(void) { + free(ddtrace_altstack.ss_sp); + if (crashtracker_socket_path.ptr) { + free((void *) crashtracker_socket_path.ptr); + } +} #else void ddtrace_signals_first_rinit(void) {} diff --git a/ext/telemetry.c b/ext/telemetry.c index 13277ed81f..a888170946 100644 --- a/ext/telemetry.c +++ b/ext/telemetry.c @@ -53,14 +53,16 @@ void ddtrace_telemetry_register_services(ddog_SidecarTransport *sidecar) { ddog_sidecar_telemetry_register_metric_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), DDOG_METRIC_NAMESPACE_TRACERS); // FIXME: it seems we must call "enqueue_actions" (even with an empty list of actions) for things to work properly - ddog_sidecar_telemetry_buffer_flush(&sidecar, ddtrace_sidecar_instance_id, &dd_bgs_queued_id, buffer); + ddtrace_ffi_try("Failed flushing background sender telemetry buffer", + ddog_sidecar_telemetry_buffer_flush(&sidecar, ddtrace_sidecar_instance_id, &dd_bgs_queued_id, buffer)); ddog_CharSlice php_version = dd_zend_string_to_CharSlice(Z_STR_P(zend_get_constant_str(ZEND_STRL("PHP_VERSION")))); struct ddog_RuntimeMetadata *meta = ddog_sidecar_runtimeMeta_build(DDOG_CHARSLICE_C("php"), php_version, DDOG_CHARSLICE_C(PHP_DDTRACE_VERSION)); - ddog_sidecar_telemetry_flushServiceData( - &sidecar, ddtrace_sidecar_instance_id, &dd_bgs_queued_id, meta, - DDOG_CHARSLICE_C("background_sender-php-service"), DDOG_CHARSLICE_C("none") - ); + ddtrace_ffi_try("Failed flushing background sender service data", + ddog_sidecar_telemetry_flushServiceData( + &sidecar, ddtrace_sidecar_instance_id, &dd_bgs_queued_id, meta, + DDOG_CHARSLICE_C("background_sender-php-service"), DDOG_CHARSLICE_C("none") + )); ddog_sidecar_runtimeMeta_drop(meta); } @@ -140,7 +142,8 @@ void ddtrace_telemetry_finalize(void) { } } - ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), buffer); + ddtrace_ffi_try("Failed flushing telemetry buffer", + ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), buffer)); ddog_CharSlice service_name = DDOG_CHARSLICE_C_BARE("unnamed-php-service"); if (DDTRACE_G(last_flushed_root_service_name)) { @@ -155,11 +158,13 @@ void ddtrace_telemetry_finalize(void) { ddog_CharSlice php_version = dd_zend_string_to_CharSlice(Z_STR_P(zend_get_constant_str(ZEND_STRL("PHP_VERSION")))); struct ddog_RuntimeMetadata *meta = ddog_sidecar_runtimeMeta_build(DDOG_CHARSLICE_C("php"), php_version, DDOG_CHARSLICE_C(PHP_DDTRACE_VERSION)); - ddog_sidecar_telemetry_flushServiceData(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), meta, service_name, env_name); + ddtrace_ffi_try("Failed flushing service data", + ddog_sidecar_telemetry_flushServiceData(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), meta, service_name, env_name)); ddog_sidecar_runtimeMeta_drop(meta); - ddog_sidecar_telemetry_end(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id)); + ddtrace_ffi_try("Failed signaling lifecycle end", + ddog_sidecar_lifecycle_end(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id))); } void ddtrace_telemetry_notify_integration(const char *name, size_t name_len) { @@ -238,5 +243,6 @@ void ddtrace_telemetry_send_trace_api_metrics(trace_api_metrics metrics) { ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), (double)metrics.errors_status_code, DDOG_CHARSLICE_C("type:status_code")); } - ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &dd_bgs_queued_id, buffer); + ddtrace_ffi_try("Failed flushing background sender metrics", + ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &dd_bgs_queued_id, buffer)); } diff --git a/libdatadog b/libdatadog index 16528ffee4..f55df58ddb 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 16528ffee456f7af5fe9ad80a6294fb5dcd38918 +Subproject commit f55df58ddba5e34ccd88fa4c61f068184fce7ee9 diff --git a/tests/ext/crashtracker_segfault.phpt b/tests/ext/crashtracker_segfault.phpt new file mode 100644 index 0000000000..40dd40b3bc --- /dev/null +++ b/tests/ext/crashtracker_segfault.phpt @@ -0,0 +1,70 @@ +--TEST-- +Send crashtracker report when segmentation fault signal is raised and config enables it +--SKIPIF-- + +--ENV-- +DD_TRACE_SIDECAR_TRACE_SENDER=1 +--INI-- +datadog.trace.agent_url="file://{PWD}/crashtracker_segfault_agent.out" +--FILE-- + +--EXPECTF-- +%A + "counters": { +%A + }, + "files": { + "/proc/self/maps": [ +%A + ] + }, + "incomplete": false, + "metadata": { + "library_name": "dd-trace-php", + "library_version": "%s", + "family": "php", + "tags": [ + "service:Standard input code" + ] + }, + "os_info": { +%A + }, + "proc_info": { + "pid": %d + }, + "siginfo": { + "signum": 11, + "signame": "SIGSEGV" + }, + "timestamp": "%s", + "uuid": "%s" +%A +--CLEAN-- + +--ENV-- +DD_LOG_BACKTRACE=0 --FILE-- --XFAIL-- Code called in the segv handler is not signal safe, this will sometimes result in a segfault. ---ENV-- -DD_LOG_BACKTRACE=1 --FILE-- Date: Tue, 27 Aug 2024 15:52:12 +0200 Subject: [PATCH 043/103] fix: MicroBenchmarks Failures (#2815) * Add `phpbench/phpbench` to `tests/composer.json` * Change `PHPBENCH` path * Change `$schema` path * Use bootstrap_phpunit instead of bootstrap_common * Update `runner.path` and `composer.json` dependencies * Change WordPress `dbname` to `wp61` * Create wp61 database if it doesn't exist --- Makefile | 2 +- tests/Benchmarks/Integrations/WordPressBench.php | 3 ++- tests/Benchmarks/composer.json | 4 +++- tests/bootstrap_phpbench.php | 2 +- tests/phpbench-opcache.json | 4 ++-- tests/phpbench.json | 4 ++-- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 456ac3ac21..08898e7d30 100644 --- a/Makefile +++ b/Makefile @@ -506,7 +506,7 @@ PHPUNIT_COVERAGE ?= PHPBENCH_OPTS ?= PHPBENCH_CONFIG ?= $(TESTS_ROOT)/phpbench.json PHPBENCH_OPCACHE_CONFIG ?= $(TESTS_ROOT)/phpbench-opcache.json -PHPBENCH = $(TESTS_ROOT)/vendor/bin/phpbench $(PHPBENCH_OPTS) run +PHPBENCH = $(TESTS_ROOT)/Benchmarks/vendor/bin/phpbench $(PHPBENCH_OPTS) run PHPCOV = $(TESTS_ROOT)/vendor/bin/phpcov TELEMETRY_ENABLED=0 diff --git a/tests/Benchmarks/Integrations/WordPressBench.php b/tests/Benchmarks/Integrations/WordPressBench.php index d33838021c..ff5aa223af 100644 --- a/tests/Benchmarks/Integrations/WordPressBench.php +++ b/tests/Benchmarks/Integrations/WordPressBench.php @@ -33,7 +33,8 @@ public static function getAppIndexScript() public function disableWordPressTracing() { - $pdo = new \PDO('mysql:host=mysql_integration;dbname=test', 'test', 'test'); + $pdo = new \PDO('mysql:host=mysql_integration', 'test', 'test'); + $pdo->exec('CREATE DATABASE IF NOT EXISTS wp61'); $pdo->exec(file_get_contents(__DIR__ . '/../../Frameworks/WordPress/Version_6_1/scripts/wp_initdb.sql')); $this->setUpWebServer([ 'DD_TRACE_ENABLED' => 0, diff --git a/tests/Benchmarks/composer.json b/tests/Benchmarks/composer.json index 962f122d40..20a6aaf663 100644 --- a/tests/Benchmarks/composer.json +++ b/tests/Benchmarks/composer.json @@ -2,6 +2,8 @@ "require": { "monolog/monolog": "~2.0", "open-telemetry/sdk": "@stable", - "phpbench/phpbench": "^1.0" + "phpbench/phpbench": "^1.0", + "phpunit/phpunit": "<10", + "symfony/process": "<5" } } diff --git a/tests/bootstrap_phpbench.php b/tests/bootstrap_phpbench.php index 749a900f41..245aa17a69 100644 --- a/tests/bootstrap_phpbench.php +++ b/tests/bootstrap_phpbench.php @@ -2,4 +2,4 @@ require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/Benchmarks/vendor/autoload.php'; -require_once __DIR__ . '/bootstrap_common.php'; +require_once __DIR__ . '/bootstrap_phpunit.php'; diff --git a/tests/phpbench-opcache.json b/tests/phpbench-opcache.json index da07cee8c6..675db25b29 100644 --- a/tests/phpbench-opcache.json +++ b/tests/phpbench-opcache.json @@ -1,7 +1,7 @@ { - "$schema":"vendor/phpbench/phpbench/phpbench.schema.json", + "$schema":"Benchmarks/vendor/phpbench/phpbench/phpbench.schema.json", "runner.bootstrap": "./bootstrap_phpbench.php", - "runner.path": "Benchmarks", + "runner.path": [ "Benchmarks/API", "Benchmarks/Integrations" ], "runner.file_pattern": "*Bench.php", "runner.php_env": { "DD_TRACE_OTEL_ENABLED": "true", diff --git a/tests/phpbench.json b/tests/phpbench.json index 579e4e5ab7..dc39b6d356 100644 --- a/tests/phpbench.json +++ b/tests/phpbench.json @@ -1,7 +1,7 @@ { - "$schema":"vendor/phpbench/phpbench/phpbench.schema.json", + "$schema":"Benchmarks/vendor/phpbench/phpbench/phpbench.schema.json", "runner.bootstrap": "./bootstrap_phpbench.php", - "runner.path": "Benchmarks", + "runner.path": [ "Benchmarks/API", "Benchmarks/Integrations" ], "runner.file_pattern": "*Bench.php", "runner.php_env": { "DD_TRACE_OTEL_ENABLED": "true", From b276644ff741059e82e2184edd94bce5c7cabb6c Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Tue, 27 Aug 2024 14:54:21 +0100 Subject: [PATCH 044/103] datadog-setup.php: fix and improve wording appsec settings descs (#2816) * datadog-setup.php: fix and improve wording appsec settings descs * Missing commas --------- Co-authored-by: Bob Weinand --- datadog-setup.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/datadog-setup.php b/datadog-setup.php index a54bad752b..e1c89dda27 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -2265,7 +2265,7 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'default' => $appsecHelperPath, 'commented' => false, 'description' => [ - 'The path to the shared library the appsec extension loads in the sidecar.', + 'The path to the shared library that the appsec extension loads in the sidecar.', 'This ini setting is configured by the installer', ], ], @@ -2274,7 +2274,7 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'default' => $appsecRulesPath, 'commented' => true, 'description' => [ - 'The path to the rules json file. The helper process must be able to read the', + 'The path to the rules json file. The sidecar process must be able to read the', 'file. This ini setting is configured by the installer', ], ], @@ -2283,9 +2283,9 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'default' => '/tmp/', 'commented' => true, 'description' => [ - 'The location to the UNIX socket that extension uses to communicate with the', - 'helper inside sidecar and to the lock file. Ultimately, the paths include' - 'the version of the extension and pid/gid.', + 'The directory where to place the lock file and the UNIX socket that the', + 'extension uses communicate with the helper inside sidecar. Ultimately,', + 'the paths include the version of the extension and uid/gid.', ], ], [ @@ -2293,7 +2293,7 @@ function get_ini_settings($sourcesDir, $appsecHelperPath, $appsecRulesPath) 'default' => '/dev/null', 'commented' => true, 'description' => [ - 'The location of the log file of the helper. This defaults to /dev/null ', + 'The location of the log file of the helper. This defaults to /dev/null', '(the log messages will be discarded).', ], ], From 5513f97af537b90c03ce237741c3440974c3f38f Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 27 Aug 2024 16:57:23 +0200 Subject: [PATCH 045/103] Bump to 1.3.0 (#2814) * Bump to 1.3.0 Signed-off-by: Bob Weinand * Add note about log_backtrace default change Signed-off-by: Bob Weinand --------- Signed-off-by: Bob Weinand --- VERSION | 2 +- libdatadog | 2 +- package.xml | 64 ++++++++++++++++++++++++++++------------------------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/VERSION b/VERSION index 867e52437a..589268e6fe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.0 \ No newline at end of file +1.3.0 \ No newline at end of file diff --git a/libdatadog b/libdatadog index f55df58ddb..0be5fad8da 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit f55df58ddba5e34ccd88fa4c61f068184fce7ee9 +Subproject commit 0be5fad8daf1559d9360d042802b7cb85689deca diff --git a/package.xml b/package.xml index ab1036b174..acbdff3ba4 100644 --- a/package.xml +++ b/package.xml @@ -74,53 +74,57 @@ BSD 3-Clause From 1ae62ef5cbb7446eb13f3fba40d8928eb4ae67d6 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Wed, 28 Aug 2024 18:17:16 +0200 Subject: [PATCH 046/103] Fix crashtracker config (#2817) --- ext/sidecar.c | 2 +- ext/sidecar.h | 1 + ext/signals.c | 15 +++++++++++++++ tests/ext/crashtracker_segfault.phpt | 4 ++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ext/sidecar.c b/ext/sidecar.c index 5cb3df0ebc..d2f5305640 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -183,7 +183,7 @@ ddog_Endpoint *ddtrace_sidecar_agent_endpoint(void) { return agent_endpoint; } -static inline void ddtrace_sidecar_push_tag(ddog_Vec_Tag *vec, ddog_CharSlice key, ddog_CharSlice value) { +void ddtrace_sidecar_push_tag(ddog_Vec_Tag *vec, ddog_CharSlice key, ddog_CharSlice value) { ddog_Vec_Tag_PushResult tag_result = ddog_Vec_Tag_push(vec, key, value); if (tag_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) { zend_string *msg = dd_CharSlice_to_zend_string(ddog_Error_message(&tag_result.err)); diff --git a/ext/sidecar.h b/ext/sidecar.h index 9123e2d171..225bc123a9 100644 --- a/ext/sidecar.h +++ b/ext/sidecar.h @@ -9,6 +9,7 @@ void ddtrace_sidecar_setup(void); void ddtrace_sidecar_ensure_active(void); void ddtrace_sidecar_shutdown(void); void ddtrace_reset_sidecar_globals(void); +void ddtrace_sidecar_push_tag(ddog_Vec_Tag *vec, ddog_CharSlice key, ddog_CharSlice value); void ddtrace_sidecar_push_tags(ddog_Vec_Tag *vec, zval *tags); ddog_Endpoint *ddtrace_sidecar_agent_endpoint(void); diff --git a/ext/signals.c b/ext/signals.c index c91ccfea32..1b051e1e90 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -107,11 +107,26 @@ static void ddtrace_init_crashtracker() { ddog_crasht_Config config = { .endpoint = agent_endpoint, .timeout_secs = 5, + .resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, + .wait_for_receiver = true }; ddog_Vec_Tag tags = ddog_Vec_Tag_new(); ddtrace_sidecar_push_tags(&tags, NULL); + ddtrace_sidecar_push_tag(&tags, DDOG_CHARSLICE_C("is_crash"), DDOG_CHARSLICE_C("true")); + ddtrace_sidecar_push_tag(&tags, DDOG_CHARSLICE_C("severity"), DDOG_CHARSLICE_C("crash")); + ddtrace_sidecar_push_tag(&tags, DDOG_CHARSLICE_C("library_version"), DDOG_CHARSLICE_C(PHP_DDTRACE_VERSION)); + ddtrace_sidecar_push_tag(&tags, DDOG_CHARSLICE_C("language"), DDOG_CHARSLICE_C("php")); + ddtrace_sidecar_push_tag(&tags, DDOG_CHARSLICE_C("runtime"), DDOG_CHARSLICE_C("php")); + + uint8_t runtime_id[36]; + ddtrace_format_runtime_id(&runtime_id); + ddtrace_sidecar_push_tag(&tags, DDOG_CHARSLICE_C("runtime-id"), (ddog_CharSlice) {.ptr = (char *) runtime_id, .len = sizeof(runtime_id)}); + + const char *runtime_version = zend_get_module_version("Reflection"); + ddtrace_sidecar_push_tag(&tags, DDOG_CHARSLICE_C("runtime_version"), (ddog_CharSlice) {.ptr = (char *) runtime_version, .len = strlen(runtime_version)}); + const ddog_crasht_Metadata metadata = { .library_name = DDOG_CHARSLICE_C_BARE("dd-trace-php"), .library_version = DDOG_CHARSLICE_C_BARE(PHP_DDTRACE_VERSION), diff --git a/tests/ext/crashtracker_segfault.phpt b/tests/ext/crashtracker_segfault.phpt index 40dd40b3bc..782603c6a1 100644 --- a/tests/ext/crashtracker_segfault.phpt +++ b/tests/ext/crashtracker_segfault.phpt @@ -42,13 +42,13 @@ for ($i = 0; $i < 100; ++$i) { %A ] }, - "incomplete": false, + "incomplete": %s, "metadata": { "library_name": "dd-trace-php", "library_version": "%s", "family": "php", "tags": [ - "service:Standard input code" +%A ] }, "os_info": { From fca0ec26bab46d3d158849ea568ae9750fa8bb12 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 29 Aug 2024 15:56:29 +0200 Subject: [PATCH 047/103] Disable SIGSEGV signal handler and crashtracker when sapi is FrankenPHP (#2819) * Disable crashtracker on FrankenPHP * Disable sigsegv handler on FrankenPHP * Disable unecessary 'wait_for_receiver' --- ext/signals.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/signals.c b/ext/signals.c index 1b051e1e90..38157f5ff6 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -108,7 +108,7 @@ static void ddtrace_init_crashtracker() { .endpoint = agent_endpoint, .timeout_secs = 5, .resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, - .wait_for_receiver = true + .wait_for_receiver = false }; ddog_Vec_Tag tags = ddog_Vec_Tag_new(); @@ -160,7 +160,7 @@ void ddtrace_signals_first_rinit(void) { * Using an alternate stack allows the handler to run even when the main * stack overflows. */ - if (install_handler) { + if (install_handler && ddtrace_active_sapi != DATADOG_PHP_SAPI_FRANKENPHP) { size_t stack_size = SIGSTKSZ < MIN_STACKSZ ? MIN_STACKSZ : SIGSTKSZ; if ((ddtrace_altstack.ss_sp = malloc(stack_size))) { ddtrace_altstack.ss_size = stack_size; From 9f9751d1606902183475cbce2a2aeb924edc48fe Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 29 Aug 2024 17:14:04 +0100 Subject: [PATCH 048/103] Fix appsec integration tests --- appsec/tests/integration/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index fa20894b41..4103c09868 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -157,6 +157,9 @@ def buildRunInDockerTask = { Map options -> volumes['php-tracer-cargo-cache'] = [ mountPoint: '/root/.cargo/registry', ] + volumes['php-tracer-cargo-cache-git'] = [ + mountPoint: '/root/.cargo/git', + ] } def composerDlTask @@ -443,6 +446,7 @@ task loadCaches(type: Exec) { dependsOn downloadCaches dependsOn 'createVolume-php-tracer-cargo-cache' + dependsOn 'createVolume-php-tracer-cargo-cache-git' dependsOn 'createVolume-php-appsec-hunter-cache' } From 6397d1a4622da9daeacf90bb1f1a46d9236d37ae Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Thu, 29 Aug 2024 18:55:54 +0100 Subject: [PATCH 049/103] Alternative method to detect liveliness of RoadRunner --- .../appsec/php/integration/RoadRunnerTests.groovy | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RoadRunnerTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RoadRunnerTests.groovy index b17d41fa60..4a5503e363 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RoadRunnerTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RoadRunnerTests.groovy @@ -8,7 +8,6 @@ import com.datadog.appsec.php.model.Span import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.EnabledIf -import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy import org.testcontainers.containers.wait.strategy.WaitStrategy import org.testcontainers.containers.wait.strategy.WaitStrategyTarget import org.testcontainers.junit.jupiter.Container @@ -23,7 +22,6 @@ import static com.datadog.appsec.php.integration.TestParams.getVariant import static com.datadog.appsec.php.integration.TestParams.phpVersionAtLeast import static com.datadog.appsec.php.test.JsonMatcher.matchesJson import static java.net.http.HttpResponse.BodyHandlers.ofString -import static java.time.temporal.ChronoUnit.SECONDS import static org.hamcrest.MatcherAssert.assertThat @Testcontainers @@ -45,7 +43,9 @@ class RoadRunnerTests { phpVariant: variant, www: 'roadrunner', ).with { - // we only start listening on http after run.sh has finished + // we only start listening on http after run.sh has finished, + // so mark immediately the container as ready. We instead check for liveliness + // in beforeAll() setWaitStrategy(new WaitStrategy() { @Override void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { @@ -62,7 +62,14 @@ class RoadRunnerTests { @BeforeAll static void beforeAll() { - new HostPortWaitStrategy().withStartupTimeout(Duration.of(300, SECONDS) ).waitUntilReady(CONTAINER) + // wait until roadrunner is running + long deadline = System.currentTimeMillis() + 300_000 + while (CONTAINER.execInContainer('grep', 'http server was started', '/tmp/logs/rr.log').exitCode != 0) { + if (System.currentTimeMillis() > deadline) { + throw new RuntimeException('Roadrunner did not start on time (see output of run.sh)') + } + Thread.sleep(500) + } } @Test From 0d5bf6a891301e7da95362040fe2fb95a0ad4d55 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 30 Aug 2024 11:27:02 +0200 Subject: [PATCH 050/103] Improve on randomized tests flakiness (#2823) Signed-off-by: Bob Weinand --- dockerfiles/ci/buster/php-8.3/suppr.txt | 1 + libdatadog | 2 +- tests/randomized/analyze-results.php | 23 +++++++++++++++++++++-- tests/randomized/docker/buster.Dockerfile | 7 +++++++ tests/randomized/docker/centos.Dockerfile | 4 +++- tests/randomized/docker/prepare.sh | 6 +++++- tests/randomized/docker/run.sh | 15 +++++++++++++-- 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/dockerfiles/ci/buster/php-8.3/suppr.txt b/dockerfiles/ci/buster/php-8.3/suppr.txt index cf16629467..94e010b06a 100644 --- a/dockerfiles/ci/buster/php-8.3/suppr.txt +++ b/dockerfiles/ci/buster/php-8.3/suppr.txt @@ -3,3 +3,4 @@ leak:timer_create leak:add_to_global leak:_dl_map_object_deps leak:__res_context_send +leak:_dl_make_tlsdesc_dynamic diff --git a/libdatadog b/libdatadog index 0be5fad8da..3b99aa732d 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 0be5fad8daf1559d9360d042802b7cb85689deca +Subproject commit 3b99aa732d49e758cb748d8ca58b625c9112863d diff --git a/tests/randomized/analyze-results.php b/tests/randomized/analyze-results.php index b5efa5fea9..4067da43b7 100644 --- a/tests/randomized/analyze-results.php +++ b/tests/randomized/analyze-results.php @@ -10,14 +10,23 @@ function analyze_web($tmpScenariosFolder) $unexpectedCodes = []; $possibleSegfaults = []; $minimumRequestCount = []; + $prepareErrors = []; foreach (scandir($resultsFolder) as $identifier) { if (in_array($identifier, ['.', '..'])) { continue; } + + $analyzed[] = $identifier; + $scenarioResultsRoot = $resultsFolder . DIRECTORY_SEPARATOR . $identifier; + $prepareErrorFilePath = $scenarioResultsRoot . DIRECTORY_SEPARATOR . 'prepare-error'; $absFilePath = $scenarioResultsRoot . DIRECTORY_SEPARATOR . 'results.json'; - $analyzed[] = $identifier; + + if (file_exists($prepareErrorFilePath)) { + $prepareErrors[$identifier] = (int)file_get_contents($prepareErrorFilePath); + continue; + } $jsonResult = json_decode(file_get_contents($absFilePath), 1); @@ -71,6 +80,10 @@ function analyze_web($tmpScenariosFolder) echo "Minimum request not matched: " . var_export($minimumRequestCount, 1) . "\n"; $isError = true; } + if (count($prepareErrors)) { + echo "Prepare step failed with error: " . var_export($prepareErrors, 1) . "\n"; + $isError = count($prepareErrors) > count($analyzed) / 2; // In that case it's probably not transient + } if ($isError) { return false; @@ -115,7 +128,13 @@ function analyze_cli($tmpScenariosFolder) $analyzed[] = $identifier; - $absFilePath = $resultsFolder . DIRECTORY_SEPARATOR . $identifier . DIRECTORY_SEPARATOR . 'memory.out'; + $scenarioResultsRoot = $resultsFolder . DIRECTORY_SEPARATOR . $identifier; + $absFilePath = $scenarioResultsRoot . DIRECTORY_SEPARATOR . 'memory.out'; + $prepareErrorFilePath = $scenarioResultsRoot . DIRECTORY_SEPARATOR . 'prepare-error'; + + if (file_exists($prepareErrorFilePath)) { + continue; // We already handled it in analyze_web + } $values = array_map('intval', array_filter( file($absFilePath), diff --git a/tests/randomized/docker/buster.Dockerfile b/tests/randomized/docker/buster.Dockerfile index 1d7e5ef329..41cf886f19 100644 --- a/tests/randomized/docker/buster.Dockerfile +++ b/tests/randomized/docker/buster.Dockerfile @@ -10,6 +10,9 @@ RUN for DIR in /opt/php/*; do (echo "zend_extension=opcache.so"; echo "opcache.e # don't execute an asan *binary* under qemu RUN mv /opt/php/debug/bin/php-config /opt/php/debug/bin/php-config-debug; cp /opt/php/debug-zts-asan/bin/php-config /opt/php/debug/bin/php-config +# install redis for randomized tests +RUN echo "extension=redis" >> $(php-config --ini-dir)/redis.ini + # Igbinary RUN set -eux; \ pecl install "igbinary"; \ @@ -65,6 +68,10 @@ RUN sed -i 's/apache2/httpd/' /etc/apache2/envvars ADD run.sh /scripts/run.sh ADD prepare.sh /scripts/prepare.sh +# actually bind the sidecar error output to docker out +ENV _DD_DEBUG_SIDECAR_LOG_METHOD=file:///proc/1/fd/2 +ENV DD_SPAWN_WORKER_USE_EXEC=1 + WORKDIR /var/www/html ENV COMPOSER_CACHE_DIR /composer-cache diff --git a/tests/randomized/docker/centos.Dockerfile b/tests/randomized/docker/centos.Dockerfile index 9e562dc920..c134fe5ad4 100644 --- a/tests/randomized/docker/centos.Dockerfile +++ b/tests/randomized/docker/centos.Dockerfile @@ -30,7 +30,7 @@ RUN set -eux; \ # Redis RUN set -eux; \ - printf 'yes' | pecl install "redis"; \ + printf 'yes' | pecl install "redis$(if [ ${PHP_VERSION/./} -le 71 ]; then echo -5.3.7; fi)"; \ for DIR in /opt/php/*; do echo "extension=redis.so" > $DIR/conf.d/redis.ini; done # Create coredumps folder @@ -83,6 +83,8 @@ RUN echo "CoreDumpDirectory /tmp/corefiles" >> /etc/httpd/conf/httpd.conf ADD run.sh /scripts/run.sh ADD prepare.sh /scripts/prepare.sh +ENV DD_SPAWN_WORKER_USE_EXEC=1 + WORKDIR /var/www/html ENV COMPOSER_CACHE_DIR /composer-cache diff --git a/tests/randomized/docker/prepare.sh b/tests/randomized/docker/prepare.sh index e201b12f6d..4e6c322754 100644 --- a/tests/randomized/docker/prepare.sh +++ b/tests/randomized/docker/prepare.sh @@ -25,7 +25,11 @@ php -v echo "Starting PHP-FPM" mkdir -p /var/log/php-fpm/ chmod a+w /var/log/php-fpm/ -php-fpm -D -d datadog.trace.log_file=/results/dd_php_error.log +if ldd $(which php) 2>/dev/null | grep -q libasan; then + php-fpm -D -d datadog.trace.log_file=/results/dd_php_error.log +else + nohup strace -ttfs 200 bash -c 'php-fpm -F -d datadog.trace.log_file=/results/dd_php_error.log 2>&3' 0<&- 3>&2 2>/results/php-fpm.strace >/dev/null & +fi sleep 1 # Start nginx diff --git a/tests/randomized/docker/run.sh b/tests/randomized/docker/run.sh index c49d752f2c..583b2044b2 100644 --- a/tests/randomized/docker/run.sh +++ b/tests/randomized/docker/run.sh @@ -3,12 +3,23 @@ set -e . /scripts/enable-coredump.sh -bash /scripts/prepare.sh + +ret=0 +bash /scripts/prepare.sh || ret=$? +if [[ $ret -ne 0 ]]; then + # handle transient prepare failures + echo $ret > /results/prepare-error + exit $ret +fi echo "Starting web load" vegeta -cpus=2 attack -format=http -targets=/vegeta-request-targets.txt -duration=${DURATION:-30s} -keepalive=false -max-workers=10 -workers=10 -rate=100 | tee results.bin | vegeta report --type=json --output=/results/results.json echo "Done web load" echo "Starting CLI load" -sh /cli-runner.sh +if ldd $(which php) 2>/dev/null | grep -q libasan; then + sh /cli-runner.sh +else + strace -ttfs 200 bash -c 'sh /cli-runner.sh 2>&3' 3>&2 2>/results/php-cli.strace +fi echo "Done CLI load" From d03ef3f5566a61057be1f5e9e28521d7fb48d184 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 30 Aug 2024 11:28:43 +0200 Subject: [PATCH 051/103] Do not implicitly fall back to spl_autoload() if no autoloader is registered on PHP 7 (#2822) * Do not implicitly fall back to spl_autoload() if no autoloader is registered on PHP 7 This could lead to accidental inclusion of files, breaking __autoload() and possibly including the wrong file. Signed-off-by: Bob Weinand --- ext/autoload_php_files.c | 54 ++++++++++++++++++- package.xml | 1 + .../default_spl_autoloader.phpt | 17 ++++++ .../autoload-php-files/legacy_autoloader.phpt | 23 ++++++++ .../skip_default_autoloader.phpt | 15 ++++++ tests/ext/autoload-php-files/splautoload.inc | 5 ++ 6 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 tests/ext/autoload-php-files/default_spl_autoloader.phpt create mode 100644 tests/ext/autoload-php-files/legacy_autoloader.phpt create mode 100644 tests/ext/autoload-php-files/skip_default_autoloader.phpt create mode 100644 tests/ext/autoload-php-files/splautoload.inc diff --git a/ext/autoload_php_files.c b/ext/autoload_php_files.c index db763a32f9..1de3313f46 100644 --- a/ext/autoload_php_files.c +++ b/ext/autoload_php_files.c @@ -238,9 +238,61 @@ static inline bool dd_legacy_autoload_wrapper(INTERNAL_FUNCTION_PARAMETERS) { zend_string *lower = zend_string_tolower(class_name); bool found = dd_perform_autoload(class_name, lower) != NULL; + + if (found) { + zend_string_release(lower); + return true; + } + + bool autoloading = EG(in_autoload) && zend_hash_exists(EG(in_autoload), lower); zend_string_release(lower); - return found; + // check whether we're actually autoloading + if (autoloading) { + if (dd_has_registered_spl_autoloader) { + return false; + } + + zend_function *func = +#if PHP_VERSION_ID >= 70300 + zend_fetch_function(ZSTR_KNOWN(ZEND_STR_MAGIC_AUTOLOAD)) +#else + zend_hash_str_find_ptr(EG(function_table), ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1) +#endif + ; + if (func) { + zval ret; + zend_fcall_info fcall_info; + zend_fcall_info_cache fcall_cache; + + fcall_info.size = sizeof(fcall_info); + ZVAL_STR(&fcall_info.function_name, func->common.function_name); + fcall_info.retval = &ret; + fcall_info.param_count = 1; + fcall_info.params = EX_VAR_NUM(0); + fcall_info.object = NULL; + fcall_info.no_separation = 1; +#if PHP_VERSION_ID < 70100 + fcall_info.symbol_table = NULL; +#endif + +#if PHP_VERSION_ID < 70300 + fcall_cache.initialized = 1; +#endif + fcall_cache.function_handler = func; + fcall_cache.calling_scope = NULL; + fcall_cache.called_scope = NULL; + fcall_cache.object = NULL; + + zend_call_function(&fcall_info, &fcall_cache); + zval_ptr_dtor(&ret); + } + + // skip original implementation if there's no spl autoloader registered + return true; + } + + return false; } static ZEND_NAMED_FUNCTION(dd_wrap_autoload_register_fn) { diff --git a/package.xml b/package.xml index acbdff3ba4..cae934e29d 100644 --- a/package.xml +++ b/package.xml @@ -98,6 +98,7 @@ The default value for datadog.log_backtrace is now set to true, meaning that on - Implement fallback for when memfd is not available on Linux Datadog/libdatadog#591 - Use the Windows User ID as sidecar identifier instead of the Session ID Datadog/libdatadog#558 - Fix error check in trampoline.c Datadog/libdatadog#569 +- Do not implicitly fall back to spl_autoload() if no autoloader is registered on PHP 7 #2822 ### Internal - Send x-datadog-test-session-token metric and send metrics to request-replayer #2802 diff --git a/tests/ext/autoload-php-files/default_spl_autoloader.phpt b/tests/ext/autoload-php-files/default_spl_autoloader.phpt new file mode 100644 index 0000000000..3ed5a422c5 --- /dev/null +++ b/tests/ext/autoload-php-files/default_spl_autoloader.phpt @@ -0,0 +1,17 @@ +--TEST-- +Execute the default spl_autoload implementation if spl_autoload_register() is called without args +--INI-- +datadog.trace.sources_path="{PWD}/.." +--FILE-- + +--EXPECT-- +bool(true) +Request start diff --git a/tests/ext/autoload-php-files/legacy_autoloader.phpt b/tests/ext/autoload-php-files/legacy_autoloader.phpt new file mode 100644 index 0000000000..606edb1895 --- /dev/null +++ b/tests/ext/autoload-php-files/legacy_autoloader.phpt @@ -0,0 +1,23 @@ +--TEST-- +Execute __autoload() if present +--SKIPIF-- += 80000) die("skip: __autoload was removed in PHP 8") ?> +--INI-- +error_reporting=8191 +datadog.trace.sources_path="{PWD}/.." +--FILE-- + +--EXPECT-- +Autoload splautoload attempted! +bool(false) +Request start diff --git a/tests/ext/autoload-php-files/skip_default_autoloader.phpt b/tests/ext/autoload-php-files/skip_default_autoloader.phpt new file mode 100644 index 0000000000..bf25fa4eac --- /dev/null +++ b/tests/ext/autoload-php-files/skip_default_autoloader.phpt @@ -0,0 +1,15 @@ +--TEST-- +Do not execute the default spl_autoload implementation if no autoloader is specified +--INI-- +datadog.trace.sources_path="{PWD}/.." +--FILE-- + +--EXPECT-- +bool(false) +Request start diff --git a/tests/ext/autoload-php-files/splautoload.inc b/tests/ext/autoload-php-files/splautoload.inc new file mode 100644 index 0000000000..b324fc1363 --- /dev/null +++ b/tests/ext/autoload-php-files/splautoload.inc @@ -0,0 +1,5 @@ + Date: Fri, 30 Aug 2024 14:05:47 +0200 Subject: [PATCH 052/103] Prevent crash in shutdown if hook was not stored (#2824) This seems to be possible to happen when fatal errors occur beforehand. This specific fatal error occurred due to a timeout, hence it's not trivially reproducible. Signed-off-by: Bob Weinand --- package.xml | 1 + zend_abstract_interface/hook/hook.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.xml b/package.xml index cae934e29d..e855a399cf 100644 --- a/package.xml +++ b/package.xml @@ -99,6 +99,7 @@ The default value for datadog.log_backtrace is now set to true, meaning that on - Use the Windows User ID as sidecar identifier instead of the Session ID Datadog/libdatadog#558 - Fix error check in trampoline.c Datadog/libdatadog#569 - Do not implicitly fall back to spl_autoload() if no autoloader is registered on PHP 7 #2822 +- Prevent crash in shutdown if hook was not stored #2824 ### Internal - Send x-datadog-test-session-token metric and send metrics to request-replayer #2802 diff --git a/zend_abstract_interface/hook/hook.c b/zend_abstract_interface/hook/hook.c index d3e4b4ad4e..20a5691360 100644 --- a/zend_abstract_interface/hook/hook.c +++ b/zend_abstract_interface/hook/hook.c @@ -1083,7 +1083,8 @@ void zai_hook_finish(zend_execute_data *ex, zval *rv, zai_hook_memory_t *memory) zend_ulong address = zai_hook_frame_address(ex); zai_hook_table_find(&zai_hook_resolved, address, (void**)&hooks); zval *hook_zv; - if ((hook_zv = zend_hash_index_find(&hooks->hooks, (zend_ulong) -hook->id))) { + ZEND_ASSERT(CG(unclean_shutdown) || hooks != NULL); + if (hooks && (hook_zv = zend_hash_index_find(&hooks->hooks, (zend_ulong) -hook->id))) { if (Z_TYPE_INFO_P(hook_zv) == ZAI_IS_SHARED_HOOK_PTR) { // lookup primary by name zend_class_entry *ce = NULL; From 2cdee3f98882979f30a33ed7545080d4ce61f29f Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 2 Sep 2024 11:26:45 +0200 Subject: [PATCH 053/103] Avoid using non-bundled rust git dependencies (#2827) Signed-off-by: Bob Weinand --- .circleci/continue_config.yml | 8 ++++++-- Cargo.lock | 3 ++- libdatadog | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 14e5dbaa11..78e0122b23 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3835,12 +3835,16 @@ jobs: name: Install cbindgen command: cargo install --version "^0.26" cbindgen - run: - name: Regenerate cbindgen headers and + name: Regenerate cbindgen headers and compare them command: | set -eu make cbindgen git diff --exit-code components-rs - + - run: + name: Ensure no non-bundled rust git dependencies are used + command: | + set -eu + ! grep -P 'source = "git+(?!.*libdatadog)' Cargo.lock placeholder: docker: diff --git a/Cargo.lock b/Cargo.lock index 23f5190da5..615e880d6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -551,7 +551,8 @@ checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" [[package]] name = "blazesym" version = "0.2.0-rc.0" -source = "git+https://github.com/libbpf/blazesym.git?rev=v0.2.0-rc.0#2f393f66a448f46ea71889e81a8866799762463d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519a0f9df086d6c4f44576558523a777c984454daeb124bee79bde69227360c4" dependencies = [ "cpp_demangle", "gimli 0.30.0", diff --git a/libdatadog b/libdatadog index 3b99aa732d..cfae8afc60 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 3b99aa732d49e758cb748d8ca58b625c9112863d +Subproject commit cfae8afc60c7fdde0f3737927ad1b01821e4bbd9 From e7adb515242dc130fa967a1b1a1858a86ad8d2c8 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Mon, 2 Sep 2024 11:40:28 +0200 Subject: [PATCH 054/103] Make the GitLab CI workflow work when triggered by tags (#2828) --- .gitlab/download-circleci_artifact.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab/download-circleci_artifact.sh b/.gitlab/download-circleci_artifact.sh index 1abe9ffe21..b473d8324e 100755 --- a/.gitlab/download-circleci_artifact.sh +++ b/.gitlab/download-circleci_artifact.sh @@ -24,8 +24,10 @@ download_circleci_artifact() { ARTIFACT_PATTERN=$4 # "loader/modules/dd_library_loader.so" ARTIFACT_NAME=$5 # "dd_library_loader-x86_64-linux-gnu.so" - BRANCH=${CI_COMMIT_BRANCH} # Set by Gilab CI - COMMIT_SHA=${CI_COMMIT_SHA} # Set by Gilab CI + # Circle CI workflow is not triggered by tags, + # So we fallback to the release branch (eg. "ddtrace-1.3.0") + BRANCH=${CI_COMMIT_BRANCH:-"ddtrace-${CI_COMMIT_TAG}"} # Set by Gilab CI + COMMIT_SHA=${CI_COMMIT_SHA} # Set by Gilab CI PIPELINES=$(call_api "https://circleci.com/api/v2/project/${SLUG}/pipeline?branch=${BRANCH}") From 201810f8ca953b930423a2e0d0cc4dadd2bf878e Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 4 Sep 2024 13:40:21 +0200 Subject: [PATCH 055/103] Fix DD_AUTOLOAD_NO_COMPILE (#2833) Signed-off-by: Bob Weinand --- ext/configuration.h | 2 +- tests/ext/autoload-php-files/dd_init_open_basedir.phpt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/configuration.h b/ext/configuration.h index df5aa2c56b..4a8b051fea 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -92,7 +92,7 @@ enum ddtrace_sampling_rules_format { #define DD_CONFIGURATION_ALL \ CONFIG(STRING, DD_TRACE_SOURCES_PATH, DD_DEFAULT_SOURCES_PATH, .ini_change = zai_config_system_ini_change) \ - CONFIG(STRING, DD_AUTOLOAD_NO_COMPILE, "0", .ini_change = zai_config_system_ini_change) \ + CONFIG(BOOL, DD_AUTOLOAD_NO_COMPILE, "false", .ini_change = zai_config_system_ini_change) \ CONFIG(STRING, DD_TRACE_AGENT_URL, "", .ini_change = zai_config_system_ini_change) \ CONFIG(STRING, DD_AGENT_HOST, "", .ini_change = zai_config_system_ini_change) \ CONFIG(STRING, DD_DOGSTATSD_URL, "") \ diff --git a/tests/ext/autoload-php-files/dd_init_open_basedir.phpt b/tests/ext/autoload-php-files/dd_init_open_basedir.phpt index 291b1376ec..b564db7805 100644 --- a/tests/ext/autoload-php-files/dd_init_open_basedir.phpt +++ b/tests/ext/autoload-php-files/dd_init_open_basedir.phpt @@ -2,6 +2,7 @@ Calling dd_init.php is confined to open_basedir settings --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off,datadog_sidecar=off +DD_AUTOLOAD_NO_COMPILE=1 --INI-- open_basedir="{PWD}" datadog.trace.sources_path="{PWD}/.." From bcb0c30d9f026caf8d2f0571f6743707ba4cc9f6 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Wed, 4 Sep 2024 14:01:32 +0200 Subject: [PATCH 056/103] Improve tests stability (#2830) * Remove flakiness from the crashtracker_segfault.phpt test * Add a sligh delay to avoid race condition in tests using metrics * Add a delay to reduce flakiness on pnctl tests * Make the test sets datadog.trace.agent_test_session_token --- ext/sidecar.c | 8 +- tests/Common/SnapshotTestTrait.php | 1 + tests/Common/TracerTestTrait.php | 1 + tests/Integrations/OpenAI/OpenAITest.php | 2 +- tests/ext/crashtracker_segfault.phpt | 89 ++++++++++--------- tests/ext/includes/request_replayer.inc | 35 ++++++-- .../dd_trace_exception_span_event.phpt | 1 + 7 files changed, 86 insertions(+), 51 deletions(-) diff --git a/ext/sidecar.c b/ext/sidecar.c index d2f5305640..0f9754c433 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -23,10 +23,6 @@ static uint8_t dd_sidecar_formatted_session_id[36]; static void ddtrace_set_non_resettable_sidecar_globals(void) { ddtrace_format_runtime_id(&dd_sidecar_formatted_session_id); ddtrace_endpoint = ddtrace_sidecar_agent_endpoint(); - - if (ZSTR_LEN(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())) { - ddog_endpoint_set_test_token(ddtrace_endpoint, dd_zend_string_to_CharSlice(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())); - } } // Set the globals that must be updated in case of fork @@ -180,6 +176,10 @@ ddog_Endpoint *ddtrace_sidecar_agent_endpoint(void) { free(agent_url); } + if (ZSTR_LEN(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())) { + ddog_endpoint_set_test_token(agent_endpoint, dd_zend_string_to_CharSlice(get_global_DD_TRACE_AGENT_TEST_SESSION_TOKEN())); + } + return agent_endpoint; } diff --git a/tests/Common/SnapshotTestTrait.php b/tests/Common/SnapshotTestTrait.php index 59d8656dde..b825e35085 100644 --- a/tests/Common/SnapshotTestTrait.php +++ b/tests/Common/SnapshotTestTrait.php @@ -344,6 +344,7 @@ public function isolateTracerSnapshot( $fn($tracer); if ($snapshotMetrics) { + usleep(50000); // Add a slight delay to avoid a race condition where the "tracer-snapshot-end" metric is handled before a test metric. \DDTrace\dogstatsd_count("tracer-snapshot-end", 1); } diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index 4ce8e6fe7d..66430ffe8e 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -220,6 +220,7 @@ public function inCli($scriptPath, $customEnvs = [], $customInis = [], $argument { $this->resetRequestDumper(); $output = $this->executeCli($scriptPath, $customEnvs, $customInis, $arguments, $withOutput); + usleep(100000); // Add a slight delay to give the request-replayer time to handle and store all requests. $out = [$this->parseTracesFromDumpedData()]; if ($withOutput) { $out[] = $output; diff --git a/tests/Integrations/OpenAI/OpenAITest.php b/tests/Integrations/OpenAI/OpenAITest.php index cf5f1c668d..2a4851fc5b 100644 --- a/tests/Integrations/OpenAI/OpenAITest.php +++ b/tests/Integrations/OpenAI/OpenAITest.php @@ -29,7 +29,7 @@ private function checkErrors() protected function ddSetUp() { - // Note: Remember that DD_DOGSTATSD_URL=http://127.0.0.1:9876 is set in the Makefile call + // Note: Remember that DD_DOGSTATSD_URL=http://request-replayer:80 is set in the Makefile call ini_set("log_errors", 1); ini_set("error_log", __DIR__ . "/openai.log"); self::putEnvAndReloadConfig([ diff --git a/tests/ext/crashtracker_segfault.phpt b/tests/ext/crashtracker_segfault.phpt index 782603c6a1..8a00d52b3f 100644 --- a/tests/ext/crashtracker_segfault.phpt +++ b/tests/ext/crashtracker_segfault.phpt @@ -7,14 +7,21 @@ if (getenv('SKIP_ASAN') || getenv('USE_ZEND_ALLOC') === '0') die("skip: intentio if (getenv('PHP_PEAR_RUNTESTS') === '1') die("skip: pecl run-tests does not support %A in EXPECTF"); if (getenv('DD_TRACE_CLI_ENABLED') === '0') die("skip: tracer is disabled"); if (PHP_VERSION_ID < 70200) die("skip: TEST_PHP_EXTRA_ARGS is only available on PHP 7.2+"); +include __DIR__ . '/includes/skipif_no_dev_env.inc'; ?> --ENV-- -DD_TRACE_SIDECAR_TRACE_SENDER=1 +DD_TRACE_LOG_LEVEL=0 +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 --INI-- -datadog.trace.agent_url="file://{PWD}/crashtracker_segfault_agent.out" +datadog.trace.agent_test_session_token=tests/ext/crashtracker_segfault.phpt --FILE-- replayRequest(); // cleanup possible leftover + usleep(100000); // Let time to the sidecar to open the crashtracker socket $php = getenv('TEST_PHP_EXECUTABLE'); @@ -22,49 +29,49 @@ $args = getenv('TEST_PHP_ARGS')." ".getenv("TEST_PHP_EXTRA_ARGS"); $cmd = $php." ".$args." -r 'posix_kill(posix_getpid(), 11);'"; system($cmd); -for ($i = 0; $i < 100; ++$i) { - $content = file_get_contents(__DIR__."/crashtracker_segfault_agent.out"); - if (false != strpos($content, '"signame": "SIGSEGV"')) { - echo $content; - break; +$rr->waitForRequest(function ($request) { + if ($request["uri"] != "/telemetry/proxy/api/v2/apmtelemetry") { + return false; + } + $body = json_decode($request["body"], true); + if ($body["request_type"] != "logs" || !isset($body["payload"][0]["message"])) { + return false; } - usleep(5000); // Let time for the crash report to be uploaded -} + $payload = $body["payload"][0]; + $payload["message"] = json_decode($payload["message"], true); + $output = json_encode($payload, JSON_PRETTY_PRINT); + + echo $output; + + return true; +}); ?> --EXPECTF-- +%A{ + "message": { + "additional_stacktraces": [], + "files": { %A - "counters": { -%A - }, - "files": { - "/proc/self/maps": [ + }, + "metadata": { + "library_name": "dd-trace-php", + "library_version": "%s", + "family": "php", + "tags": [ %A - ] - }, - "incomplete": %s, - "metadata": { - "library_name": "dd-trace-php", - "library_version": "%s", - "family": "php", - "tags": [ + ] + }, + "os_info": { %A - ] - }, - "os_info": { -%A - }, - "proc_info": { - "pid": %d - }, - "siginfo": { - "signum": 11, - "signame": "SIGSEGV" - }, - "timestamp": "%s", - "uuid": "%s" -%A ---CLEAN-- -flushInterval * 2 * 1000); } + public function waitForRequest($matcher) + { + $i = 0; + do { + if ($i++ == 100) { + throw new Exception("wait for replay timeout"); + } + usleep($this->flushInterval * 1000); + + $requests = $this->replayAllRequests(); + if (is_array($requests)) { + foreach ($requests as $request) { + if ($matcher($request)) { + return $request; + } + } + } + } while (true); + } + public function waitForDataAndReplay($ignoreTelemetry = true) { $i = 0; @@ -46,17 +66,22 @@ class RequestReplayer { // Request replayer now returns as many requests as were sent during a session. // For the scope of the tests, we are returning the very first one. - $allRequests = json_decode(file_get_contents($this->endpoint . '/replay', false, stream_context_create([ - "http" => [ - "header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), - ], - ])), true); + $allRequests = $this->replayAllRequests(); if ($allRequests && $ignoreTelemetry) { $allRequests = array_values(array_filter($allRequests, function ($v) { return $v["uri"] != '/telemetry/proxy/api/v2/apmtelemetry'; })); } return $allRequests ? $allRequests[0] : []; } + public function replayAllRequests() + { + return json_decode(file_get_contents($this->endpoint . '/replay', false, stream_context_create([ + "http" => [ + "header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ], + ])), true); + } + public function replayHeaders($showOnly = []) { $request = $this->waitForDataAndReplay(); diff --git a/tests/ext/request-replayer/dd_trace_exception_span_event.phpt b/tests/ext/request-replayer/dd_trace_exception_span_event.phpt index 105670b243..5ef462d78d 100644 --- a/tests/ext/request-replayer/dd_trace_exception_span_event.phpt +++ b/tests/ext/request-replayer/dd_trace_exception_span_event.phpt @@ -36,6 +36,7 @@ DDTrace\trace_method('ExceptionClass', 'exceptionMethod', function (SpanData $sp }); $rr = new RequestReplayer(); +$rr->replayRequest(); // cleanup possible leftover try { $exceptionClass = new ExceptionClass(); From 1e31c3a5b12ba46419231a2bef5f3a759e7ada11 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Wed, 4 Sep 2024 16:41:19 +0200 Subject: [PATCH 057/103] When a crash happens in a container, give the sidecar more time to upload the crash report (#2834) --- ext/signals.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/signals.c b/ext/signals.c index 38157f5ff6..19ccfdf24a 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -108,7 +108,9 @@ static void ddtrace_init_crashtracker() { .endpoint = agent_endpoint, .timeout_secs = 5, .resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, - .wait_for_receiver = false + // Likely running in a container, so wait until the report is uploaded. + // Otherwise, the container shutdown may stop the sidecar before it has finished uploading the crash report. + .wait_for_receiver = getpid() == 1, }; ddog_Vec_Tag tags = ddog_Vec_Tag_new(); From 735db41484d6c1d5e1d8d20035eb35c919f50938 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Wed, 4 Sep 2024 17:52:39 +0200 Subject: [PATCH 058/103] Disable trace_generator_jit.phpt test on PHP 8.0 because JIT is not stable enough on PHP 8.0 (#2835) --- tests/ext/sandbox/install_hook/trace_generator_jit.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ext/sandbox/install_hook/trace_generator_jit.phpt b/tests/ext/sandbox/install_hook/trace_generator_jit.phpt index a447530e89..45736d3c19 100644 --- a/tests/ext/sandbox/install_hook/trace_generator_jit.phpt +++ b/tests/ext/sandbox/install_hook/trace_generator_jit.phpt @@ -2,7 +2,7 @@ generator hooking works with JIT --SKIPIF-- - + --INI-- opcache.enable=1 opcache.enable_cli = 1 From 37418fd5ec786ef8cf700dbdb6309c25ae5e5815 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 5 Sep 2024 23:40:16 +0200 Subject: [PATCH 059/103] Fix crash with double parent key in tracestate (#2837) Access of span_parent_key would be NULL on second 'p'. Signed-off-by: Bob Weinand --- ext/distributed_tracing_headers.c | 12 +++++++----- ...uted_trace_conflicting_span_ids_tracecontext.phpt | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ext/distributed_tracing_headers.c b/ext/distributed_tracing_headers.c index cc0a41bd9e..f54a4d06e4 100644 --- a/ext/distributed_tracing_headers.c +++ b/ext/distributed_tracing_headers.c @@ -263,11 +263,13 @@ static ddtrace_distributed_tracing_result ddtrace_read_distributed_tracing_ids_t size_t valuelen = valueend - valuestart; if (keylen == 1 && keystart[0] == 'p') { - zval zv; - ZVAL_STRINGL(&zv, valuestart, valuelen); - zend_hash_update(&result.meta_tags, span_parent_key, &zv); - zend_string_release(span_parent_key); - span_parent_key = NULL; + if (span_parent_key) { + zval zv; + ZVAL_STRINGL(&zv, valuestart, valuelen); + zend_hash_update(&result.meta_tags, span_parent_key, &zv); + zend_string_release(span_parent_key); + span_parent_key = NULL; + } } else if (keylen == 1 && keystart[0] == 's') { int extraced_priority = strtol(valuestart, NULL, 10); if ((result.priority_sampling > 0) == (extraced_priority > 0)) { diff --git a/tests/ext/distributed_tracing/distributed_trace_conflicting_span_ids_tracecontext.phpt b/tests/ext/distributed_tracing/distributed_trace_conflicting_span_ids_tracecontext.phpt index 696ee326aa..e2c5261fc4 100644 --- a/tests/ext/distributed_tracing/distributed_trace_conflicting_span_ids_tracecontext.phpt +++ b/tests/ext/distributed_tracing/distributed_trace_conflicting_span_ids_tracecontext.phpt @@ -8,7 +8,7 @@ DD_TRACE_PROPAGATION_STYLE=tracecontext DDTrace\consume_distributed_tracing_headers([ "traceparent" => "00-0000000000000000000000000000002a-0000000000000001-01", - "tracestate" => "dd=p:00000000000000bb;s:1", + "tracestate" => "dd=p:00000000000000bb;p:00000000000000bb;s:1", ]); $span = \DDTrace\start_span(); From 69af19779c07288dbb185c7e0715646873371067 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Fri, 6 Sep 2024 14:49:32 +0200 Subject: [PATCH 060/103] Loader: Use the same version number as for other extensions (#2838) --- .circleci/continue_config.yml | 2 +- loader/php_dd_library_loader.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 78e0122b23..a3dcce6447 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3313,7 +3313,7 @@ jobs: phpize ./configure make clean - make -j all ECHO_ARG="-e" CFLAGS="-std=gnu11 -O2 -g -Wall -Wextra -Werror" + make -j all ECHO_ARG="-e" CFLAGS="-std=gnu11 -O2 -g -Wall -Wextra -Werror -DPHP_DD_LIBRARY_LOADER_VERSION='\"$(cat ../VERSION)\"'" cp modules/dd_library_loader.so ../dd_library_loader-$(uname -m)-<< parameters.os >>.so - persist_to_workspace: root: '.' diff --git a/loader/php_dd_library_loader.h b/loader/php_dd_library_loader.h index c625acc873..a8d0ed01b4 100644 --- a/loader/php_dd_library_loader.h +++ b/loader/php_dd_library_loader.h @@ -3,7 +3,9 @@ #ifndef PHP_DD_LIBRARY_LOADER_H #define PHP_DD_LIBRARY_LOADER_H +#ifndef PHP_DD_LIBRARY_LOADER_VERSION #define PHP_DD_LIBRARY_LOADER_VERSION "0.1.0" +#endif #define UNUSED(x) (void)(x) From ff59f29e9a117cc6afc052a669605c35f8696715 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 6 Sep 2024 14:57:35 +0200 Subject: [PATCH 061/103] Fix #2831 (#2839) LTO seems to recognize some variable was never set and eliminated a branch we relied on to exist. Fool the compiler a bit better. And add minor changes to the tests to allow running them more generally. Signed-off-by: Bob Weinand --- Cargo.lock | 3 +++ libdatadog | 2 +- tests/ext/force_flush_traces.phpt | 3 +-- tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt | 2 ++ tests/ext/telemetry/config.phpt | 1 + zend_abstract_interface/interceptor/php8/interceptor.c | 5 +++++ 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 615e880d6a..19df62f047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1325,6 +1325,7 @@ dependencies = [ "bytes", "criterion", "datadog-ipc-macros", + "ddcommon 0.0.1", "futures", "glibc_version", "io-lifetimes", @@ -1604,6 +1605,7 @@ dependencies = [ "hyper-util", "indexmap 2.2.6", "lazy_static", + "libc 0.2.154", "log", "maplit", "pin-project", @@ -1614,6 +1616,7 @@ dependencies = [ "static_assertions", "tokio", "tokio-rustls 0.26.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/libdatadog b/libdatadog index cfae8afc60..e899c91b52 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit cfae8afc60c7fdde0f3737927ad1b01821e4bbd9 +Subproject commit e899c91b52a15bf056e27ce164f37542b0e84b24 diff --git a/tests/ext/force_flush_traces.phpt b/tests/ext/force_flush_traces.phpt index d1357ea147..0381291c5f 100644 --- a/tests/ext/force_flush_traces.phpt +++ b/tests/ext/force_flush_traces.phpt @@ -43,5 +43,4 @@ var_dump(dd_trace_serialize_closed_spans()); // Spans should be flushed, so this tracing process process [ddtrace] [info] Flushing trace of size 3 to send-queue for %s -kill -%r\n*(Killed\n*)?(Termsig=9)?%r +kill%r\n*(Killed\n*)?(Termsig=9)?%r diff --git a/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt b/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt index d064760b10..c482ff0684 100644 --- a/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt +++ b/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt @@ -6,6 +6,8 @@ Long running autoflush DD_TRACE_GENERATE_ROOT_SPAN=false DD_TRACE_AUTO_FLUSH_ENABLED=true DD_TRACE_LOG_LEVEL=info,startup=off +--INI-- +datadog.trace.sources_path= --FILE-- Date: Fri, 6 Sep 2024 15:55:11 +0200 Subject: [PATCH 062/103] Track peak memory usage in root span metrics (#2840) Fixes #2832. Signed-off-by: Bob Weinand --- .../client_init_record_span_tags.phpt | 2 ++ .../extension/rinit_record_span_tags.phpt | 2 ++ appsec/tests/extension/root_span_add_tag.phpt | 6 +++++- ...t_span_add_tag_with_intermediate_spans.phpt | 2 ++ docker-compose.yml | 2 +- ext/configuration.h | 1 + ext/serializer.c | 10 ++++++++-- github-actions-helpers/Build.Github.cs | 2 +- tests/Common/SnapshotTestTrait.php | 8 ++++---- tests/Common/SpanChecker.php | 8 ++++++++ .../Autoloaded/CompileTimeDisabledTest.php | 5 +++++ .../Autoloaded/CompileTimeEnabledTest.php | 5 +++++ .../Guzzle/V6/GuzzleIntegrationTest.php | 2 ++ .../Guzzle/V7/GuzzleIntegrationTest.php | 2 ++ .../Symfony/V4_4/MessengerTest.php | 2 ++ .../Symfony/V5_2/MessengerTest.php | 2 ++ .../Symfony/V6_2/MessengerTest.php | 2 ++ .../Symfony/V7_0/MessengerTest.php | 2 ++ tests/ext/close_spans_until.phpt | 2 +- .../distributed_trace_bogus_ids.phpt | 6 +++++- .../distributed_trace_inherit.phpt | 6 +++++- .../source_code/commit_sha_env_var.phpt | 6 +++++- .../git_metadata_injection_from_env.phpt | 6 +++++- ...it_metadata_injection_from_global_tags.phpt | 6 +++++- ..._injection_remove_credentials_from_env.phpt | 6 +++++- .../source_code/repository_url_env_var.phpt | 6 +++++- tests/ext/sandbox-prehook/dd_trace_method.phpt | 12 ++++++++++-- .../ext/sandbox/dd_trace_function_complex.phpt | 18 +++++++++++++++--- .../sandbox/dd_trace_function_internal.phpt | 6 +++++- tests/ext/sandbox/dd_trace_method.phpt | 12 ++++++++++-- tests/ext/sandbox/die_in_sandbox.phpt | 2 +- .../errors_are_flagged_from_userland.phpt | 6 +++++- tests/ext/sandbox/span_clone.phpt | 6 +++++- tests/ext/span_with_removed_exception.phpt | 18 +++++++++++++++--- tests/ext/start_span_with_all_properties.phpt | 12 ++++++++++-- tests/ext/start_span_without_closing.phpt | 6 +++++- 36 files changed, 173 insertions(+), 34 deletions(-) diff --git a/appsec/tests/extension/client_init_record_span_tags.phpt b/appsec/tests/extension/client_init_record_span_tags.phpt index 8a86280ff4..5455aeafd6 100644 --- a/appsec/tests/extension/client_init_record_span_tags.phpt +++ b/appsec/tests/extension/client_init_record_span_tags.phpt @@ -109,4 +109,6 @@ Array [_dd.agent_psr] => 1 [_sampling_priority_v1] => 1 [php.compilation.total_time_ms] => %f + [php.memory.peak_usage_bytes] => %f + [php.memory.peak_real_usage_bytes] => %f ) diff --git a/appsec/tests/extension/rinit_record_span_tags.phpt b/appsec/tests/extension/rinit_record_span_tags.phpt index 981f0ade01..3ffe1d1f79 100644 --- a/appsec/tests/extension/rinit_record_span_tags.phpt +++ b/appsec/tests/extension/rinit_record_span_tags.phpt @@ -102,4 +102,6 @@ Array [_dd.agent_psr] => 1 [_sampling_priority_v1] => 1 [php.compilation.total_time_ms] => %f + [php.memory.peak_usage_bytes] => %f + [php.memory.peak_real_usage_bytes] => %f ) diff --git a/appsec/tests/extension/root_span_add_tag.phpt b/appsec/tests/extension/root_span_add_tag.phpt index fbb7086f03..46b3375cf5 100644 --- a/appsec/tests/extension/root_span_add_tag.phpt +++ b/appsec/tests/extension/root_span_add_tag.phpt @@ -56,7 +56,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { [%s"]=> float(%d) ["_dd.agent_psr"]=> @@ -65,6 +65,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%s) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/appsec/tests/extension/root_span_add_tag_with_intermediate_spans.phpt b/appsec/tests/extension/root_span_add_tag_with_intermediate_spans.phpt index fe8b0b9b3f..1a6f89f10c 100644 --- a/appsec/tests/extension/root_span_add_tag_with_intermediate_spans.phpt +++ b/appsec/tests/extension/root_span_add_tag_with_intermediate_spans.phpt @@ -84,4 +84,6 @@ Array [_dd.agent_psr] => 1 [_sampling_priority_v1] => 1 [php.compilation.total_time_ms] => %s + [php.memory.peak_usage_bytes] => %f + [php.memory.peak_real_usage_bytes] => %f ) diff --git a/docker-compose.yml b/docker-compose.yml index 77916a295f..d5e4d00842 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -224,7 +224,7 @@ services: - DD_DISABLE_ERROR_RESPONSES=true - SNAPSHOTS_DIR=/snapshots - SNAPSHOT_CI=0 - - SNAPSHOT_REMOVED_ATTRS=start,duration,metrics.php.compilation.total_time_ms,metrics.process_id + - SNAPSHOT_REMOVED_ATTRS=start,duration,metrics.php.compilation.total_time_ms,metrics.php.memory.peak_usage_bytes,metrics.php.memory.peak_real_usage_bytes,metrics.process_id - ENABLED_CHECKS=trace_stall,trace_peer_service,trace_dd_service diff --git a/ext/configuration.h b/ext/configuration.h index 4a8b051fea..2c4bd325de 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -115,6 +115,7 @@ enum ddtrace_sampling_rules_format { CONFIG(BOOL, DD_TRACE_AUTO_FLUSH_ENABLED, "false") \ CONFIG(BOOL, DD_TRACE_CLI_ENABLED, "false") \ CONFIG(BOOL, DD_TRACE_MEASURE_COMPILE_TIME, "true") \ + CONFIG(BOOL, DD_TRACE_MEASURE_PEAK_MEMORY_USAGE, "true") \ CONFIG(BOOL, DD_TRACE_DEBUG, "false", .ini_change = ddtrace_alter_dd_trace_debug) \ CONFIG(BOOL, DD_TRACE_ENABLED, "true", .ini_change = ddtrace_alter_dd_trace_disabled_config, \ .env_config_fallback = ddtrace_conf_otel_traces_exporter) \ diff --git a/ext/serializer.c b/ext/serializer.c index 7f97e61d38..8e521512fb 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -1847,8 +1847,14 @@ void ddtrace_serialize_span_to_array(ddtrace_span_data *span, zval *array) { } } - if (ddtrace_span_is_entrypoint_root(span) && get_DD_TRACE_MEASURE_COMPILE_TIME()) { - add_assoc_double(&metrics_zv, "php.compilation.total_time_ms", ddtrace_compile_time_get() / 1000.); + if (ddtrace_span_is_entrypoint_root(span)) { + if (get_DD_TRACE_MEASURE_COMPILE_TIME()) { + add_assoc_double(&metrics_zv, "php.compilation.total_time_ms", ddtrace_compile_time_get() / 1000.); + } + if (get_DD_TRACE_MEASURE_PEAK_MEMORY_USAGE()) { + add_assoc_double(&metrics_zv, "php.memory.peak_usage_bytes", zend_memory_peak_usage(false)); + add_assoc_double(&metrics_zv, "php.memory.peak_real_usage_bytes", zend_memory_peak_usage(true)); + } } LOGEV(SPAN, { diff --git a/github-actions-helpers/Build.Github.cs b/github-actions-helpers/Build.Github.cs index 87b4978907..37a0377f96 100644 --- a/github-actions-helpers/Build.Github.cs +++ b/github-actions-helpers/Build.Github.cs @@ -164,7 +164,7 @@ string CleanValue(string value) char[] charsToTrim = { ' ', ',' }; string cleaned = value.TrimStart('-', '+').Trim(charsToTrim); - string[] keysToReplace = { "start", "duration", "php.compilation.total_time_ms", "process_id" }; + string[] keysToReplace = { "start", "duration", "php.compilation.total_time_ms", "metrics.php.memory.peak_usage_bytes", "metrics.php.memory.peak_real_usage_bytes", "process_id" }; foreach (var key in keysToReplace) { if (cleaned.Contains(key)) diff --git a/tests/Common/SnapshotTestTrait.php b/tests/Common/SnapshotTestTrait.php index b825e35085..49a5de1be1 100644 --- a/tests/Common/SnapshotTestTrait.php +++ b/tests/Common/SnapshotTestTrait.php @@ -123,7 +123,7 @@ private function waitForTraces(string $token, int $numExpectedTraces = 0) */ private function stopAndCompareSnapshotSession( string $token, - array $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'meta.error.stack', 'meta._dd.p.tid'], + array $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'metrics.php.memory.peak_usage_bytes', 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd.p.tid'], int $numExpectedTraces = 1, bool $snapshotMetrics = false, array $fieldsToIgnoreMetrics = ['openai.request.duration'], @@ -290,7 +290,7 @@ private function filterMetrics($metrics, $fieldsToIgnore) public function tracesFromWebRequestSnapshot( $fn, - $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'meta.error.stack', 'meta._dd.p.tid', 'start', 'duration'], + $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'metrics.php.memory.peak_usage_bytes', 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd.p.tid', 'start', 'duration'], $numExpectedTraces = 1, $tracer = null ) { @@ -317,7 +317,7 @@ public function tracesFromWebRequestSnapshot( public function isolateTracerSnapshot( $fn, - $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'meta.error.stack', 'meta._dd.p.tid'], + $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'metrics.php.memory.peak_usage_bytes', 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd.p.tid'], $numExpectedTraces = 1, $tracer = null, $config = [], @@ -370,7 +370,7 @@ public function isolateTracerSnapshot( public function snapshotFromTraces( $traces, - $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'meta.error.stack', 'meta._dd.p.tid'], + $fieldsToIgnore = ['metrics.php.compilation.total_time_ms', 'metrics.php.memory.peak_usage_bytes', 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd.p.tid'], $tokenSubstitute = null, $ignoreSampledAway = false ) { diff --git a/tests/Common/SpanChecker.php b/tests/Common/SpanChecker.php index 3153a8551b..a8c473ad84 100644 --- a/tests/Common/SpanChecker.php +++ b/tests/Common/SpanChecker.php @@ -87,6 +87,8 @@ public static function dumpSpansGraph(array $spansGraph, int $indent = 0) } if (isset($span['metrics'])) { unset($span['metrics']['php.compilation.total_time_ms']); + unset($span['metrics']['php.memory.peak_usage_bytes']); + unset($span['metrics']['php.memory.peak_real_usage_bytes']); unset($span['metrics']['process_id']); foreach ($span['metrics'] as $k => $v) { $out .= str_repeat(' ', $indent) . ' ' . $k . ' => ' . $v . "\n"; @@ -524,6 +526,12 @@ function ($key) use ($pattern) { if (!isset($metrics['php.compilation.total_time_ms'])) { unset($spanMetrics['php.compilation.total_time_ms']); } + if (!isset($metrics['php.memory.peak_usage_bytes'])) { + unset($spanMetrics['php.memory.peak_usage_bytes']); + } + if (!isset($metrics['php.memory.peak_real_usage_bytes'])) { + unset($spanMetrics['php.memory.peak_real_usage_bytes']); + } if (isset($metrics['process_id'])) { unset($metrics['process_id']); } diff --git a/tests/Integrations/Custom/Autoloaded/CompileTimeDisabledTest.php b/tests/Integrations/Custom/Autoloaded/CompileTimeDisabledTest.php index 75d9ca48e6..90a5b07460 100644 --- a/tests/Integrations/Custom/Autoloaded/CompileTimeDisabledTest.php +++ b/tests/Integrations/Custom/Autoloaded/CompileTimeDisabledTest.php @@ -16,6 +16,7 @@ protected static function getEnvs() { return array_merge(parent::getEnvs(), [ 'DD_TRACE_MEASURE_COMPILE_TIME' => '0', + 'DD_TRACE_MEASURE_PEAK_MEMORY_USAGE' => '0', ]); } @@ -26,12 +27,14 @@ protected function ddSetUp() * For the compile-time metrics specifically, this goofs things up, so let's disable. */ self::putenv('DD_TRACE_MEASURE_COMPILE_TIME=0'); + self::putenv('DD_TRACE_MEASURE_PEAK_MEMORY_USAGE=0'); \dd_trace_internal_fn('ddtrace_reload_config'); } protected function ddTearDown() { self::putenv('DD_TRACE_MEASURE_COMPILE_TIME'); + self::putenv('DD_TRACE_MEASURE_PEAK_MEMORY_USAGE'); dd_trace_internal_fn('ddtrace_reload_config'); parent::ddTearDown(); } @@ -44,5 +47,7 @@ public function testScenario() }); self::assertFalse(isset($traces[0][0]['metrics']['php.compilation.total_time_ms'])); + self::assertFalse(isset($traces[0][0]['metrics']['php.memory.peak_usage_bytes'])); + self::assertFalse(isset($traces[0][0]['metrics']['php.memory.peak_real_usage_bytes'])); } } diff --git a/tests/Integrations/Custom/Autoloaded/CompileTimeEnabledTest.php b/tests/Integrations/Custom/Autoloaded/CompileTimeEnabledTest.php index 98de286e2e..32bd515bff 100644 --- a/tests/Integrations/Custom/Autoloaded/CompileTimeEnabledTest.php +++ b/tests/Integrations/Custom/Autoloaded/CompileTimeEnabledTest.php @@ -16,6 +16,7 @@ protected static function getEnvs() { return array_merge(parent::getEnvs(), [ 'DD_TRACE_MEASURE_COMPILE_TIME' => '1', + 'DD_TRACE_MEASURE_PEAK_MEMORY_USAGE' => '1', ]); } @@ -26,12 +27,14 @@ protected function ddSetUp() * For the compile-time metrics specifically, this goofs things up, so let's disable. */ self::putenv('DD_TRACE_MEASURE_COMPILE_TIME=0'); + self::putenv('DD_TRACE_MEASURE_PEAK_MEMORY_USAGE=0'); \dd_trace_internal_fn('ddtrace_reload_config'); } protected function ddTearDown() { self::putenv('DD_TRACE_MEASURE_COMPILE_TIME'); + self::putenv('DD_TRACE_MEASURE_PEAK_MEMORY_USAGE'); dd_trace_internal_fn('ddtrace_reload_config'); parent::ddTearDown(); } @@ -44,5 +47,7 @@ public function testScenario() }); self::assertTrue(isset($traces[0][0]['metrics']['php.compilation.total_time_ms'])); + self::assertTrue(isset($traces[0][0]['metrics']['php.memory.peak_usage_bytes'])); + self::assertTrue(isset($traces[0][0]['metrics']['php.memory.peak_real_usage_bytes'])); } } diff --git a/tests/Integrations/Guzzle/V6/GuzzleIntegrationTest.php b/tests/Integrations/Guzzle/V6/GuzzleIntegrationTest.php index 46604cc87f..3626216180 100644 --- a/tests/Integrations/Guzzle/V6/GuzzleIntegrationTest.php +++ b/tests/Integrations/Guzzle/V6/GuzzleIntegrationTest.php @@ -516,6 +516,8 @@ public function testMultiExec() }, [ 'start', 'metrics.php.compilation.total_time_ms', + 'metrics.php.memory.peak_usage_bytes', + 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd.p.tid', 'meta.curl.appconnect_time_us', diff --git a/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php b/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php index 87086fe707..4d6a93c09c 100644 --- a/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php +++ b/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php @@ -84,6 +84,8 @@ public function testMultiExec() }, [ 'start', 'metrics.php.compilation.total_time_ms', + 'metrics.php.memory.peak_usage_bytes', + 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd.p.tid', 'meta.curl.appconnect_time_us', diff --git a/tests/Integrations/Symfony/V4_4/MessengerTest.php b/tests/Integrations/Symfony/V4_4/MessengerTest.php index dc387c2169..cc58c62907 100644 --- a/tests/Integrations/Symfony/V4_4/MessengerTest.php +++ b/tests/Integrations/Symfony/V4_4/MessengerTest.php @@ -10,6 +10,8 @@ class MessengerTest extends WebFrameworkTestCase { const FIELDS_TO_IGNORE = [ 'metrics.php.compilation.total_time_ms', + 'metrics.php.memory.peak_usage_bytes', + 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd..tid', 'meta.messaging.message_id', diff --git a/tests/Integrations/Symfony/V5_2/MessengerTest.php b/tests/Integrations/Symfony/V5_2/MessengerTest.php index 814be69070..3bf22a2327 100644 --- a/tests/Integrations/Symfony/V5_2/MessengerTest.php +++ b/tests/Integrations/Symfony/V5_2/MessengerTest.php @@ -10,6 +10,8 @@ class MessengerTest extends WebFrameworkTestCase { const FIELDS_TO_IGNORE = [ 'metrics.php.compilation.total_time_ms', + 'metrics.php.memory.peak_usage_bytes', + 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd..tid', 'meta.messaging.message_id', diff --git a/tests/Integrations/Symfony/V6_2/MessengerTest.php b/tests/Integrations/Symfony/V6_2/MessengerTest.php index bc801867ec..c3c2adffc3 100644 --- a/tests/Integrations/Symfony/V6_2/MessengerTest.php +++ b/tests/Integrations/Symfony/V6_2/MessengerTest.php @@ -10,6 +10,8 @@ class MessengerTest extends WebFrameworkTestCase { const FIELDS_TO_IGNORE = [ 'metrics.php.compilation.total_time_ms', + 'metrics.php.memory.peak_usage_bytes', + 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd..tid', 'meta.messaging.message_id', diff --git a/tests/Integrations/Symfony/V7_0/MessengerTest.php b/tests/Integrations/Symfony/V7_0/MessengerTest.php index 839c2339e5..cf729fb468 100644 --- a/tests/Integrations/Symfony/V7_0/MessengerTest.php +++ b/tests/Integrations/Symfony/V7_0/MessengerTest.php @@ -10,6 +10,8 @@ class MessengerTest extends WebFrameworkTestCase { const FIELDS_TO_IGNORE = [ 'metrics.php.compilation.total_time_ms', + 'metrics.php.memory.peak_usage_bytes', + 'metrics.php.memory.peak_real_usage_bytes', 'meta.error.stack', 'meta._dd.p.tid', 'meta.messaging.message_id', diff --git a/tests/ext/close_spans_until.phpt b/tests/ext/close_spans_until.phpt index 33f5209ba1..f362895631 100644 --- a/tests/ext/close_spans_until.phpt +++ b/tests/ext/close_spans_until.phpt @@ -48,7 +48,7 @@ int(2) [ddtrace] [span] Switching to different SpanStack: %d int(1) int(0) -[ddtrace] [span] Encoding span %d: trace_id=%s, name='close_spans_until.php', service='close_spans_until.php', resource: 'close_spans_until.php', type 'cli' with tags: runtime-id='%s', _dd.p.dm='-0', _dd.p.tid='%s'; and metrics: process_id='%d', _dd.agent_psr='1', _sampling_priority_v1='1', php.compilation.total_time_ms='%f' +[ddtrace] [span] Encoding span %d: trace_id=%s, name='close_spans_until.php', service='close_spans_until.php', resource: 'close_spans_until.php', type 'cli' with tags: runtime-id='%s', _dd.p.dm='-0', _dd.p.tid='%s'; and metrics: process_id='%d', _dd.agent_psr='1', _sampling_priority_v1='1', php.compilation.total_time_ms='%f', php.memory.peak_usage_bytes='%f', php.memory.peak_real_usage_bytes='%f' [ddtrace] [span] Encoding span %d: trace_id=%s, name='traced', service='close_spans_until.php', resource: 'traced', type 'cli' with tags: -; and metrics: - [ddtrace] [span] Encoding span %d: trace_id=%s, name='', service='close_spans_until.php', resource: '', type 'cli' with tags: -; and metrics: - [ddtrace] [span] Encoding span %d: trace_id=%s, name='', service='close_spans_until.php', resource: '', type 'cli' with tags: -; and metrics: - diff --git a/tests/ext/distributed_tracing/distributed_trace_bogus_ids.phpt b/tests/ext/distributed_tracing/distributed_trace_bogus_ids.phpt index 7621e5e154..95d2fe36bf 100644 --- a/tests/ext/distributed_tracing/distributed_trace_bogus_ids.phpt +++ b/tests/ext/distributed_tracing/distributed_trace_bogus_ids.phpt @@ -47,7 +47,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -56,6 +56,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/distributed_tracing/distributed_trace_inherit.phpt b/tests/ext/distributed_tracing/distributed_trace_inherit.phpt index eec5fb4034..f559599153 100644 --- a/tests/ext/distributed_tracing/distributed_trace_inherit.phpt +++ b/tests/ext/distributed_tracing/distributed_trace_inherit.phpt @@ -59,13 +59,17 @@ array(2) { string(7) "datadog" } ["metrics"]=> - array(3) { + array(5) { ["process_id"]=> float(%f) ["_sampling_priority_v1"]=> float(3) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> diff --git a/tests/ext/integrations/source_code/commit_sha_env_var.phpt b/tests/ext/integrations/source_code/commit_sha_env_var.phpt index 4cfa334504..d158be18a1 100644 --- a/tests/ext/integrations/source_code/commit_sha_env_var.phpt +++ b/tests/ext/integrations/source_code/commit_sha_env_var.phpt @@ -49,7 +49,7 @@ array(2) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -58,6 +58,10 @@ array(2) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> diff --git a/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt b/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt index 46f1bec83a..31c2d5013f 100644 --- a/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt +++ b/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt @@ -52,7 +52,7 @@ array(2) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%d) ["_dd.agent_psr"]=> @@ -61,6 +61,10 @@ array(2) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> diff --git a/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt b/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt index 2201f2e9fa..56059b30e5 100644 --- a/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt +++ b/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt @@ -55,7 +55,7 @@ array(2) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%d) ["_dd.agent_psr"]=> @@ -64,6 +64,10 @@ array(2) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> diff --git a/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt b/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt index b2ab4e940c..e90a2a8e7e 100644 --- a/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt +++ b/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt @@ -52,7 +52,7 @@ array(2) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%d) ["_dd.agent_psr"]=> @@ -61,6 +61,10 @@ array(2) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> diff --git a/tests/ext/integrations/source_code/repository_url_env_var.phpt b/tests/ext/integrations/source_code/repository_url_env_var.phpt index 12a04d6b11..db27df5267 100644 --- a/tests/ext/integrations/source_code/repository_url_env_var.phpt +++ b/tests/ext/integrations/source_code/repository_url_env_var.phpt @@ -49,7 +49,7 @@ array(2) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%d) ["_dd.agent_psr"]=> @@ -58,6 +58,10 @@ array(2) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> diff --git a/tests/ext/sandbox-prehook/dd_trace_method.phpt b/tests/ext/sandbox-prehook/dd_trace_method.phpt index 5b09b27dd5..1e3aeb855d 100644 --- a/tests/ext/sandbox-prehook/dd_trace_method.phpt +++ b/tests/ext/sandbox-prehook/dd_trace_method.phpt @@ -121,7 +121,7 @@ array(3) { string(16) "%s" } ["metrics"]=> - array(6) { + array(8) { ["process_id"]=> float(%f) ["foo"]=> @@ -134,6 +134,10 @@ array(3) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> @@ -190,7 +194,7 @@ array(3) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -199,6 +203,10 @@ array(3) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/sandbox/dd_trace_function_complex.phpt b/tests/ext/sandbox/dd_trace_function_complex.phpt index 0394cb5023..2409725bac 100644 --- a/tests/ext/sandbox/dd_trace_function_complex.phpt +++ b/tests/ext/sandbox/dd_trace_function_complex.phpt @@ -128,7 +128,7 @@ array(5) { string(16) "%s" } ["metrics"]=> - array(6) { + array(8) { ["process_id"]=> float(%f) ["foo"]=> @@ -141,6 +141,10 @@ array(5) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> @@ -223,7 +227,7 @@ array(5) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -232,6 +236,10 @@ array(5) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [4]=> @@ -262,7 +270,7 @@ array(5) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -271,6 +279,10 @@ array(5) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/sandbox/dd_trace_function_internal.phpt b/tests/ext/sandbox/dd_trace_function_internal.phpt index fc1acb3d62..8e94373a98 100644 --- a/tests/ext/sandbox/dd_trace_function_internal.phpt +++ b/tests/ext/sandbox/dd_trace_function_internal.phpt @@ -51,7 +51,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -60,6 +60,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/sandbox/dd_trace_method.phpt b/tests/ext/sandbox/dd_trace_method.phpt index 5274ed6e67..33705ad6ea 100644 --- a/tests/ext/sandbox/dd_trace_method.phpt +++ b/tests/ext/sandbox/dd_trace_method.phpt @@ -136,7 +136,7 @@ array(3) { string(16) "%s" } ["metrics"]=> - array(6) { + array(8) { ["process_id"]=> float(%f) ["foo"]=> @@ -149,6 +149,10 @@ array(3) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> @@ -209,7 +213,7 @@ array(3) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -218,6 +222,10 @@ array(3) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/sandbox/die_in_sandbox.phpt b/tests/ext/sandbox/die_in_sandbox.phpt index 988f595585..d70f85d96b 100644 --- a/tests/ext/sandbox/die_in_sandbox.phpt +++ b/tests/ext/sandbox/die_in_sandbox.phpt @@ -16,6 +16,6 @@ x(); ?> --EXPECTF-- [ddtrace] [warning] UnwindExit thrown in ddtrace's closure defined at %s:%d for x(): -[ddtrace] [span] Encoding span %d: trace_id=%s, name='die_in_sandbox.php', service='die_in_sandbox.php', resource: 'die_in_sandbox.php', type 'cli' with tags: runtime-id='%s', _dd.p.dm='-0', _dd.p.tid='%s'; and metrics: process_id='%d', _dd.agent_psr='1', _sampling_priority_v1='1', php.compilation.total_time_ms='%f' +[ddtrace] [span] Encoding span %d: trace_id=%s, name='die_in_sandbox.php', service='die_in_sandbox.php', resource: 'die_in_sandbox.php', type 'cli' with tags: runtime-id='%s', _dd.p.dm='-0', _dd.p.tid='%s'; and metrics: process_id='%d', _dd.agent_psr='1', _sampling_priority_v1='1', php.compilation.total_time_ms='%f', php.memory.peak_usage_bytes='%f', php.memory.peak_real_usage_bytes='%f' [ddtrace] [span] Encoding span %d: trace_id=%s, name='x', service='die_in_sandbox.php', resource: 'x', type 'cli' with tags: -; and metrics: - [ddtrace] [info] Flushing trace of size 2 to send-queue for %s diff --git a/tests/ext/sandbox/errors_are_flagged_from_userland.phpt b/tests/ext/sandbox/errors_are_flagged_from_userland.phpt index 2214480f58..244ac1ce2c 100644 --- a/tests/ext/sandbox/errors_are_flagged_from_userland.phpt +++ b/tests/ext/sandbox/errors_are_flagged_from_userland.phpt @@ -55,7 +55,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -64,6 +64,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/sandbox/span_clone.phpt b/tests/ext/sandbox/span_clone.phpt index 34fe3f3604..1961a50873 100644 --- a/tests/ext/sandbox/span_clone.phpt +++ b/tests/ext/sandbox/span_clone.phpt @@ -240,7 +240,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -249,6 +249,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/span_with_removed_exception.phpt b/tests/ext/span_with_removed_exception.phpt index 0231ce6469..b34267c41b 100644 --- a/tests/ext/span_with_removed_exception.phpt +++ b/tests/ext/span_with_removed_exception.phpt @@ -77,7 +77,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -86,6 +86,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } @@ -118,7 +122,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -127,6 +131,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } @@ -159,7 +167,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -168,6 +176,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/start_span_with_all_properties.phpt b/tests/ext/start_span_with_all_properties.phpt index 6bf0c725ff..28a6701904 100644 --- a/tests/ext/start_span_with_all_properties.phpt +++ b/tests/ext/start_span_with_all_properties.phpt @@ -82,7 +82,7 @@ array(2) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -91,6 +91,10 @@ array(2) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } [1]=> @@ -123,7 +127,7 @@ array(2) { string(16) "%s" } ["metrics"]=> - array(5) { + array(7) { ["process_id"]=> float(%f) ["cc"]=> @@ -134,6 +138,10 @@ array(2) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } diff --git a/tests/ext/start_span_without_closing.phpt b/tests/ext/start_span_without_closing.phpt index a3af7e2bab..9eaa55a6bf 100644 --- a/tests/ext/start_span_without_closing.phpt +++ b/tests/ext/start_span_without_closing.phpt @@ -54,7 +54,7 @@ array(1) { string(16) "%s" } ["metrics"]=> - array(4) { + array(6) { ["process_id"]=> float(%f) ["_dd.agent_psr"]=> @@ -63,6 +63,10 @@ array(1) { float(1) ["php.compilation.total_time_ms"]=> float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) } } } From 98839cf637cac6374236c4d40dc70bede9551472 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 6 Sep 2024 15:57:28 +0200 Subject: [PATCH 063/103] Fix github actions permissions (#2842) As inspired by Datadog/dd-trace-dotnet#5728. Signed-off-by: Bob Weinand --- .github/workflows/auto_add_pr_to_miletone.yml | 5 +++++ .github/workflows/auto_check_snapshots.yml | 3 +++ .github/workflows/auto_label_prs.yml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/.github/workflows/auto_add_pr_to_miletone.yml b/.github/workflows/auto_add_pr_to_miletone.yml index 17a27b08c6..357c9b1425 100644 --- a/.github/workflows/auto_add_pr_to_miletone.yml +++ b/.github/workflows/auto_add_pr_to_miletone.yml @@ -11,6 +11,11 @@ jobs: add_to_milestone: if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.title, '[Version Bump]') == false runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write # need to modify existing PR + issues: write # need to potentially create a new milestone + steps: - name: Checkout uses: actions/checkout@v2 diff --git a/.github/workflows/auto_check_snapshots.yml b/.github/workflows/auto_check_snapshots.yml index 18ee808434..e649dc7b47 100644 --- a/.github/workflows/auto_check_snapshots.yml +++ b/.github/workflows/auto_check_snapshots.yml @@ -6,6 +6,9 @@ on: jobs: check-snapshots: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write # need to add a comment to a PR steps: - name: Checkout diff --git a/.github/workflows/auto_label_prs.yml b/.github/workflows/auto_label_prs.yml index a45527256b..7b5a4e5d50 100644 --- a/.github/workflows/auto_label_prs.yml +++ b/.github/workflows/auto_label_prs.yml @@ -7,6 +7,10 @@ jobs: add-labels: runs-on: ubuntu-latest + permissions: + contents: read + issues: write # Update labels on PRs (might not be necessary, but we call the UpdateIssue API so...) + pull-requests: write # Update labels on PRs steps: - name: Checkout From 459fe0866dadcad252ffb490fff2039c265ef390 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Tue, 10 Sep 2024 23:17:00 +0200 Subject: [PATCH 064/103] Crashtracker: fix a use-after-free error (#2843) * Crashtracker: fix a use-after-free error * Set DD_LOG_BACKTRACE default value to "false" when compiled with libasan --- ext/configuration.h | 8 +++++++- ext/signals.c | 21 +++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/ext/configuration.h b/ext/configuration.h index 2c4bd325de..b9c531f611 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -90,6 +90,12 @@ enum ddtrace_sampling_rules_format { #define DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP_DEFAULT \ "(?i)(?:(?:\"|%22)?)(?:(?:old[-_]?|new[-_]?)?p(?:ass)?w(?:or)?d(?:1|2)?|pass(?:[-_]?phrase)?|secret|(?:api[-_]?|private[-_]?|public[-_]?|access[-_]?|secret[-_]?|app(?:lication)?[-_]?)key(?:[-_]?id)?|token|consumer[-_]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\\s|%20)*(?:=|%3D)[^&]+|(?:\"|%22)(?:\\s|%20)*(?::|%3A)(?:\\s|%20)*(?:\"|%22)(?:%2[^2]|%[^2]|[^\"%])+(?:\"|%22))|(?:bearer(?:\\s|%20)+[a-z0-9._\\-]+|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\\w=-]|%3D)+\\.ey[I-L](?:[\\w=-]|%3D)+(?:\\.(?:[\\w.+/=-]|%3D|%2F|%2B)+)?|-{5}BEGIN(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY-{5}[^\\-]+-{5}END(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY(?:-{5})?(?:\\n|%0A)?|(?:ssh-(?:rsa|dss)|ecdsa-[a-z0-9]+-[a-z0-9]+)(?:\\s|%20|%09)+(?:[a-z0-9/.+]|%2F|%5C|%2B){100,}(?:=|%3D)*(?:(?:\\s|%20|%09)+[a-z0-9._-]+)?)" +#ifdef __SANITIZE_ADDRESS__ +#define DD_LOG_BACKTRACE_DEFAULT "false" +#else +#define DD_LOG_BACKTRACE_DEFAULT "true" +#endif + #define DD_CONFIGURATION_ALL \ CONFIG(STRING, DD_TRACE_SOURCES_PATH, DD_DEFAULT_SOURCES_PATH, .ini_change = zai_config_system_ini_change) \ CONFIG(BOOL, DD_AUTOLOAD_NO_COMPILE, "false", .ini_change = zai_config_system_ini_change) \ @@ -163,7 +169,7 @@ enum ddtrace_sampling_rules_format { CONFIG(INT, DD_TRACE_AGENT_CONNECT_TIMEOUT, DD_CFG_EXPSTR(DD_TRACE_AGENT_CONNECT_TIMEOUT_VAL), \ .ini_change = zai_config_system_ini_change) \ CONFIG(INT, DD_TRACE_DEBUG_PRNG_SEED, "-1", .ini_change = ddtrace_reseed_seed_change) \ - CONFIG(BOOL, DD_LOG_BACKTRACE, "true") \ + CONFIG(BOOL, DD_LOG_BACKTRACE, DD_LOG_BACKTRACE_DEFAULT) \ CONFIG(BOOL, DD_TRACE_GENERATE_ROOT_SPAN, "true", .ini_change = ddtrace_span_alter_root_span_config) \ CONFIG(INT, DD_TRACE_SPANS_LIMIT, "1000") \ CONFIG(BOOL, DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED, "true") \ diff --git a/ext/signals.c b/ext/signals.c index 19ccfdf24a..2f465045dc 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -37,7 +37,7 @@ // true globals; only modify in MINIT/MSHUTDOWN static stack_t ddtrace_altstack; static struct sigaction ddtrace_sigaction; -static ddog_CharSlice crashtracker_socket_path = {0}; +static char crashtracker_socket_path[100] = {0}; #define MAX_STACK_SIZE 1024 #define MIN_STACKSZ 16384 // enough to hold void *array[MAX_STACK_SIZE] plus a couple kilobytes @@ -101,8 +101,20 @@ static bool ddtrace_crashtracker_check_result(ddog_crasht_Result result, const c } static void ddtrace_init_crashtracker() { + ddog_CharSlice socket_path = ddog_sidecar_get_crashtracker_unix_socket_path(); + if (socket_path.len > sizeof(crashtracker_socket_path) - 1) { + LOG(ERROR, "Cannot initialize CrashTracker : the socket path is too long."); + free((void *) socket_path.ptr); + return; + } + + // Copy the string to a global buffer to avoid a use-after-free error + memcpy(crashtracker_socket_path, socket_path.ptr, socket_path.len); + crashtracker_socket_path[socket_path.len] = '\0'; + free((void *) socket_path.ptr); + socket_path.ptr = crashtracker_socket_path; + ddog_Endpoint *agent_endpoint = ddtrace_sidecar_agent_endpoint(); - crashtracker_socket_path = ddog_sidecar_get_crashtracker_unix_socket_path(); ddog_crasht_Config config = { .endpoint = agent_endpoint, @@ -139,7 +151,7 @@ static void ddtrace_init_crashtracker() { ddtrace_crashtracker_check_result( ddog_crasht_init_with_unix_socket( config, - crashtracker_socket_path, + socket_path, metadata ), "Cannot initialize CrashTracker" @@ -183,9 +195,6 @@ void ddtrace_signals_first_rinit(void) { void ddtrace_signals_mshutdown(void) { free(ddtrace_altstack.ss_sp); - if (crashtracker_socket_path.ptr) { - free((void *) crashtracker_socket_path.ptr); - } } #else From 186011fbc108dbb2761583f32f24b8240c982704 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 11 Sep 2024 10:50:40 +0200 Subject: [PATCH 065/103] Add PHP 8.0 to correctness tests (#2845) --- .github/workflows/prof_correctness.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prof_correctness.yml b/.github/workflows/prof_correctness.yml index aa3c8f1d1a..43c31e941b 100644 --- a/.github/workflows/prof_correctness.yml +++ b/.github/workflows/prof_correctness.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: [8.1, 8.2, 8.3] + php-version: [8.0, 8.1, 8.2, 8.3] phpts: [nts, zts] include: - phpts: zts From ebaa1ca1e21a83f57d7d5a9ebf4719c4f79a0d8d Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 12 Sep 2024 10:04:06 +0200 Subject: [PATCH 066/103] Improve pcntl tests stability (#2841) * Improve pcntl tests stability * Simplify code * Throw an exception when expected request is not found * Increase sleep time + make test easier to debug * Work around race conditions leading to different orders or numbers of requests * Remove custom DD_TRACE_SHUTDOWN_TIMEOUT value for most pcntl tests * Give a bit more time to Circle CI * Assert trace by trace because the SpanChecker does not support having multiple spans matching --- .gitlab-ci.yml | 2 +- tests/Common/SpanChecker.php | 2 +- tests/Common/TracerTestTrait.php | 98 +++++++++++++------ .../CLI/Symfony/V4_4/CommonScenariosTest.php | 2 +- .../CLI/Symfony/V5_2/CommonScenariosTest.php | 2 +- .../CLI/Symfony/V6_2/CommonScenariosTest.php | 2 +- .../Custom/Autoloaded/InstrumentationTest.php | 6 +- tests/Integrations/PCNTL/PCNTLTest.php | 72 ++++++++------ .../PCNTL/scripts/long-running-autoflush.php | 12 +-- .../Integration/InternalTelemetryTest.php | 6 +- tests/OpenTracing/InternalTelemetryTest.php | 6 +- 11 files changed, 126 insertions(+), 84 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c833c2835a..53e28da7c7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,7 +50,7 @@ download_artifacts: tags: [ "runner:main", "size:large" ] script: - | - sleep 1m # Let time for the CircleCI pipeline to start + sleep 2m # Let time for the CircleCI pipeline to start source .gitlab/download-circleci_artifact.sh download_circleci_artifact "gh/DataDog/dd-trace-php" "build_packages" "package extension" "dd-library-php-ssi-.*-x86_64-linux.tar.gz" "dd-library-php-ssi-x86_64-linux.tar.gz" diff --git a/tests/Common/SpanChecker.php b/tests/Common/SpanChecker.php index a8c473ad84..292d1caa8a 100644 --- a/tests/Common/SpanChecker.php +++ b/tests/Common/SpanChecker.php @@ -195,7 +195,7 @@ private function findOne(array $graph, SpanAssertion $expectedNodeRoot, $parentN // Not using a TestCase::markTestAsIncomplete() because it exits immediately, // while with an error log we are still able to proceed with tests. error_log(sprintf( - "WARNING: More then one candidate found for '%s' at the same level. " + "WARNING: More than one candidate found for '%s' at the same level. " . "Proceeding in the order they appears. " . "This might not work if this span is not a leaf span.", $expectedNodeRoot diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index 66430ffe8e..a74e03f946 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -216,12 +216,12 @@ public function inWebServer($fn, $rootPath, $envs = [], $inis = [], &$curlInfo = /** * This method executes a single script with the provided configuration. */ - public function inCli($scriptPath, $customEnvs = [], $customInis = [], $arguments = '', $withOutput = false) + public function inCli($scriptPath, $customEnvs = [], $customInis = [], $arguments = '', $withOutput = false, $until = null, $throw = true) { $this->resetRequestDumper(); $output = $this->executeCli($scriptPath, $customEnvs, $customInis, $arguments, $withOutput); usleep(100000); // Add a slight delay to give the request-replayer time to handle and store all requests. - $out = [$this->parseTracesFromDumpedData()]; + $out = [$this->parseTracesFromDumpedData($until, $throw)]; if ($withOutput) { $out[] = $output; } @@ -387,9 +387,9 @@ private function parseRawDumpedSpans($rawSpans) * @return array * @throws \Exception */ - private function parseTracesFromDumpedData(callable $until = null) + private function parseTracesFromDumpedData(callable $until = null, $throw = false) { - $loaded = $this->retrieveDumpedTraceData($until); + $loaded = $this->retrieveDumpedTraceData($until, $throw); if (!$loaded) { return []; } @@ -443,12 +443,6 @@ public function parseMultipleRequestsFromDumpedData() */ public function retrieveDumpedData(callable $until = null, $throw = false) { - if (!$until) { - $until = function ($request) { - return (strpos($request["uri"] ?? "", "/telemetry/") !== 0); - }; - } - return $this->retrieveAnyDumpedData($until, $throw); } @@ -457,16 +451,12 @@ public function retrieveDumpedData(callable $until = null, $throw = false) */ public function retrieveDumpedMetrics(callable $until = null, $throw = false) { - if (!$until) { - $until = function ($request) { - return (strpos($request["uri"] ?? "", "/telemetry/") !== 0); - }; - } - return $this->retrieveAnyDumpedData($until, $throw, true); } - public function retrieveAnyDumpedData(callable $until, $throw, $metrics = false) { + public function retrieveAnyDumpedData(callable $until = null, $throw, $metrics = false) { + $until = $until ?? $this->untilFirstTraceRequest(); + $allResponses = []; // When tests run with the background sender enabled, there might be some delay between when a trace is flushed @@ -495,7 +485,7 @@ public function retrieveAnyDumpedData(callable $until, $throw, $metrics = false) return $allResponses; } } - \usleep(1000); + \usleep(10000); } } @@ -506,17 +496,69 @@ public function retrieveAnyDumpedData(callable $until, $throw, $metrics = false) return $allResponses; } - public function retrieveDumpedTraceData(callable $until = null) + public function retrieveDumpedTraceData(callable $until = null, $throw = false) { - if ($until) { - return array_values(array_filter($this->retrieveDumpedData($until), function ($request) use ($until) { - return $until($request); - })); - } else { - return array_values(array_filter($this->retrieveDumpedData(), function ($request) { - return strpos($request["uri"] ?? "", "/telemetry/") !== 0; - })); - } + return array_values(array_filter($this->retrieveDumpedData($until, $throw), function ($request) { + // Filter telemetry requests + return strpos($request["uri"] ?? "", "/telemetry/") !== 0; + })); + } + + function untilNumberOfTraces($number) { + $count = 0; + return function ($request) use (&$count, $number) { + $count += $request['headers']['X-Datadog-Trace-Count'] ?? 0; + return $count >= $number; + }; + } + + function untilFirstTraceRequest() { + return function ($request) { + return (strpos($request["uri"] ?? "", "/v0.4/traces") === 0) + || (strpos($request["uri"] ?? "", "/v0.7/traces") === 0) + ; + }; + } + + function untilTelemetryRequest($metricName) { + return function ($request) use ($metricName) { + return (strpos($request["uri"] ?? "", "/telemetry/") === 0) + && (strpos($request["body"] ?? "", $metricName) !== false) + ; + }; + } + + function untilSpan(SpanAssertion $assertion) { + return function ($request) use ($assertion) { + if (strpos($request["uri"] ?? "", "/telemetry/") === 0 || !isset($request['body'])) { + return false; + } + $traces = $this->parseRawDumpedTraces(json_decode($request['body'], true)); + + foreach ($traces as $trace) { + try { + (new SpanChecker())->assertFlameGraph([$trace], [$assertion]); + } catch (\Exception $e) { + continue; + } + + return true; + } + + return false; + }; + } + + function until(...$expectations) { + return function ($request) use (&$expectations) { + foreach ($expectations as $key => $expect) { + if ($expect($request)) { + unset($expectations[$key]); + } + } + + return !count($expectations); + }; } /** diff --git a/tests/Integrations/CLI/Symfony/V4_4/CommonScenariosTest.php b/tests/Integrations/CLI/Symfony/V4_4/CommonScenariosTest.php index 3be74bec74..7385003657 100644 --- a/tests/Integrations/CLI/Symfony/V4_4/CommonScenariosTest.php +++ b/tests/Integrations/CLI/Symfony/V4_4/CommonScenariosTest.php @@ -153,7 +153,7 @@ public function testLongRunningCommandWithoutRootSpan() 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_EXEC_ENABLED' => 'false', - ], [], 'about'); + ], [], 'about', false, null, false); $this->assertFlameGraph( $traces, diff --git a/tests/Integrations/CLI/Symfony/V5_2/CommonScenariosTest.php b/tests/Integrations/CLI/Symfony/V5_2/CommonScenariosTest.php index 0eaeb36480..6496046189 100644 --- a/tests/Integrations/CLI/Symfony/V5_2/CommonScenariosTest.php +++ b/tests/Integrations/CLI/Symfony/V5_2/CommonScenariosTest.php @@ -153,7 +153,7 @@ public function testLongRunningCommandWithoutRootSpan() 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_EXEC_ENABLED' => 'false', - ], [], 'about'); + ], [], 'about', false, null, false); $this->assertFlameGraph( $traces, diff --git a/tests/Integrations/CLI/Symfony/V6_2/CommonScenariosTest.php b/tests/Integrations/CLI/Symfony/V6_2/CommonScenariosTest.php index fb47ec52b1..3e60c64c8c 100644 --- a/tests/Integrations/CLI/Symfony/V6_2/CommonScenariosTest.php +++ b/tests/Integrations/CLI/Symfony/V6_2/CommonScenariosTest.php @@ -153,7 +153,7 @@ public function testLongRunningCommandWithoutRootSpan() 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_EXEC_ENABLED' => 'false', - ], [], 'about'); + ], [], 'about', false, null, false); $this->assertFlameGraph( $traces, diff --git a/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php b/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php index 3bb94ee9b6..7532739549 100644 --- a/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php +++ b/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php @@ -53,11 +53,7 @@ public function testInstrumentation() $this->resetRequestDumper(); $this->call(GetSpec::create("autoloaded", "/simple")); - $response = $this->retrieveDumpedData(function ($request) { - return (strpos($request["uri"] ?? "", "/telemetry/") === 0) - && (strpos($request["body"] ?? "", "spans_created") !== false) - ; - }, true); + $response = $this->retrieveDumpedData($this->untilTelemetryRequest("spans_created")); $this->assertGreaterThanOrEqual(3, $response); $payloads = $this->readTelemetryPayloads($response); diff --git a/tests/Integrations/PCNTL/PCNTLTest.php b/tests/Integrations/PCNTL/PCNTLTest.php index a10c94d465..bbebe09dd4 100644 --- a/tests/Integrations/PCNTL/PCNTLTest.php +++ b/tests/Integrations/PCNTL/PCNTLTest.php @@ -84,9 +84,12 @@ public function testCliShortRunningTracingWhenEnabled() __DIR__ . '/scripts/synthetic.php', [ 'DD_TRACE_CLI_ENABLED' => 'true', - 'DD_TRACE_SHUTDOWN_TIMEOUT' => 5000, 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', - ] + ], + [], + '', + false, + $this->untilNumberOfTraces(2) ); $this->assertCount(2, $requests); @@ -112,7 +115,6 @@ public function testAccessingTracerAfterForkIsUnproblematic() __DIR__ . '/scripts/access-tracer-after-fork.php', [ 'DD_TRACE_CLI_ENABLED' => 'true', - 'DD_TRACE_SHUTDOWN_TIMEOUT' => 5000, 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', 'DD_TRACE_DEBUG' => 'false', ], @@ -137,9 +139,12 @@ public function testCliShortRunningMainSpanAreGenerateBeforeAndAfter() __DIR__ . '/scripts/short-running.php', [ 'DD_TRACE_CLI_ENABLED' => 'true', - 'DD_TRACE_SHUTDOWN_TIMEOUT' => 5000, 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', - ] + ], + [], + '', + false, + $this->untilNumberOfTraces(2) ); $this->assertCount(2, $requests); @@ -165,9 +170,12 @@ public function testCliShortRunningMultipleForks() __DIR__ . '/scripts/short-running-multiple.php', [ 'DD_TRACE_CLI_ENABLED' => 'true', - 'DD_TRACE_SHUTDOWN_TIMEOUT' => 5000, 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', - ] + ], + [], + '', + false, + $this->untilNumberOfTraces(6) ); $this->assertCount(6, $requests); @@ -204,7 +212,6 @@ public function testCliShortRunningMultipleNestedForks() __DIR__ . '/scripts/short-running-multiple-nested.php', [ 'DD_TRACE_CLI_ENABLED' => 'true', - 'DD_TRACE_SHUTDOWN_TIMEOUT' => 5000, 'DD_TRACE_GENERATE_ROOT_SPAN' => 'true', ] ); @@ -238,32 +245,34 @@ public function testCliShortRunningMultipleNestedForks() public function testCliLongRunningMultipleForksAutoFlush() { - list($requests) = $this->inCli( + $this->inCli( __DIR__ . '/scripts/long-running-autoflush.php', [ 'DD_TRACE_CLI_ENABLED' => 'true', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'true', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', - 'DD_TRACE_AGENT_FLUSH_INTERVAL' => 0, - 'DD_TRACE_SHUTDOWN_TIMEOUT' => 5000, - ] - ); - $this->assertCount(4, $requests); - - for ($i = 0; $i < 4; $i += 2) { - $this->assertFlameGraph([$requests[$i]], [ - SpanAssertion::exists('curl_exec', '/httpbin_integration/ip'), - ]); - - $this->assertFlameGraph([$requests[$i + 1]], [ - SpanAssertion::exists('long_running_entry_point')->withChildren([ - SpanAssertion::exists('curl_exec', '/httpbin_integration/get'), - SpanAssertion::exists('curl_exec', '/httpbin_integration/headers'), - SpanAssertion::exists('curl_exec', '/httpbin_integration/user-agent'), + 'DD_TRACE_AGENT_FLUSH_INTERVAL' => 333, + ], + [], + '', + false, + $this->until( + $this->untilSpan(SpanAssertion::exists('curl_exec', '/httpbin_integration/child-0')), + $this->untilSpan(SpanAssertion::exists('curl_exec', '/httpbin_integration/child-1')), + $this->untilSpan(SpanAssertion::exists('long_running_entry_point')->withChildren([ + SpanAssertion::exists('curl_exec', '/httpbin_integration/entry_point-0'), + SpanAssertion::exists('curl_exec', '/httpbin_integration/main_process-0'), + SpanAssertion::exists('curl_exec', '/httpbin_integration/end_entry_point-0'), SpanAssertion::exists('pcntl_fork'), - ]), - ]); - } + ])), + $this->untilSpan(SpanAssertion::exists('long_running_entry_point')->withChildren([ + SpanAssertion::exists('curl_exec', '/httpbin_integration/entry_point-1'), + SpanAssertion::exists('curl_exec', '/httpbin_integration/main_process-1'), + SpanAssertion::exists('curl_exec', '/httpbin_integration/end_entry_point-1'), + SpanAssertion::exists('pcntl_fork'), + ])) + ) + ); } public function testCliLongRunningMultipleForksManualFlush() @@ -278,8 +287,11 @@ public function testCliLongRunningMultipleForksManualFlush() 'DD_TRACE_CLI_ENABLED' => 'true', 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'false', 'DD_TRACE_GENERATE_ROOT_SPAN' => 'false', - 'DD_TRACE_SHUTDOWN_TIMEOUT' => 5000, - ] + ], + [], + '', + false, + $this->untilNumberOfTraces(6) ); $this->assertCount(6, $requests); diff --git a/tests/Integrations/PCNTL/scripts/long-running-autoflush.php b/tests/Integrations/PCNTL/scripts/long-running-autoflush.php index b13c29bc90..876ff59dd9 100644 --- a/tests/Integrations/PCNTL/scripts/long-running-autoflush.php +++ b/tests/Integrations/PCNTL/scripts/long-running-autoflush.php @@ -10,26 +10,26 @@ }); for ($iteration = 0; $iteration < ITERATIONS; $iteration++) { - long_running_entry_point(); + long_running_entry_point($iteration); } -function long_running_entry_point() +function long_running_entry_point($iteration) { - call_httpbin('get'); + call_httpbin('entry_point-'.$iteration); $forkPid = pcntl_fork(); if ($forkPid > 0) { // Main - call_httpbin('headers'); + call_httpbin('main_process-'.$iteration); } else if ($forkPid === 0) { // Child - call_httpbin('ip'); + call_httpbin('child-'.$iteration); exit(0); } else { error_log('Error'); exit(-1); } - call_httpbin('user-agent'); + call_httpbin('end_entry_point-'.$iteration); pcntl_waitpid($forkPid, $childStatus); } diff --git a/tests/OpenTelemetry/Integration/InternalTelemetryTest.php b/tests/OpenTelemetry/Integration/InternalTelemetryTest.php index 4023745f02..9df322e544 100644 --- a/tests/OpenTelemetry/Integration/InternalTelemetryTest.php +++ b/tests/OpenTelemetry/Integration/InternalTelemetryTest.php @@ -56,11 +56,7 @@ public function testInternalMetricWithOpenTelemetry() $this->executeCommand(); - $requests = $this->retrieveDumpedData(function ($request) { - return (strpos($request["uri"] ?? "", "/telemetry/") === 0) - && (strpos($request["body"] ?? "", "spans_created") !== false) - ; - }, true); + $requests = $this->retrieveDumpedData($this->untilTelemetryRequest("spans_created")); $payloads = $this->readTelemetryPayloads($requests); $isMetric = function (array $payload) { diff --git a/tests/OpenTracing/InternalTelemetryTest.php b/tests/OpenTracing/InternalTelemetryTest.php index 2dc9f0143f..197c7fc320 100644 --- a/tests/OpenTracing/InternalTelemetryTest.php +++ b/tests/OpenTracing/InternalTelemetryTest.php @@ -37,11 +37,7 @@ public function testInternalMetricWithOpenTracing() $this->executeCommand(); - $requests = $this->retrieveDumpedData(function ($request) { - return (strpos($request["uri"] ?? "", "/telemetry/") === 0) - && (strpos($request["body"] ?? "", "spans_created") !== false) - ; - }, true); + $requests = $this->retrieveDumpedData($this->untilTelemetryRequest("spans_created")); $payloads = $this->readTelemetryPayloads($requests); $isMetric = function (array $payload) { From ddf15fee772bf1bddf9f3fc50dbc7aa8807243c6 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 12 Sep 2024 12:00:39 +0200 Subject: [PATCH 067/103] Both scenarios DeferredLoading and Predis were installing the same package in the same directory (#2846) --- Makefile | 2 +- tests/Integrations/DeferredLoading/composer.json | 5 +++++ tests/Integrations/DeferredLoading/index.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/Integrations/DeferredLoading/composer.json diff --git a/Makefile b/Makefile index 08898e7d30..94ca7022a2 100644 --- a/Makefile +++ b/Makefile @@ -1154,7 +1154,7 @@ test_integrations_amqp2: global_test_run_dependencies tests/Integrations/AMQP/V2 $(call run_tests_debug,tests/Integrations/AMQP/V2) test_integrations_amqp35: global_test_run_dependencies tests/Integrations/AMQP/V3_5/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/AMQP/V3_5) -test_integrations_deferred_loading: global_test_run_dependencies tests/Integrations/Predis/composer.lock-php$(PHP_MAJOR_MINOR) +test_integrations_deferred_loading: global_test_run_dependencies tests/Integrations/DeferredLoading/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests_debug,tests/Integrations/DeferredLoading) test_integrations_curl: global_test_run_dependencies $(call run_tests_debug,tests/Integrations/Curl) diff --git a/tests/Integrations/DeferredLoading/composer.json b/tests/Integrations/DeferredLoading/composer.json new file mode 100644 index 0000000000..5b58b88b4d --- /dev/null +++ b/tests/Integrations/DeferredLoading/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "predis/predis": "^1.1" + } +} diff --git a/tests/Integrations/DeferredLoading/index.php b/tests/Integrations/DeferredLoading/index.php index 283e911a18..11767cb556 100644 --- a/tests/Integrations/DeferredLoading/index.php +++ b/tests/Integrations/DeferredLoading/index.php @@ -1,6 +1,6 @@ Date: Thu, 12 Sep 2024 16:26:54 +0200 Subject: [PATCH 068/103] Fix a race condition where curl_multi_exec is called many times while finished ($still_running at false) (#2847) before curl_multi_info_read. In that case, the spans could have been removed from the storage --- .../Integrations/Curl/CurlIntegration.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/DDTrace/Integrations/Curl/CurlIntegration.php b/src/DDTrace/Integrations/Curl/CurlIntegration.php index 623c41f1d1..61304f8d29 100644 --- a/src/DDTrace/Integrations/Curl/CurlIntegration.php +++ b/src/DDTrace/Integrations/Curl/CurlIntegration.php @@ -121,12 +121,15 @@ public function init(): int list($ch, $requestSpan) = $requestSpan; $requestSpan->metrics["_dd.measured"] = 1; $info = curl_getinfo($ch); - if (isset($requestSpan->meta['network.destination.name']) && $requestSpan->meta['network.destination.name'] !== 'unparsable-host') { + if (empty($info["http_code"])) { + $saveSpans = true; + } + + if (isset($requestSpan->meta[Tag::NETWORK_DESTINATION_NAME]) && $requestSpan->meta[Tag::NETWORK_DESTINATION_NAME] !== 'unparsable-host') { continue; } if (empty($info["http_code"])) { - $saveSpans = true; if (!isset($error_trace)) { $error_trace = \DDTrace\get_sanitized_exception_trace(new \Exception(), 1); } @@ -183,16 +186,15 @@ public function init(): int } list(, $spans) = $data; + if (empty($spans)) { + return; + } if (!isset($hook->returned["result"]) || $hook->returned["result"] == CURLE_OK) { - if (empty($spans)) { - return; - } - foreach ($spans as $requestSpan) { list($ch, $requestSpan) = $requestSpan; if ($ch === $handle) { - if (isset($requestSpan->meta['network.destination.name']) && $requestSpan->meta['network.destination.name'] !== 'unparsable-host') { + if (isset($requestSpan->meta[Tag::NETWORK_DESTINATION_NAME]) && $requestSpan->meta[Tag::NETWORK_DESTINATION_NAME] !== 'unparsable-host') { continue; } $info = curl_getinfo($ch); From 31382ec7e2f822cf156883b11e249f4bfd4db72e Mon Sep 17 00:00:00 2001 From: Alejandro Estringana Ruiz Date: Mon, 16 Sep 2024 12:38:14 +0200 Subject: [PATCH 069/103] Fix benchmarks (#2852) --- benchmark/runall.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/runall.sh b/benchmark/runall.sh index 6045fcedb6..0f2eeff874 100755 --- a/benchmark/runall.sh +++ b/benchmark/runall.sh @@ -22,13 +22,13 @@ elif [ "$SCENARIO" = "tracer" ]; then ## Non-OPCache Benchmarks make benchmarks - cp tests/Benchmarks/reports/tracer-bench-results.csv "$ARTIFACTS_DIR" + cp tests/Benchmarks/reports/tracer-bench-results.csv "$ARTIFACTS_DIR/tracer-bench-results.csv" ## OPCache Benchmarks make benchmarks_opcache - cp tests/Benchmarks/reports/tracer-bench-results-opcache.csv "$ARTIFACTS_DIR" + cp tests/Benchmarks/reports/tracer-bench-results-opcache.csv "$ARTIFACTS_DIR/tracer-bench-results-opcache.csv" ## Request Startup/Shutdown Benchmarks make benchmarks_tea - cp tea/benchmarks/reports/tea-bench-results.json "$ARTIFACTS_DIR" + cp tea/benchmarks/reports/tea-bench-results.json "$ARTIFACTS_DIR/tracer-tea-bench-results.json" fi From 9b5944c29bc0a4bd450f5f484b076fa4d843edc8 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 16 Sep 2024 15:09:22 +0100 Subject: [PATCH 070/103] Fix improper symbol exports in appsec ext/helper Both the extension and the helper need version scripts to prevent symbols from being exported. -fvisibility=hidden is not sufficient because it's not being applied to some of the static libraries they link against. On the extensions side: the path to the version script was wrong, as this error was ultimately ignored because invalid ld options were being ingored. This resulted in some extra zai_* functions being exported. On the helper side. Symbols from libunwind, libc++ and possibly others were being exported. --- appsec/cmake/cond_flag.cmake | 14 ++++++++++++++ appsec/cmake/extension.cmake | 17 +++-------------- appsec/cmake/helper.cmake | 3 +++ appsec/src/helper/helper.version | 7 +++++++ 4 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 appsec/cmake/cond_flag.cmake create mode 100644 appsec/src/helper/helper.version diff --git a/appsec/cmake/cond_flag.cmake b/appsec/cmake/cond_flag.cmake new file mode 100644 index 0000000000..1dfc82e3e5 --- /dev/null +++ b/appsec/cmake/cond_flag.cmake @@ -0,0 +1,14 @@ +macro(target_linker_flag_conditional target) # flags as argv + try_compile(LINKER_HAS_FLAG "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/check.c" + LINK_OPTIONS ${ARGN} + OUTPUT_VARIABLE LINKER_HAS_FLAG_ERROR_LOG) + + if(LINKER_HAS_FLAG) + target_link_options(${target} PRIVATE ${ARGN}) + message(STATUS "Linker has flag ${ARGN}") + else() + #message(STATUS "Linker does not have flag: ${LINKER_HAS_FLAG_ERROR_LOG}") + endif() +endmacro() + + diff --git a/appsec/cmake/extension.cmake b/appsec/cmake/extension.cmake index 6739e9d28c..f42c835bee 100644 --- a/appsec/cmake/extension.cmake +++ b/appsec/cmake/extension.cmake @@ -26,19 +26,6 @@ target_compile_definitions(extension PRIVATE TESTING=1 ZEND_ENABLE_STATIC_TSRMLS target_link_libraries(extension PRIVATE mpack PhpConfig zai) target_include_directories(extension PRIVATE ..) -macro(target_linker_flag_conditional target) # flags as argv - try_compile(LINKER_HAS_FLAG "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/check.c" - LINK_OPTIONS ${ARGN} - OUTPUT_VARIABLE LINKER_HAS_FLAG_ERROR_LOG) - - if(LINKER_HAS_FLAG) - target_link_options(${target} PRIVATE ${ARGN}) - message(STATUS "Linker has flag ${ARGN}") - else() - #message(STATUS "Linker does not have flag: ${LINKER_HAS_FLAG_ERROR_LOG}") - endif() -endmacro() - # we don't have any C++ now, but just so we don't forget in the future... check_cxx_compiler_flag("-fno-gnu-unique" COMPILER_HAS_NO_GNU_UNIQUE) if(COMPILER_HAS_NO_GNU_UNIQUE) @@ -49,11 +36,13 @@ target_compile_options(extension PRIVATE -Wall -Wextra -Werror) # our thread local variables are only used by ourselves target_compile_options(extension PRIVATE -ftls-model=local-dynamic) +include(cmake/cond_flag.cmake) + target_linker_flag_conditional(extension -Wl,--as-needed) # ld doesn't necessarily respect the visibility of hidden symbols if # they're inside static libraries, so use a linker script only exporting # ddappsec.version as a safeguard -target_linker_flag_conditional(extension "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/ddappsec.version") +target_linker_flag_conditional(extension "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/extension/ddappsec.version") # Mac OS target_linker_flag_conditional(extension -flat_namespace "-undefined suppress") diff --git a/appsec/cmake/helper.cmake b/appsec/cmake/helper.cmake index cb6a7d3c02..7846afb5b6 100644 --- a/appsec/cmake/helper.cmake +++ b/appsec/cmake/helper.cmake @@ -45,6 +45,9 @@ set_target_properties(ddappsec-helper PROPERTIES SUFFIX .so ) +include(cmake/cond_flag.cmake) +target_linker_flag_conditional(ddappsec-helper "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/helper/helper.version") + patch_away_libc(ddappsec-helper) try_compile(STDLIBXX_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR} diff --git a/appsec/src/helper/helper.version b/appsec/src/helper/helper.version new file mode 100644 index 0000000000..3c9f9a284d --- /dev/null +++ b/appsec/src/helper/helper.version @@ -0,0 +1,7 @@ +{ + global: + appsec_helper_main; + appsec_helper_shutdown; + local: *; +}; + From 7f4e7791283cc1ff901c0da57a0319d5a0c5fb08 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 16 Sep 2024 17:02:31 +0100 Subject: [PATCH 071/103] add missing export in appsec extension --- appsec/src/extension/ddappsec.version | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appsec/src/extension/ddappsec.version b/appsec/src/extension/ddappsec.version index f90d3623ed..eb4dab81b2 100644 --- a/appsec/src/extension/ddappsec.version +++ b/appsec/src/extension/ddappsec.version @@ -1,4 +1,6 @@ { - global: get_module; + global: + get_module; + dd_appsec_maybe_enable_helper; local: *; }; From dafdd41f94683db9895e566e545812cb960bbd5b Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 16 Sep 2024 17:42:01 +0100 Subject: [PATCH 072/103] Remove gid from socket/lock path of helper Now that we're inside sidecar, we need to have the same uniqueness characteristics of sidecar; in particular, there's one instance of sidecar per uid, but not per (uid,gid) as was the case with the helper before. --- appsec/src/extension/helper_process.c | 11 +++++------ appsec/tests/extension/inc/mock_helper.php | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/appsec/src/extension/helper_process.c b/appsec/src/extension/helper_process.c index d599b23a96..8d0e166723 100644 --- a/appsec/src/extension/helper_process.c +++ b/appsec/src/extension/helper_process.c @@ -42,7 +42,7 @@ static const int timeout_send = 500; static const int timeout_recv_initial = 7500; static const int timeout_recv_subseq = 2000; -#define DD_PATH_FORMAT "%s%sddappsec_" PHP_DDAPPSEC_VERSION "_%u.%u" +#define DD_PATH_FORMAT "%s%sddappsec_" PHP_DDAPPSEC_VERSION "_%u" #define DD_SOCK_PATH_FORMAT DD_PATH_FORMAT ".sock" #define DD_LOCK_PATH_FORMAT DD_PATH_FORMAT ".lock" @@ -135,24 +135,23 @@ bool dd_on_runtime_path_update(zval *nullable old_val, zval *nonnull new_val) UNUSED(old_val); uid_t uid = getuid(); - gid_t gid = getgid(); char *base = Z_STRVAL_P(new_val); size_t base_len = Z_STRLEN_P(new_val); char *separator = base[base_len - 1] != '/' ? "/" : ""; size_t sock_name_len = - snprintf(NULL, 0, DD_SOCK_PATH_FORMAT, base, separator, uid, gid); + snprintf(NULL, 0, DD_SOCK_PATH_FORMAT, base, separator, uid); char *sock_name = safe_pemalloc(sock_name_len, sizeof(char), 1, 1); snprintf(sock_name, sock_name_len + 1, DD_SOCK_PATH_FORMAT, base, separator, - uid, gid); + uid); pefree(_mgr.socket_path, 1); _mgr.socket_path = sock_name; size_t lock_name_len = - snprintf(NULL, 0, DD_LOCK_PATH_FORMAT, base, separator, uid, gid); + snprintf(NULL, 0, DD_LOCK_PATH_FORMAT, base, separator, uid); char *lock_name = safe_pemalloc(lock_name_len, sizeof(char), 1, 1); snprintf(lock_name, lock_name_len + 1, DD_LOCK_PATH_FORMAT, base, separator, - uid, gid); + uid); pefree(_mgr.lock_path, 1); _mgr.lock_path = lock_name; diff --git a/appsec/tests/extension/inc/mock_helper.php b/appsec/tests/extension/inc/mock_helper.php index e25ea575b7..7a9a75594a 100644 --- a/appsec/tests/extension/inc/mock_helper.php +++ b/appsec/tests/extension/inc/mock_helper.php @@ -13,7 +13,7 @@ class Helper { function __construct($opts = array()) { $runtime_path = key_exists('runtime_path', $opts) ? $opts['runtime_path'] : ini_get('datadog.appsec.helper_runtime_path'); - $sock_path = $runtime_path . "/ddappsec_" . phpversion('ddappsec') . "_" . getmyuid() . "." . getmygid() . ".sock"; + $sock_path = $runtime_path . "/ddappsec_" . phpversion('ddappsec') . "_" . getmyuid() . ".sock"; $received_pipe = key_exists('received_pipe', $opts) ? $opts['received_pipe'] : true; $this->mock_helper_path = key_exists('mock_helper_path', $opts) ? $opts['mock_helper_path'] : getenv('MOCK_HELPER_BINARY'); $this->lock_path = $runtime_path . "/ddappsec_" . phpversion('ddappsec') . ".lock"; From 4f522183c2831da52393f0830a9cc8474bc27199 Mon Sep 17 00:00:00 2001 From: William Conti Date: Mon, 16 Sep 2024 15:28:15 -0400 Subject: [PATCH 073/103] add new scenarios and aws auth --- .circleci/continue_config.yml | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index a3dcce6447..b3d1334105 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3881,7 +3881,7 @@ jobs: docker-compose --version - run: name: Clone System Tests repo - command: git clone https://github.com/DataDog/system-tests.git + command: git clone https://github.com/DataDog/system-tests.git && git checkout conti/fix-php-errors - fetch_job_artifact: job: "package extension" @@ -4859,6 +4859,36 @@ workflows: run: | cd system-tests DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh + + - system_tests: + requires: [ 'package extension' ] + name: "System tests Integration Tests" + build: | + cd system-tests + ./build.sh php + environment: + - AWS_ACCESS_KEY_ID: $SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY: $SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY + - AWS_REGION: us-east-1 + - AWS_DEFAULT_REGION: us-east-1 # AWS services should use `AWS_REGION`, but some still use the older `AWS_DEFAULT_REGION` + run: | + cd system-tests + DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh + + - system_tests: + requires: [ 'package extension' ] + name: "System tests Crossed Tracer Propagation Tests for Messaging" + build: | + cd system-tests + ./build.sh php + environment: + - AWS_ACCESS_KEY_ID: $SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY: $SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY + - AWS_REGION: us-east-1 + - AWS_DEFAULT_REGION: us-east-1 # AWS services should use `AWS_REGION`, but some still use the older `AWS_DEFAULT_REGION` + run: | + cd system-tests + DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh - system_tests: requires: [ 'package extension' ] From 5cb04b3100fefa647901f9992fbc43858fc1296b Mon Sep 17 00:00:00 2001 From: William Conti Date: Mon, 16 Sep 2024 15:33:39 -0400 Subject: [PATCH 074/103] fix commands --- .circleci/continue_config.yml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index b3d1334105..b3a045e0d0 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -4866,27 +4866,25 @@ workflows: build: | cd system-tests ./build.sh php - environment: - - AWS_ACCESS_KEY_ID: $SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID - - AWS_SECRET_ACCESS_KEY: $SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY - - AWS_REGION: us-east-1 - - AWS_DEFAULT_REGION: us-east-1 # AWS services should use `AWS_REGION`, but some still use the older `AWS_DEFAULT_REGION` run: | + export AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID + export AWS_SECRET_ACCESS_KEY=$SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY + export AWS_REGION=us-east-1 + export AWS_DEFAULT_REGION=us-east-1 cd system-tests DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh - + - system_tests: requires: [ 'package extension' ] name: "System tests Crossed Tracer Propagation Tests for Messaging" build: | cd system-tests ./build.sh php - environment: - - AWS_ACCESS_KEY_ID: $SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID - - AWS_SECRET_ACCESS_KEY: $SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY - - AWS_REGION: us-east-1 - - AWS_DEFAULT_REGION: us-east-1 # AWS services should use `AWS_REGION`, but some still use the older `AWS_DEFAULT_REGION` run: | + export AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID + export AWS_SECRET_ACCESS_KEY=$SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY + export AWS_REGION=us-east-1 + export AWS_DEFAULT_REGION=us-east-1 cd system-tests DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh From d44d91227f9c2e455ba3dc0ddde86aee624d224a Mon Sep 17 00:00:00 2001 From: William Conti Date: Mon, 16 Sep 2024 15:36:07 -0400 Subject: [PATCH 075/103] update config --- .circleci/continue_config.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index b3a045e0d0..2cc11cc58e 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3881,7 +3881,7 @@ jobs: docker-compose --version - run: name: Clone System Tests repo - command: git clone https://github.com/DataDog/system-tests.git && git checkout conti/fix-php-errors + command: git clone https://github.com/DataDog/system-tests.git - fetch_job_artifact: job: "package extension" @@ -4865,6 +4865,7 @@ workflows: name: "System tests Integration Tests" build: | cd system-tests + git checkout conti/fix-php-errors ./build.sh php run: | export AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID @@ -4872,6 +4873,7 @@ workflows: export AWS_REGION=us-east-1 export AWS_DEFAULT_REGION=us-east-1 cd system-tests + git checkout conti/fix-php-errors DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh - system_tests: @@ -4879,6 +4881,7 @@ workflows: name: "System tests Crossed Tracer Propagation Tests for Messaging" build: | cd system-tests + git checkout conti/fix-php-errors ./build.sh php run: | export AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID @@ -4886,6 +4889,7 @@ workflows: export AWS_REGION=us-east-1 export AWS_DEFAULT_REGION=us-east-1 cd system-tests + git checkout conti/fix-php-errors DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh - system_tests: From 1b68009f02cebf22f6e71323068dd3046f9510b7 Mon Sep 17 00:00:00 2001 From: William Conti Date: Mon, 16 Sep 2024 15:42:02 -0400 Subject: [PATCH 076/103] another fix --- .circleci/continue_config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 2cc11cc58e..3ead1b8743 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -4874,7 +4874,7 @@ workflows: export AWS_DEFAULT_REGION=us-east-1 cd system-tests git checkout conti/fix-php-errors - DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh + DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh INTEGRATIONS - system_tests: requires: [ 'package extension' ] @@ -4890,7 +4890,7 @@ workflows: export AWS_DEFAULT_REGION=us-east-1 cd system-tests git checkout conti/fix-php-errors - DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh + DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh CROSSED_TRACING_LIBRARIES - system_tests: requires: [ 'package extension' ] From 17b3c7a33d21154310c2fc43a5b1db198abb554f Mon Sep 17 00:00:00 2001 From: Alexandre Choura <42672104+PROFeNoM@users.noreply.github.com> Date: Tue, 17 Sep 2024 08:20:54 +0200 Subject: [PATCH 077/103] fix(otel): Missing `addLink` method and Fiber handling (#2849) * fix(otel): Missing method and Fiber handling * tests: `test_opentelemetry_{1|beta}` * tests: Properly set composer to 1.0.* * fix: remove `cleanup_opentelemetry` * fix: php 7.4 unpacking issue * tests: skip fiber test on coverage * style: remove useless require * remove comment * perf: condition check * perf: Class lookup optimization Co-authored-by: Bob Weinand --------- Co-authored-by: Bob Weinand --- Makefile | 22 ++++- src/DDTrace/OpenTelemetry/Context.php | 12 ++- src/DDTrace/OpenTelemetry/Span.php | 93 +++++++++++++------ .../Context/{ => Fiber}/FiberTest.php | 74 +-------------- .../test_context_switching_ffi_observer.phpt | 46 +++++++++ ...ng_ffi_observer_registered_on_startup.phpt | 45 +++++++++ .../Integration/SDK/SpanBuilderTest.php | 16 ++++ tests/OpenTelemetry/composer-1.json | 9 ++ tests/OpenTelemetry/composer-beta.json | 9 ++ tests/OpenTelemetry/composer.json | 8 -- tests/phpunit.xml | 1 + 11 files changed, 223 insertions(+), 112 deletions(-) rename tests/OpenTelemetry/Integration/Context/{ => Fiber}/FiberTest.php (75%) create mode 100644 tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer.phpt create mode 100644 tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer_registered_on_startup.phpt create mode 100644 tests/OpenTelemetry/composer-1.json create mode 100644 tests/OpenTelemetry/composer-beta.json delete mode 100644 tests/OpenTelemetry/composer.json diff --git a/Makefile b/Makefile index 94ca7022a2..ef9dfec03f 100644 --- a/Makefile +++ b/Makefile @@ -846,6 +846,7 @@ TEST_INTEGRATIONS_81 := \ test_integrations_mysqli \ test_integrations_openai \ test_opentelemetry_1 \ + test_opentelemetry_beta \ test_integrations_guzzle7 \ test_integrations_pcntl \ test_integrations_pdo \ @@ -898,6 +899,7 @@ TEST_INTEGRATIONS_82 := \ test_integrations_mysqli \ test_integrations_openai \ test_opentelemetry_1 \ + test_opentelemetry_beta \ test_integrations_guzzle7 \ test_integrations_pcntl \ test_integrations_pdo \ @@ -958,6 +960,7 @@ TEST_INTEGRATIONS_83 := \ test_integrations_mysqli \ test_integrations_openai \ test_opentelemetry_1 \ + test_opentelemetry_beta \ test_integrations_guzzle7 \ test_integrations_pcntl \ test_integrations_pdo \ @@ -1133,10 +1136,27 @@ benchmarks: benchmarks_run_dependencies call_benchmarks benchmarks_opcache: benchmarks_run_dependencies call_benchmarks_opcache -test_opentelemetry_1: global_test_run_dependencies tests/Frameworks/Custom/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) tests/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) +define setup_opentelemetry + cp $(1) $(dir $(1))/composer.json +endef + +define run_opentelemetry_tests $(eval TEST_EXTRA_ENV=$(shell [ $(PHP_MAJOR_MINOR) -ge 81 ] && echo "OTEL_PHP_FIBERS_ENABLED=1" || echo '') DD_TRACE_OTEL_ENABLED=1 DD_TRACE_GENERATE_ROOT_SPAN=0) $(call run_tests,--testsuite=opentelemetry1 $(TESTS)) $(eval TEST_EXTRA_ENV=) +endef + +_test_opentelemetry_beta_setup: global_test_run_dependencies + $(call setup_opentelemetry,tests/OpenTelemetry/composer-beta.json) + +test_opentelemetry_beta: _test_opentelemetry_beta_setup tests/Frameworks/Custom/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) tests/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) + $(call run_opentelemetry_tests) + +_test_opentelemetry_1_setup: global_test_run_dependencies + $(call setup_opentelemetry,tests/OpenTelemetry/composer-1.json) + +test_opentelemetry_1: _test_opentelemetry_1_setup tests/Frameworks/Custom/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) tests/OpenTelemetry/composer.lock-php$(PHP_MAJOR_MINOR) + $(call run_opentelemetry_tests) test_opentracing_10: global_test_run_dependencies tests/OpenTracer1Unit/composer.lock-php$(PHP_MAJOR_MINOR) tests/Frameworks/Custom/OpenTracing/composer.lock-php$(PHP_MAJOR_MINOR) $(call run_tests,tests/OpenTracer1Unit) diff --git a/src/DDTrace/OpenTelemetry/Context.php b/src/DDTrace/OpenTelemetry/Context.php index 44910695e9..a05f6b1445 100644 --- a/src/DDTrace/OpenTelemetry/Context.php +++ b/src/DDTrace/OpenTelemetry/Context.php @@ -27,6 +27,9 @@ final class Context implements ContextInterface /** @var ContextStorageInterface&ExecutionContextAwareInterface */ private static ContextStorageInterface $storage; + /** @var string $storageClass */ + private static string $storageClass = ''; + // Optimization for spans to avoid copying the context array. private static ContextKeyInterface $spanContextKey; private ?object $span = null; @@ -58,8 +61,13 @@ public static function setStorage(ContextStorageInterface $storage): void */ public static function storage(): ContextStorageInterface { - /** @psalm-suppress RedundantPropertyInitializationCheck */ - return self::$storage ??= new ContextStorage(); + if (self::$storageClass === '') { + self::$storageClass = class_exists('OpenTelemetry\Context\FiberBoundContextStorageExecutionAwareBC') + ? 'OpenTelemetry\Context\FiberBoundContextStorageExecutionAwareBC' // v1.1+ + : 'OpenTelemetry\Context\ContextStorage'; + } + + return self::$storage ??= new self::$storageClass(); } /** diff --git a/src/DDTrace/OpenTelemetry/Span.php b/src/DDTrace/OpenTelemetry/Span.php index 52779a8775..c6aa4ae95e 100644 --- a/src/DDTrace/OpenTelemetry/Span.php +++ b/src/DDTrace/OpenTelemetry/Span.php @@ -112,28 +112,18 @@ private function __construct( foreach ($links as $link) { /** @var LinkInterface $link */ $linkContext = $link->getSpanContext(); - - $spanLink = new SpanLink(); - $spanLink->traceId = $linkContext->getTraceId(); - $spanLink->spanId = $linkContext->getSpanId(); - $spanLink->traceState = (string)$linkContext->getTraceState(); // __toString() - $spanLink->attributes = $link->getAttributes()->toArray(); - $spanLink->droppedAttributesCount = 0; // Attributes limit aren't supported/meaningful in DD - - // Save the link - ObjectKVStore::put($spanLink, "link", $link); - $span->links[] = $spanLink; + $span->links[] = $this->createAndSaveSpanLink($linkContext, $link->getAttributes()->toArray(), $link); } foreach ($events as $event) { /** @var EventInterface $event */ $spanEvent = new SpanEvent( - $event->getName(), + $event->getName(), $event->getAttributes()->toArray(), $event->getEpochNanos() ); - + // Save the event ObjectKVStore::put($spanEvent, "event", $event); $span->events[] = $spanEvent; @@ -235,17 +225,33 @@ public function toSpanData(): SpanDataInterface $this->updateSpanLinks(); $this->updateSpanEvents(); - return new ImmutableSpan( - $this, - $this->getName(), - $this->links, - $this->events, - Attributes::create(array_merge($this->span->meta, $this->span->metrics)), - 0, - StatusData::create($this->status->getCode(), $this->status->getDescription()), - $hasEnded ? $this->span->getStartTime() + $this->span->getDuration() : 0, - $this->hasEnded() - ); + if (method_exists(SpanInterface::class, 'addLink')) { + // v1.1 backward compatibility: totalRecordedLinks parameter added + return new ImmutableSpan( + $this, + $this->getName(), + $this->links, + $this->events, + Attributes::create(array_merge($this->span->meta, $this->span->metrics)), + $this->totalRecordedEvents, + $this->totalRecordedLinks, + StatusData::create($this->status->getCode(), $this->status->getDescription()), + $hasEnded ? $this->span->getStartTime() + $this->span->getDuration() : 0, + $this->hasEnded(), + ); + } else { + return new ImmutableSpan( + $this, + $this->getName(), + $this->links, + $this->events, + Attributes::create(array_merge($this->span->meta, $this->span->metrics)), + $this->totalRecordedEvents, + StatusData::create($this->status->getCode(), $this->status->getDescription()), + $hasEnded ? $this->span->getStartTime() + $this->span->getDuration() : 0, + $this->hasEnded(), + ); + } } /** @@ -358,6 +364,19 @@ public function setAttributes(iterable $attributes): SpanInterface return $this; } + /** + * @inheritDoc + */ + public function addLink(SpanContextInterface $context, iterable $attributes = []): SpanInterface + { + if ($this->hasEnded() || !$context->isValid()) { + return $this; + } + + $this->span->links[] = $this->createAndSaveSpanLink($context, $attributes); + return $this; + } + /** * @inheritDoc */ @@ -365,7 +384,7 @@ public function addEvent(string $name, iterable $attributes = [], int $timestamp { if (!$this->hasEnded()) { $this->span->events[] = new SpanEvent( - $name, + $name, $attributes, $timestamp ?? (int)(microtime(true) * 1e9) ); @@ -522,7 +541,7 @@ private function updateSpanEvents() { $datadogSpanEvents = $this->span->events; $this->span->meta["events"] = count($this->events); - + $otel = []; foreach ($datadogSpanEvents as $datadogSpanEvent) { $exceptionAttributes = []; @@ -539,7 +558,7 @@ private function updateSpanEvents() $event = new Event( $datadogSpanEvent->name, (int)$datadogSpanEvent->timestamp, - Attributes::create(array_merge($exceptionAttributes, \is_array($datadogSpanEvent->attributes) ? $datadogSpanEvent->attributes : iterator_to_array($datadogSpanEvent->attributes))) + Attributes::create(array_merge($exceptionAttributes, \is_array($datadogSpanEvent->attributes) ? $datadogSpanEvent->attributes : iterator_to_array($datadogSpanEvent->attributes))) ); // Save the event @@ -547,9 +566,27 @@ private function updateSpanEvents() } $otel[] = $event; } - + // Update the events $this->events = $otel; $this->totalRecordedEvents = count($otel); } + + private function createAndSaveSpanLink(SpanContextInterface $context, iterable $attributes = [], LinkInterface $link = null) + { + $spanLink = new SpanLink(); + $spanLink->traceId = $context->getTraceId(); + $spanLink->spanId = $context->getSpanId(); + $spanLink->traceState = (string)$context->getTraceState(); // __toString() + $spanLink->attributes = $attributes; + $spanLink->droppedAttributesCount = 0; // Attributes limit aren't supported/meaningful in DD + + // Save the link + if (is_null($link)) { + $link = new Link($context, Attributes::create($attributes)); + } + ObjectKVStore::put($spanLink, "link", $link); + + return $spanLink; + } } diff --git a/tests/OpenTelemetry/Integration/Context/FiberTest.php b/tests/OpenTelemetry/Integration/Context/Fiber/FiberTest.php similarity index 75% rename from tests/OpenTelemetry/Integration/Context/FiberTest.php rename to tests/OpenTelemetry/Integration/Context/Fiber/FiberTest.php index 55697a32fb..cda6c178a4 100644 --- a/tests/OpenTelemetry/Integration/Context/FiberTest.php +++ b/tests/OpenTelemetry/Integration/Context/Fiber/FiberTest.php @@ -13,7 +13,7 @@ use OpenTelemetry\API\Trace\Span; use OpenTelemetry\API\Trace\SpanKind; use OpenTelemetry\Context\Context; -use OpenTelemetry\Context\ContextStorage; +use OpenTelemetry\Context\ExecutionContextAwareInterface; use OpenTelemetry\SDK\Trace\TracerProvider; use function DDTrace\close_span; use function DDTrace\start_span; @@ -31,78 +31,6 @@ public function ddSetUp(): void public function ddTearDown() { parent::ddTearDown(); - Context::setStorage(new ContextStorage()); // Reset OpenTelemetry context - } - - /** - * @throws \Throwable - */ - public function test_context_switching_ffi_observer() - { - $key = Context::createKey('-'); - $scope = Context::getCurrent() - ->with($key, 'main') - ->activate(); - - $fiber = new Fiber(function () use ($key) { - $scope = Context::getCurrent() - ->with($key, 'fiber') - ->activate(); - - $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key)); - - Fiber::suspend(); - - $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key)); - - $scope->detach(); - }); - - $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key)); - - $fiber->start(); - - $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key)); - - $fiber->resume(); - - $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key)); - - $scope->detach(); - } - - public function test_context_switching_ffi_observer_registered_on_startup() - { - $key = Context::createKey('-'); - - $fiber = new Fiber(function () use ($key) { - $scope = Context::getCurrent() - ->with($key, 'fiber') - ->activate(); - - $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key)); - - Fiber::suspend(); - - $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key)); - - $scope->detach(); - }); - - - $fiber->start(); - - $this->assertSame('main:', 'main:' . Context::getCurrent()->get($key)); - - $scope = Context::getCurrent() - ->with($key, 'main') - ->activate(); - - $fiber->resume(); - - $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key)); - - $scope->detach(); } public function testFiberInteroperabilityStackSwitch() diff --git a/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer.phpt b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer.phpt new file mode 100644 index 0000000000..7caf00f006 --- /dev/null +++ b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer.phpt @@ -0,0 +1,46 @@ +--TEST-- +Context switches on execution context switch. +--SKIPIF-- + +--ENV-- +OTEL_PHP_FIBERS_ENABLED=1 +--FILE-- +with($key, 'main') + ->activate(); + +$fiber = new Fiber(function() use ($key) { + $scope = Context::getCurrent() + ->with($key, 'fiber') + ->activate(); + + echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL; + + Fiber::suspend(); + echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL; + + $scope->detach(); +}); + +echo 'main:' . Context::getCurrent()->get($key), PHP_EOL; + +$fiber->start(); +echo 'main:' . Context::getCurrent()->get($key), PHP_EOL; + +$fiber->resume(); +echo 'main:' . Context::getCurrent()->get($key), PHP_EOL; + +$scope->detach(); +?> +--EXPECT-- +main:main +fiber:fiber +main:main +fiber:fiber +main:main diff --git a/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer_registered_on_startup.phpt b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer_registered_on_startup.phpt new file mode 100644 index 0000000000..4de8eb1db9 --- /dev/null +++ b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer_registered_on_startup.phpt @@ -0,0 +1,45 @@ +--TEST-- +Fiber handler has to be loaded before fibers are used. +--SKIPIF-- + +--ENV-- +OTEL_PHP_FIBERS_ENABLED=1 +--FILE-- +with($key, 'fiber') + ->activate(); + + echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL; + + Fiber::suspend(); + echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL; + + $scope->detach(); +}); + +$fiber->start(); +echo 'main:' . Context::getCurrent()->get($key), PHP_EOL; + +$scope = Context::getCurrent() + ->with($key, 'main') + ->activate(); + +$fiber->resume(); +echo 'main:' . Context::getCurrent()->get($key), PHP_EOL; + +$scope->detach(); + +?> +--EXPECT-- +fiber:fiber +main: +fiber:fiber +main:main diff --git a/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php b/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php index b849ce7e45..5d8a7406b7 100644 --- a/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php +++ b/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php @@ -109,6 +109,22 @@ public function test_add_link(): void $this->assertCount(2, $span->toSpanData()->getLinks()); } + /** + * @group trace-compliance + */ + public function test_add_link_after_span_creation(): void + { + /** @var Span $span */ + $span = $this + ->tracer + ->spanBuilder(self::SPAN_NAME) + ->addLink($this->sampledSpanContext) + ->startSpan() + ->addLink($this->sampledSpanContext); + + $this->assertCount(2, $span->toSpanData()->getLinks()); + } + public function test_add_link_invalid(): void { /** @var Span $span */ diff --git a/tests/OpenTelemetry/composer-1.json b/tests/OpenTelemetry/composer-1.json new file mode 100644 index 0000000000..3cd307c3b1 --- /dev/null +++ b/tests/OpenTelemetry/composer-1.json @@ -0,0 +1,9 @@ +{ + "name": "datadog/dd-trace-tests", + "require": { + "open-telemetry/sdk": "1.0.*", + "open-telemetry/extension-propagator-b3": "1.0.*", + "open-telemetry/opentelemetry-logger-monolog": "1.0.*" + }, + "minimum-stability": "stable" +} diff --git a/tests/OpenTelemetry/composer-beta.json b/tests/OpenTelemetry/composer-beta.json new file mode 100644 index 0000000000..17891594b8 --- /dev/null +++ b/tests/OpenTelemetry/composer-beta.json @@ -0,0 +1,9 @@ +{ + "name": "datadog/dd-trace-tests", + "require": { + "open-telemetry/sdk": "@beta", + "open-telemetry/extension-propagator-b3": "@beta", + "open-telemetry/opentelemetry-logger-monolog": "@beta" + }, + "minimum-stability": "beta" +} diff --git a/tests/OpenTelemetry/composer.json b/tests/OpenTelemetry/composer.json deleted file mode 100644 index 607a2888d4..0000000000 --- a/tests/OpenTelemetry/composer.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "datadog/dd-trace-tests", - "require": { - "open-telemetry/sdk": "@stable", - "open-telemetry/extension-propagator-b3": "@stable", - "open-telemetry/opentelemetry-logger-monolog": "@stable" - } -} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index fd3902de3a..4e36afbfc6 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -74,6 +74,7 @@ ./Integrations/Laravel/Octane + ./OpenTelemetry/Integration/Context/Fiber ./OpenTelemetry/Unit/API ./OpenTelemetry/Unit/Context ./OpenTelemetry/Unit/Propagation From 6fea9c9bc135151fa086ce1ac8c7f36586fade3e Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Tue, 17 Sep 2024 15:38:06 +0100 Subject: [PATCH 078/103] Fix orderly helper appsec shutdown The sent SIGUSR1 was just terminating the process. --- appsec/src/helper/main.cpp | 21 +++++++++++++--- appsec/src/helper/network/acceptor.cpp | 9 +++---- appsec/src/helper/runner.cpp | 25 ++++++++++++++++--- appsec/src/helper/runner.hpp | 2 +- .../php/integration/Apache2FpmTests.groovy | 1 - 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/appsec/src/helper/main.cpp b/appsec/src/helper/main.cpp index 7f8d9aad77..ec4155704d 100644 --- a/appsec/src/helper/main.cpp +++ b/appsec/src/helper/main.cpp @@ -10,6 +10,8 @@ #include "config.hpp" #include "runner.hpp" #include "subscriber/waf.hpp" +#include +#include #include #include #include @@ -17,7 +19,6 @@ #include extern "C" { -#include #include #include #include @@ -101,6 +102,15 @@ int appsec_helper_main_impl() return 1; } + // block SIGUSR1 (only used to interrupt the runner) + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (auto err = pthread_sigmask(SIG_BLOCK, &mask, nullptr)) { + SPDLOG_ERROR("Failed to block SIGUSR1: error number {}", err); + return 1; + } + auto runner = std::make_unique(config, interrupted); SPDLOG_INFO("starting runner on new thread"); std::thread thr{[runner = std::move(runner)]() { @@ -149,8 +159,13 @@ appsec_helper_shutdown() noexcept return 0; } if (std::chrono::steady_clock::now() >= deadline) { - SPDLOG_WARN("Could not finish AppSec helper before deadline"); - return 1; + // we need to call exit() to avoid a segfault in the still running + // helper threads after the helper shared library is unloaded by + // trampoline.c + SPDLOG_WARN("Could not finish AppSec helper before deadline. " + "Calling exit()."); + std::exit(EXIT_FAILURE); // NOLINT + __builtin_unreachable(); } std::this_thread::sleep_for(std::chrono::milliseconds{10}); // NOLINT } diff --git a/appsec/src/helper/network/acceptor.cpp b/appsec/src/helper/network/acceptor.cpp index 4bb10c3386..2ff26ad767 100644 --- a/appsec/src/helper/network/acceptor.cpp +++ b/appsec/src/helper/network/acceptor.cpp @@ -80,13 +80,10 @@ socket::ptr acceptor::accept() struct sockaddr_un addr {}; socklen_t len = sizeof(addr); - int s; - do { - // NOLINTNEXTLINE - s = ::accept(sock_, reinterpret_cast(&addr), &len); - } while (s == -1 && errno == EINTR); + // NOLINTNEXTLINE + int s = ::accept(sock_, reinterpret_cast(&addr), &len); if (s == -1) { - if (errno == EAGAIN) { + if (errno == EINTR || errno == EAGAIN) { throw dds::timeout_error(); } diff --git a/appsec/src/helper/runner.cpp b/appsec/src/helper/runner.cpp index dcd500927c..ce243dde6f 100644 --- a/appsec/src/helper/runner.cpp +++ b/appsec/src/helper/runner.cpp @@ -7,6 +7,7 @@ #include "client.hpp" #include "subscriber/waf.hpp" +#include #include #include #include @@ -32,6 +33,26 @@ network::base_acceptor::ptr acceptor_from_config(const config::config &cfg) return std::make_unique(sock_path); } + +void unblock_sigusr1() +{ + // the signal handler need not do anything (just interrupt accept()) + struct sigaction alarmer = {}; + alarmer.sa_handler = [](int) {}; + if (sigaction(SIGUSR1, &alarmer, nullptr) < 0) { + throw std::runtime_error{ + "Failed to set SIGUSR1 handler: errno " + std::to_string(errno)}; + } + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (pthread_sigmask(SIG_UNBLOCK, &mask, nullptr) != 0) { + throw std::runtime_error{ + "Failed to unblock SIGUSR1: errno " + std::to_string(errno)}; + } +} + } // namespace runner::runner(const config::config &cfg, std::atomic &interrupted) @@ -54,8 +75,8 @@ runner::runner(const config::config &cfg, void runner::run() { try { - auto last_not_idle = std::chrono::steady_clock::now(); SPDLOG_INFO("Runner running"); + unblock_sigusr1(); while (!interrupted()) { network::base_socket::ptr socket; try { @@ -80,8 +101,6 @@ void runner::run() worker_pool_.launch( [c](worker::queue_consumer &q) mutable { c->run(q); }); - - last_not_idle = std::chrono::steady_clock::now(); } } catch (const std::exception &e) { SPDLOG_ERROR("exception: {}", e.what()); diff --git a/appsec/src/helper/runner.hpp b/appsec/src/helper/runner.hpp index f0ac1b7a5e..bde65b0edf 100644 --- a/appsec/src/helper/runner.hpp +++ b/appsec/src/helper/runner.hpp @@ -30,7 +30,7 @@ class runner { void run() noexcept(false); - bool interrupted() const + [[nodiscard]] bool interrupted() const { return interrupted_.load(std::memory_order_acquire); } diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Apache2FpmTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Apache2FpmTests.groovy index 853ab1b90f..b7b9d44983 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Apache2FpmTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Apache2FpmTests.groovy @@ -12,7 +12,6 @@ import org.testcontainers.junit.jupiter.Testcontainers import java.net.http.HttpResponse import static com.datadog.appsec.php.integration.TestParams.getPhpVersion -import static com.datadog.appsec.php.integration.TestParams.getTracerVersion import static com.datadog.appsec.php.integration.TestParams.getVariant import static org.testcontainers.containers.Container.ExecResult From 421c75f4cdd5b9bae4095ed5de931c212024db40 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Wed, 18 Sep 2024 10:27:43 +0100 Subject: [PATCH 079/103] appsec ext: retry on connection refused --- appsec/src/extension/network.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/appsec/src/extension/network.c b/appsec/src/extension/network.c index 227ad58f5b..b698c8b375 100644 --- a/appsec/src/extension/network.c +++ b/appsec/src/extension/network.c @@ -85,7 +85,8 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) res = connect( conn->socket, (struct sockaddr *)&conn->addr, sizeof(conn->addr)); if (res == -1) { - if (errno == EINPROGRESS) { + int errno_copy = errno; + if (errno_copy == EINPROGRESS) { struct pollfd pfds[] = { {.fd = conn->socket, .events = POLLIN | POLLOUT}}; struct timespec now; @@ -123,8 +124,8 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) } // else good } else { - if (errno == ENOENT) { - // the socket does not exist. Retry + if (errno_copy == ENOENT || errno_copy == ECONNREFUSED) { + // the socket does not exist or is not being listened on. Retry struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); long time_left = _timespec_delta_ms(&deadline, &now); @@ -134,8 +135,10 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) return dd_error; } - mlog(dd_log_debug, - "Socket does not exist. Waiting 100 ms for next retry"); + mlog(dd_log_debug, "Socket %s. Waiting %d ms for next retry", + errno_copy == ENOENT ? "does not exist" + : "is not being listened on", + CONNECT_RETRY_PAUSE); int ret = usleep(CONNECT_RETRY_PAUSE * 1000); // NOLINT if (ret == 0) { goto try_again; @@ -146,6 +149,7 @@ int dd_conn_init( // NOLINT(readability-function-cognitive-complexity) } dd_conn_destroy(conn); + errno = errno_copy; // restore for mlog_err mlog_err( dd_log_info, "Failed connecting to helper (connect() call)"); return dd_error; From e2598bf1192cf3498666d6dd79d0a5efcc3624da Mon Sep 17 00:00:00 2001 From: Alexandre Choura <42672104+PROFeNoM@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:10:53 +0200 Subject: [PATCH 080/103] docs: Remove pre-beta state (#2861) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2dd2687706..9200e45be3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to dd-trace-php -As an open-source project we welcome contributions of many forms, but due to the experimental pre-beta nature of this repository, you should [reach out to us](https://github.com/DataDog/dd-trace-php/issues) before starting work on any major code changes. This will ensure we avoid duplicating work, or that your code can't be merged due to a rapidly changing base. +As an open-source project we welcome contributions of many forms, but you should [reach out to us](https://github.com/DataDog/dd-trace-php/issues) before starting work on any major code changes. This will ensure we avoid duplicating work, or that your code can't be merged due to a rapidly changing base. ## Project initialization From f68f334a2e3124ef7d774734764829369a1ea915 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 19 Sep 2024 10:13:05 +0200 Subject: [PATCH 081/103] Some Redis commands can fail. Handle this in tests (#2848) --- .../PHPRedis/V3/PHPRedisClusterTest.php | 27 ++++++++++++++----- .../PHPRedis/V4/PHPRedisClusterTest.php | 27 ++++++++++++++----- .../PHPRedis/V5/PHPRedisClusterTest.php | 27 ++++++++++++++----- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php b/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php index 52da1ea320..bf2da378cc 100644 --- a/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php +++ b/tests/Integrations/PHPRedis/V3/PHPRedisClusterTest.php @@ -133,11 +133,19 @@ public function dataProviderTestMethodsSpansOnly() /** * @dataProvider dataProviderTestMethodsSimpleSpan */ - public function testMethodsOnlySpan($method, $arg) + public function testMethodsOnlySpan($method, $arg, $canFail = false) { $redis = $this->redis; - $traces = $this->isolateTracer(function () use ($redis, $method, $arg) { - null === $arg ? $redis->$method($this->connection1) : $redis->$method($this->connection1, $arg); + $hasError = false; + $traces = $this->isolateTracer(function () use ($redis, $method, $arg, $canFail, &$hasError) { + try { + null === $arg ? $redis->$method($this->connection1) : $redis->$method($this->connection1, $arg); + } catch (\RedisClusterException $e) { + if (!$canFail) { + throw $e; + } + $hasError = true; + } }); $this->assertFlameGraph($traces, [ @@ -145,8 +153,13 @@ public function testMethodsOnlySpan($method, $arg) "RedisCluster.$method", 'phpredis', 'redis', - "RedisCluster.$method" - )->withExactTags($this->baseTags()), + "RedisCluster.$method", + [], + null, + $hasError + ) + ->withExactTags($this->baseTags()) + ->skipTagsLike('/^error\..*/'), ]); } @@ -156,8 +169,8 @@ public function dataProviderTestMethodsSimpleSpan() 'ping' => ['ping', null], 'echo' => ['echo', 'hey'], 'save' => ['save', null], - 'bgRewriteAOF' => ['bgRewriteAOF', null], - 'flushAll' => ['flushAll', null], + 'bgRewriteAOF' => ['bgRewriteAOF', null, true], + 'flushAll' => ['flushAll', null, true], 'flushDb' => ['flushDb', null], ]; } diff --git a/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php b/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php index 8b52230423..e341a508ab 100644 --- a/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php +++ b/tests/Integrations/PHPRedis/V4/PHPRedisClusterTest.php @@ -133,11 +133,19 @@ public function dataProviderTestMethodsSpansOnly() /** * @dataProvider dataProviderTestMethodsSimpleSpan */ - public function testMethodsOnlySpan($method, $arg) + public function testMethodsOnlySpan($method, $arg, $canFail = false) { $redis = $this->redis; - $traces = $this->isolateTracer(function () use ($redis, $method, $arg) { - null === $arg ? $redis->$method($this->connection1) : $redis->$method($this->connection1, $arg); + $hasError = false; + $traces = $this->isolateTracer(function () use ($redis, $method, $arg, $canFail, &$hasError) { + try { + null === $arg ? $redis->$method($this->connection1) : $redis->$method($this->connection1, $arg); + } catch (\RedisClusterException $e) { + if (!$canFail) { + throw $e; + } + $hasError = true; + } }); $this->assertFlameGraph($traces, [ @@ -145,8 +153,13 @@ public function testMethodsOnlySpan($method, $arg) "RedisCluster.$method", 'phpredis', 'redis', - "RedisCluster.$method" - )->withExactTags($this->baseTags()), + "RedisCluster.$method", + [], + null, + $hasError + ) + ->withExactTags($this->baseTags()) + ->skipTagsLike('/^error\..*/'), ]); } @@ -156,8 +169,8 @@ public function dataProviderTestMethodsSimpleSpan() 'ping' => ['ping', null], 'echo' => ['echo', 'hey'], 'save' => ['save', null], - 'bgRewriteAOF' => ['bgRewriteAOF', null], - 'flushAll' => ['flushAll', null], + 'bgRewriteAOF' => ['bgRewriteAOF', null, true], + 'flushAll' => ['flushAll', null, true], 'flushDb' => ['flushDb', null], ]; } diff --git a/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php b/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php index 498b922fd5..26b01a61c4 100644 --- a/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php +++ b/tests/Integrations/PHPRedis/V5/PHPRedisClusterTest.php @@ -152,11 +152,19 @@ public function dataProviderTestMethodsSpansOnly() /** * @dataProvider dataProviderTestMethodsSimpleSpan */ - public function testMethodsOnlySpan($method, $arg) + public function testMethodsOnlySpan($method, $arg, $canFail = false) { $redis = $this->redis; - $traces = $this->isolateTracer(function () use ($redis, $method, $arg) { - null === $arg ? $redis->$method($this->connection1) : $redis->$method($this->connection1, $arg); + $hasError = false; + $traces = $this->isolateTracer(function () use ($redis, $method, $arg, $canFail, &$hasError) { + try { + null === $arg ? $redis->$method($this->connection1) : $redis->$method($this->connection1, $arg); + } catch (\RedisClusterException $e) { + if (!$canFail) { + throw $e; + } + $hasError = true; + } }); $this->assertFlameGraph($traces, [ @@ -164,8 +172,13 @@ public function testMethodsOnlySpan($method, $arg) "RedisCluster.$method", 'phpredis', 'redis', - "RedisCluster.$method" - )->withExactTags($this->baseTags()), + "RedisCluster.$method", + [], + null, + $hasError + ) + ->withExactTags($this->baseTags()) + ->skipTagsLike('/^error\..*/'), ]); } @@ -175,8 +188,8 @@ public function dataProviderTestMethodsSimpleSpan() 'ping' => ['ping', null], 'echo' => ['echo', 'hey'], 'save' => ['save', null], - 'bgRewriteAOF' => ['bgRewriteAOF', null], - 'flushAll' => ['flushAll', null], + 'bgRewriteAOF' => ['bgRewriteAOF', null, true], + 'flushAll' => ['flushAll', null, true], 'flushDb' => ['flushDb', null], ]; } From d63c6b39eb06c01f172730f00bde0629ea6a4113 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Tue, 17 Sep 2024 17:05:55 +0100 Subject: [PATCH 082/103] unblock SIGUSR1 only around accept() --- appsec/src/helper/network/acceptor.cpp | 2 +- appsec/src/helper/runner.cpp | 40 ++++++++++++++++---------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/appsec/src/helper/network/acceptor.cpp b/appsec/src/helper/network/acceptor.cpp index 2ff26ad767..2c2a567d57 100644 --- a/appsec/src/helper/network/acceptor.cpp +++ b/appsec/src/helper/network/acceptor.cpp @@ -84,7 +84,7 @@ socket::ptr acceptor::accept() int s = ::accept(sock_, reinterpret_cast(&addr), &len); if (s == -1) { if (errno == EINTR || errno == EAGAIN) { - throw dds::timeout_error(); + return {}; } throw std::system_error(errno, std::generic_category()); diff --git a/appsec/src/helper/runner.cpp b/appsec/src/helper/runner.cpp index ce243dde6f..58322b6ded 100644 --- a/appsec/src/helper/runner.cpp +++ b/appsec/src/helper/runner.cpp @@ -34,16 +34,19 @@ network::base_acceptor::ptr acceptor_from_config(const config::config &cfg) return std::make_unique(sock_path); } -void unblock_sigusr1() +void block_sigusr1() { - // the signal handler need not do anything (just interrupt accept()) - struct sigaction alarmer = {}; - alarmer.sa_handler = [](int) {}; - if (sigaction(SIGUSR1, &alarmer, nullptr) < 0) { + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (pthread_sigmask(SIG_UNBLOCK, &mask, nullptr) != 0) { throw std::runtime_error{ - "Failed to set SIGUSR1 handler: errno " + std::to_string(errno)}; + "Failed to block SIGUSR1: errno " + std::to_string(errno)}; } +} +void unblock_sigusr1() +{ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); @@ -53,6 +56,16 @@ void unblock_sigusr1() } } +void handle_sigusr1() +{ + // the signal handler need not do anything (just interrupt accept()) + struct sigaction alarmer = {}; + alarmer.sa_handler = [](int) {}; + if (sigaction(SIGUSR1, &alarmer, nullptr) < 0) { + throw std::runtime_error{ + "Failed to set SIGUSR1 handler: errno " + std::to_string(errno)}; + } +} } // namespace runner::runner(const config::config &cfg, std::atomic &interrupted) @@ -76,18 +89,15 @@ void runner::run() { try { SPDLOG_INFO("Runner running"); - unblock_sigusr1(); + handle_sigusr1(); + while (!interrupted()) { - network::base_socket::ptr socket; - try { - socket = acceptor_->accept(); - } catch (const timeout_error &e) { - continue; - } + unblock_sigusr1(); + network::base_socket::ptr socket = acceptor_->accept(); + block_sigusr1(); if (!socket) { - SPDLOG_CRITICAL("Acceptor returned invalid socket. Bug."); - break; + continue; // interrupted / timeout } if (interrupted()) { From 8b058f8335a0deb753ebd80291da324beb2a43ca Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 19 Sep 2024 16:06:20 +0200 Subject: [PATCH 083/103] Add new ini config `datadog.crashtracking_enabled` (#2850) * Add new ini config `datadog.crashtracking_enabled` * Give more time on Windows * Make DD_LOG_BACKTRACE disabled by default again + make it mutually exclusive with DD_CRASHTRACKING_ENABLED * Refactor code a bit --- ext/configuration.h | 7 ++- ext/signals.c | 27 ++++++--- ..._and_backtrace_are_mutually_exclusive.phpt | 22 ++++++++ tests/ext/crashtracker_segfault_disabled.phpt | 55 +++++++++++++++++++ tests/ext/includes/request_replayer.inc | 21 ++++--- 5 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 tests/ext/crashtracker_and_backtrace_are_mutually_exclusive.phpt create mode 100644 tests/ext/crashtracker_segfault_disabled.phpt diff --git a/ext/configuration.h b/ext/configuration.h index b9c531f611..a2362ca14a 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -91,9 +91,9 @@ enum ddtrace_sampling_rules_format { "(?i)(?:(?:\"|%22)?)(?:(?:old[-_]?|new[-_]?)?p(?:ass)?w(?:or)?d(?:1|2)?|pass(?:[-_]?phrase)?|secret|(?:api[-_]?|private[-_]?|public[-_]?|access[-_]?|secret[-_]?|app(?:lication)?[-_]?)key(?:[-_]?id)?|token|consumer[-_]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\\s|%20)*(?:=|%3D)[^&]+|(?:\"|%22)(?:\\s|%20)*(?::|%3A)(?:\\s|%20)*(?:\"|%22)(?:%2[^2]|%[^2]|[^\"%])+(?:\"|%22))|(?:bearer(?:\\s|%20)+[a-z0-9._\\-]+|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\\w=-]|%3D)+\\.ey[I-L](?:[\\w=-]|%3D)+(?:\\.(?:[\\w.+/=-]|%3D|%2F|%2B)+)?|-{5}BEGIN(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY-{5}[^\\-]+-{5}END(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY(?:-{5})?(?:\\n|%0A)?|(?:ssh-(?:rsa|dss)|ecdsa-[a-z0-9]+-[a-z0-9]+)(?:\\s|%20|%09)+(?:[a-z0-9/.+]|%2F|%5C|%2B){100,}(?:=|%3D)*(?:(?:\\s|%20|%09)+[a-z0-9._-]+)?)" #ifdef __SANITIZE_ADDRESS__ -#define DD_LOG_BACKTRACE_DEFAULT "false" +#define DD_CRASHTRACKING_ENABLED_DEFAULT "false" #else -#define DD_LOG_BACKTRACE_DEFAULT "true" +#define DD_CRASHTRACKING_ENABLED_DEFAULT "true" #endif #define DD_CONFIGURATION_ALL \ @@ -169,7 +169,8 @@ enum ddtrace_sampling_rules_format { CONFIG(INT, DD_TRACE_AGENT_CONNECT_TIMEOUT, DD_CFG_EXPSTR(DD_TRACE_AGENT_CONNECT_TIMEOUT_VAL), \ .ini_change = zai_config_system_ini_change) \ CONFIG(INT, DD_TRACE_DEBUG_PRNG_SEED, "-1", .ini_change = ddtrace_reseed_seed_change) \ - CONFIG(BOOL, DD_LOG_BACKTRACE, DD_LOG_BACKTRACE_DEFAULT) \ + CONFIG(BOOL, DD_LOG_BACKTRACE, "false") \ + CONFIG(BOOL, DD_CRASHTRACKING_ENABLED, DD_CRASHTRACKING_ENABLED_DEFAULT) \ CONFIG(BOOL, DD_TRACE_GENERATE_ROOT_SPAN, "true", .ini_change = ddtrace_span_alter_root_span_config) \ CONFIG(INT, DD_TRACE_SPANS_LIMIT, "1000") \ CONFIG(BOOL, DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED, "true") \ diff --git a/ext/signals.c b/ext/signals.c index 2f465045dc..d5a280e47c 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -162,19 +162,34 @@ static void ddtrace_init_crashtracker() { } void ddtrace_signals_first_rinit(void) { - bool install_handler = get_DD_TRACE_HEALTH_METRICS_ENABLED(); + DDTRACE_G(backtrace_handler_already_run) = false; + + // Signal handlers are causing issues with FrankenPHP. + if (ddtrace_active_sapi == DATADOG_PHP_SAPI_FRANKENPHP) { + return; + } + + bool install_crashtracker = get_DD_INSTRUMENTATION_TELEMETRY_ENABLED() && get_DD_CRASHTRACKING_ENABLED(); + bool install_backtrace_handler = get_DD_TRACE_HEALTH_METRICS_ENABLED(); #if DDTRACE_HAVE_BACKTRACE - install_handler |= get_DD_LOG_BACKTRACE(); + install_backtrace_handler |= get_DD_LOG_BACKTRACE(); #endif - DDTRACE_G(backtrace_handler_already_run) = false; + if (install_crashtracker) { + ddtrace_init_crashtracker(); + } /* Install a signal handler for SIGSEGV and run it on an alternate stack. * Using an alternate stack allows the handler to run even when the main * stack overflows. */ - if (install_handler && ddtrace_active_sapi != DATADOG_PHP_SAPI_FRANKENPHP) { + if (install_backtrace_handler) { + if (install_crashtracker) { + LOG(WARN, "Settings 'datadog.log_backtrace' and 'datadog.crashtracking_enabled' are mutually exclusive. Cannot enable the backtrace."); + return; + } + size_t stack_size = SIGSTKSZ < MIN_STACKSZ ? MIN_STACKSZ : SIGSTKSZ; if ((ddtrace_altstack.ss_sp = malloc(stack_size))) { ddtrace_altstack.ss_size = stack_size; @@ -186,10 +201,6 @@ void ddtrace_signals_first_rinit(void) { sigaction(SIGSEGV, &ddtrace_sigaction, NULL); } } - - if (get_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { - ddtrace_init_crashtracker(); - } } } diff --git a/tests/ext/crashtracker_and_backtrace_are_mutually_exclusive.phpt b/tests/ext/crashtracker_and_backtrace_are_mutually_exclusive.phpt new file mode 100644 index 0000000000..acba65ebac --- /dev/null +++ b/tests/ext/crashtracker_and_backtrace_are_mutually_exclusive.phpt @@ -0,0 +1,22 @@ +--TEST-- +Settings 'datadog.log_backtrace' and 'datadog.crashtracking_enabled' are mutually exclusive +--SKIPIF-- + +--ENV-- +DD_TRACE_LOG_LEVEL=warn,span=off,startup=off +DD_LOG_BACKTRACE=1 +DD_CRASHTRACKING_ENABLED=1 +--INI-- +datadog.trace.log_file=file://stdout +--FILE-- + +--EXPECTF-- +[ddtrace] [warning] Settings 'datadog.log_backtrace' and 'datadog.crashtracking_enabled' are mutually exclusive. Cannot enable the backtrace. +1 diff --git a/tests/ext/crashtracker_segfault_disabled.phpt b/tests/ext/crashtracker_segfault_disabled.phpt new file mode 100644 index 0000000000..bb73bfdf5c --- /dev/null +++ b/tests/ext/crashtracker_segfault_disabled.phpt @@ -0,0 +1,55 @@ +--TEST-- +Don't send crashtracker report when segmentation fault signal is raised and config disables it +--SKIPIF-- + +--ENV-- +DD_TRACE_LOG_LEVEL=0 +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_CRASHTRACKING_ENABLED=0 +--INI-- +datadog.trace.agent_test_session_token=tests/ext/crashtracker_segfault_disabled.phpt +--FILE-- +replayRequest(); // cleanup possible leftover + +usleep(100000); // Let time to the sidecar to open the crashtracker socket + +$php = getenv('TEST_PHP_EXECUTABLE'); +$args = getenv('TEST_PHP_ARGS')." ".getenv("TEST_PHP_EXTRA_ARGS"); +$cmd = $php." ".$args." -r 'posix_kill(posix_getpid(), 11);'"; +system($cmd); + +$rr->waitForRequest(function ($request) { + if ($request["uri"] != "/telemetry/proxy/api/v2/apmtelemetry") { + return false; + } + $body = json_decode($request["body"], true); + if ($body["request_type"] != "logs" || !isset($body["payload"][0]["message"])) { + return false; + } + + $payload = $body["payload"][0]; + $payload["message"] = json_decode($payload["message"], true); + $output = json_encode($payload, JSON_PRETTY_PRINT); + + echo $output; + + return true; +}); + +?> +--EXPECTF-- +%A +Fatal error: Uncaught Exception: wait for replay timeout in %s +%A diff --git a/tests/ext/includes/request_replayer.inc b/tests/ext/includes/request_replayer.inc index d1c456a063..528660e9e2 100644 --- a/tests/ext/includes/request_replayer.inc +++ b/tests/ext/includes/request_replayer.inc @@ -12,6 +12,11 @@ class RequestReplayer */ private $flushInterval; + /** + * @var int + */ + private $maxIteration; + public function __construct() { $this->endpoint = sprintf( @@ -21,23 +26,25 @@ class RequestReplayer ); $this->flushInterval = getenv('DD_TRACE_AGENT_FLUSH_INTERVAL') - ? (int) getenv('DD_TRACE_AGENT_FLUSH_INTERVAL') - : 5000; + ? (int) getenv('DD_TRACE_AGENT_FLUSH_INTERVAL') * 100 + : 50000; + + $this->maxIteration = (strncasecmp(PHP_OS, "WIN", 3) === 0) ? 500 : 200; } public function waitForFlush() { - usleep($this->flushInterval * 2 * 1000); + usleep($this->flushInterval * 2); } public function waitForRequest($matcher) { $i = 0; do { - if ($i++ == 100) { + if ($i++ == $this->maxIteration) { throw new Exception("wait for replay timeout"); } - usleep($this->flushInterval * 1000); + usleep($this->flushInterval); $requests = $this->replayAllRequests(); if (is_array($requests)) { @@ -54,10 +61,10 @@ class RequestReplayer { $i = 0; do { - if ($i++ == 100) { + if ($i++ == $this->maxIteration) { throw new Exception("wait for replay timeout"); } - usleep($this->flushInterval * 1000); + usleep($this->flushInterval); } while (empty($data = $this->replayRequest($ignoreTelemetry))); return $data; } From 3746a2f68f64484dd2b0f62228e615b3b960513d Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 19 Sep 2024 17:38:33 +0200 Subject: [PATCH 084/103] Add ionCube to the list of incompatible extensions + add INI setting to ignore incompatible extensions (#2858) * Add ionCube to the list of incompatible extensions * Add 'datadog.inject_force' INI setting to ignore incompatible extensions * Conflict with ionCube only on PHP 8.0+ * Fix access of global config Co-authored-by: Bob Weinand * Fix ini_change mode Co-authored-by: Bob Weinand --------- Co-authored-by: Bob Weinand --- ext/configuration.h | 1 + ext/excluded_modules.c | 31 ++++++++++++++----- tests/xdebug/2.7.2/self_disable_php_7.0.phpt | 3 +- tests/xdebug/2.9.2/force_inject.phpt | 18 +++++++++++ tests/xdebug/2.9.2/self_disable.phpt | 3 +- .../2.9.2/startup_logging_diagnostics.phpt | 2 +- 6 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 tests/xdebug/2.9.2/force_inject.phpt diff --git a/ext/configuration.h b/ext/configuration.h index a2362ca14a..3962ce6ffd 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -227,6 +227,7 @@ enum ddtrace_sampling_rules_format { CONFIG(INT, DD_OPENAI_SPAN_CHAR_LIMIT, "128") \ CONFIG(DOUBLE, DD_OPENAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE, "1.0") \ CONFIG(DOUBLE, DD_OPENAI_LOG_PROMPT_COMPLETION_SAMPLE_RATE, "0.1") \ + CONFIG(BOOL, DD_INJECT_FORCE, "false", .ini_change = zai_config_system_ini_change) \ DD_INTEGRATIONS #ifndef _WIN32 diff --git a/ext/excluded_modules.c b/ext/excluded_modules.c index 0caaa3d227..4bad02a804 100644 --- a/ext/excluded_modules.c +++ b/ext/excluded_modules.c @@ -6,8 +6,17 @@ #include #include +#include "configuration.h" bool ddtrace_is_excluded_module(zend_module_entry *module, char *error) { +#if PHP_VERSION_ID >= 80000 + if (strcmp("ionCube Loader", module->name) == 0) { + snprintf(error, DDTRACE_EXCLUDED_MODULES_ERROR_MAX_LEN, + "Found incompatible ionCube Loader extension"); + return true; + } +#endif + if (strcmp("xdebug", module->name) == 0) { /* PHP 7.0 was only supported from Xdebug 2.4 through 2.7 @@ -15,7 +24,7 @@ bool ddtrace_is_excluded_module(zend_module_entry *module, char *error) { */ #if PHP_VERSION_ID < 70100 snprintf(error, DDTRACE_EXCLUDED_MODULES_ERROR_MAX_LEN, - "Found incompatible Xdebug version %s; disabling conflicting functionality", module->version); + "Found incompatible Xdebug version %s", module->version); return true; #endif /* @@ -37,8 +46,7 @@ bool ddtrace_is_excluded_module(zend_module_entry *module, char *error) { int compare = php_version_compare(module->version, "2.9.5"); if (compare == -1) { snprintf(error, DDTRACE_EXCLUDED_MODULES_ERROR_MAX_LEN, - "Found incompatible Xdebug version %s; ddtrace requires Xdebug 2.9.5 or greater; disabling " - "conflicting functionality", + "Found incompatible Xdebug version %s; ddtrace requires Xdebug 2.9.5 or greater", module->version); return true; } @@ -50,18 +58,27 @@ void ddtrace_excluded_modules_startup() { zend_module_entry *module; ddtrace_has_excluded_module = false; + bool inject_force = get_global_DD_INJECT_FORCE(); ZEND_HASH_FOREACH_PTR(&module_registry, module) { char error[DDTRACE_EXCLUDED_MODULES_ERROR_MAX_LEN + 1]; if (module && module->name && module->version && ddtrace_is_excluded_module(module, error)) { ddtrace_has_excluded_module = true; - if (strcmp("xdebug", module->name) == 0) { - LOG(ERROR, error); - } else { + if (inject_force) { LOG(WARN, error); + } else { + LOG(ERROR, error); } - return; } } ZEND_HASH_FOREACH_END(); + + if (ddtrace_has_excluded_module) { + if (inject_force) { + LOG(WARN, "Found incompatible extension(s); ignoring since 'datadog.inject_force' is enabled"); + ddtrace_has_excluded_module = false; + } else { + LOG(ERROR, "Found incompatible extension(s); disabling conflicting functionality"); + } + } } diff --git a/tests/xdebug/2.7.2/self_disable_php_7.0.phpt b/tests/xdebug/2.7.2/self_disable_php_7.0.phpt index ca2cae91ad..d8a32bbac1 100644 --- a/tests/xdebug/2.7.2/self_disable_php_7.0.phpt +++ b/tests/xdebug/2.7.2/self_disable_php_7.0.phpt @@ -11,5 +11,6 @@ if (!extension_loaded('Xdebug')) die('skip: Xdebug required'); echo 'Done.' . PHP_EOL; ?> --EXPECTF-- -[ddtrace] [error] Found incompatible Xdebug version %s; disabling conflicting functionality +[ddtrace] [error] Found incompatible Xdebug version %s +[ddtrace] [error] Found incompatible extension(s); disabling conflicting functionality Done. diff --git a/tests/xdebug/2.9.2/force_inject.phpt b/tests/xdebug/2.9.2/force_inject.phpt new file mode 100644 index 0000000000..b735f5fe22 --- /dev/null +++ b/tests/xdebug/2.9.2/force_inject.phpt @@ -0,0 +1,18 @@ +--TEST-- +The tracer will ignore incompatible extensions +--SKIPIF-- + +--INI-- +xdebug.remote_enable=1 +datadog.inject_force=1 +datadog.trace.log_level=warn +--FILE-- += 0) die('Xdebug < 2.9.5 required'); + +echo 'Done.' . PHP_EOL; +?> +--EXPECTF-- +[ddtrace] [warning] Found incompatible Xdebug version %s; ddtrace requires Xdebug 2.9.5 or greater +[ddtrace] [warning] Found incompatible extension(s); ignoring since 'datadog.inject_force' is enabled +Done. diff --git a/tests/xdebug/2.9.2/self_disable.phpt b/tests/xdebug/2.9.2/self_disable.phpt index b40eaaf1ad..a6fe0fae32 100644 --- a/tests/xdebug/2.9.2/self_disable.phpt +++ b/tests/xdebug/2.9.2/self_disable.phpt @@ -11,5 +11,6 @@ if (!extension_loaded('Xdebug') || version_compare(phpversion('Xdebug'), '2.9.5' echo 'Done.' . PHP_EOL; ?> --EXPECTF-- -[ddtrace] [error] Found incompatible Xdebug version %s; ddtrace requires Xdebug 2.9.5 or greater; disabling conflicting functionality +[ddtrace] [error] Found incompatible Xdebug version %s; ddtrace requires Xdebug 2.9.5 or greater +[ddtrace] [error] Found incompatible extension(s); disabling conflicting functionality Done. diff --git a/tests/xdebug/2.9.2/startup_logging_diagnostics.phpt b/tests/xdebug/2.9.2/startup_logging_diagnostics.phpt index 27599a5ff3..f639637a3e 100644 --- a/tests/xdebug/2.9.2/startup_logging_diagnostics.phpt +++ b/tests/xdebug/2.9.2/startup_logging_diagnostics.phpt @@ -18,4 +18,4 @@ if (!isset($logs['incompatible module xdebug'])) { echo 'Log: ' . $logs['incompatible module xdebug'] . PHP_EOL; ?> --EXPECT-- -Log: Found incompatible Xdebug version 2.9.2; ddtrace requires Xdebug 2.9.5 or greater; disabling conflicting functionality +Log: Found incompatible Xdebug version 2.9.2; ddtrace requires Xdebug 2.9.5 or greater From f117e2240e3ab1a8e7ca7590aebcb8fe0c85037f Mon Sep 17 00:00:00 2001 From: Alejandro Estringana Ruiz Date: Wed, 25 Sep 2024 11:49:33 +0200 Subject: [PATCH 085/103] Add appsec benchmarks (#2791) Add benchmarking with appsec loaded Add logs of all benchmarking executions --- .gitlab/benchmarks.yml | 25 ++++ Makefile | 29 +++- benchmark/runall.sh | 14 +- docker-compose.yml | 1 + .../Integrations/EmptyFileBench.php | 21 +-- .../Integrations/FrameworkBenchmarksCase.php | 36 +++++ .../Benchmarks/Integrations/LaravelBench.php | 23 +-- .../Benchmarks/Integrations/SymfonyBench.php | 23 +-- .../Integrations/WordPressBench.php | 21 +-- tests/Benchmarks/composer.json | 5 + tests/bootstrap_common.php | 138 +++++++++++++----- tests/bootstrap_phpbench.php | 4 +- tests/bootstrap_phpunit.php | 63 -------- 13 files changed, 232 insertions(+), 171 deletions(-) create mode 100644 tests/Benchmarks/Integrations/FrameworkBenchmarksCase.php diff --git a/.gitlab/benchmarks.yml b/.gitlab/benchmarks.yml index 98e3f7a76f..c39daa6d7a 100644 --- a/.gitlab/benchmarks.yml +++ b/.gitlab/benchmarks.yml @@ -63,9 +63,34 @@ benchmarks-tracer: compare_to: "master" when: on_success - when: manual + artifacts: + name: "logs" + paths: + - candidate.tar.gz + - baseline.tar.gz + expire_in: 2 days variables: SCENARIO: "tracer" +benchmarks-appsec: + extends: .microbenchmarks + rules: + - if: $CI_PIPELINE_SOURCE != "schedule" + changes: + paths: + - appsec/src/**/* + compare_to: "master" + when: on_success + - when: manual + artifacts: + name: "logs" + paths: + - candidate.tar.gz + - baseline.tar.gz + expire_in: 2 days + variables: + SCENARIO: "appsec" + download_circle_ci_release: stage: benchmarks diff --git a/Makefile b/Makefile index ef9dfec03f..ce0cb714a4 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,10 @@ PROJECT_ROOT := ${PWD} TRACER_SOURCE_DIR := $(PROJECT_ROOT)/src/ ASAN ?= $(shell ldd $(shell which php) 2>/dev/null | grep -q libasan && echo 1) SHELL = /bin/bash +APPSEC_SOURCE_DIR = $(PROJECT_ROOT)/appsec/ BUILD_SUFFIX = extension BUILD_DIR = $(PROJECT_ROOT)/tmp/build_$(BUILD_SUFFIX) +BUILD_DIR_APPSEC = $(BUILD_DIR)/appsec/ ZAI_BUILD_DIR = $(PROJECT_ROOT)/tmp/build_zai$(if $(ASAN),_asan) TEA_BUILD_DIR = $(PROJECT_ROOT)/tmp/build_tea$(if $(ASAN),_asan) TEA_INSTALL_DIR = $(TEA_BUILD_DIR)/opt @@ -13,7 +15,8 @@ TEA_BUILD_BENCHMARKS ?= OFF TEA_BENCHMARK_REPETITIONS ?= 10 # Note: If the tea benchmark format or output is changed, make changes to ./benchmark/runall.sh TEA_BENCHMARK_FORMAT ?= json -TEA_BENCHMARK_OUTPUT ?= $(PROJECT_ROOT)/tea/benchmarks/reports/tea-bench-results.$(TEA_BENCHMARK_FORMAT) +TEA_BENCHMARK_OUTPUT ?= $(PROJECT_ROOT)/tea/benchmarks/reports/tracer-tea-bench-results.$(TEA_BENCHMARK_FORMAT) +BENCHMARK_EXTRA ?= COMPONENTS_BUILD_DIR = $(PROJECT_ROOT)/tmp/build_components SO_FILE = $(BUILD_DIR)/modules/ddtrace.so AR_FILE = $(BUILD_DIR)/modules/ddtrace.a @@ -127,6 +130,24 @@ $(INI_FILE): install_ini: $(INI_FILE) +delete_ini: + $(SUDO) rm $(INI_FILE) + +install_appsec: + cmake -S $(APPSEC_SOURCE_DIR) -B $(BUILD_DIR_APPSEC) + cd $(BUILD_DIR_APPSEC);make extension ddappsec-helper + cp $(BUILD_DIR_APPSEC)/ddappsec.so $(PHP_EXTENSION_DIR)/ddappsec.so + cp $(BUILD_DIR_APPSEC)/libddappsec-helper.so $(PHP_EXTENSION_DIR)/libddappsec-helper.so + cp $(APPSEC_SOURCE_DIR)/recommended.json /tmp/recommended.json + $(Q) echo "extension=ddappsec.so" | $(SUDO) tee -a $(INI_FILE) + $(Q) echo "datadog.appsec.cli_start_on_rinit=true" | $(SUDO) tee -a $(INI_FILE) + $(Q) echo "datadog.appsec.helper_path=$(PHP_EXTENSION_DIR)/libddappsec-helper.so" | $(SUDO) tee -a $(INI_FILE) + $(Q) echo "datadog.appsec.rules=/tmp/recommended.json" | $(SUDO) tee -a $(INI_FILE) + $(Q) echo "datadog.appsec.helper_socket_path=/tmp/ddappsec.sock" | $(SUDO) tee -a $(INI_FILE) + $(Q) echo "datadog.appsec.helper_lock_path=/tmp/ddappsec.lock" | $(SUDO) tee -a $(INI_FILE) + $(Q) echo "datadog.appsec.log_file=/tmp/logs/appsec.log" | $(SUDO) tee -a $(INI_FILE) + $(Q) echo "datadog.appsec.helper_log_file=/tmp/logs/helper.log" | $(SUDO) tee -a $(INI_FILE) + install_all: install install_ini run_tests: $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php @@ -1040,11 +1061,11 @@ endef define run_benchmarks - $(ENV_OVERRIDE) php -d extension=redis-5.3.7.so $(TEST_EXTRA_INI) $(TRACER_SOURCES_INI) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console + $(ENV_OVERRIDE) php -d extension=redis-5.3.7.so $(TEST_EXTRA_INI) $(TRACER_SOURCES_INI) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console $(BENCHMARK_EXTRA) endef define run_benchmarks_with_ddprof - $(ENV_OVERRIDE) ddprof -S $(DDPROF_IDENTIFIER) php -d extension=redis-5.3.7.so $(TEST_EXTRA_INI) $(REQUEST_INIT_HOOK) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console + $(ENV_OVERRIDE) ddprof -S $(DDPROF_IDENTIFIER) php -d extension=redis-5.3.7.so $(TEST_EXTRA_INI) $(REQUEST_INIT_HOOK) $(PHPBENCH) --config=$(1) --filter=$(FILTER) --report=all --output=file --output=console $(BENCHMARK_EXTRA) endef define run_composer_with_lock @@ -1115,7 +1136,7 @@ test_distributed_tracing_coverage: test_metrics: global_test_run_dependencies $(call run_tests,--testsuite=metrics $(TESTS)) -benchmarks_run_dependencies: global_test_run_dependencies tests/Frameworks/Symfony/Version_5_2/composer.lock-php$(PHP_MAJOR_MINOR) tests/Frameworks/Laravel/Version_8_x/composer.lock-php$(PHP_MAJOR_MINOR) tests/Benchmarks/composer.lock-php$(PHP_MAJOR_MINOR) +benchmarks_run_dependencies: global_test_run_dependencies tests/Frameworks/Symfony/Version_5_2/composer.lock-php$(PHP_MAJOR_MINOR) tests/Frameworks/Laravel/Version_10_x/composer.lock-php$(PHP_MAJOR_MINOR) tests/Benchmarks/composer.lock-php$(PHP_MAJOR_MINOR) php tests/Frameworks/Symfony/Version_5_2/bin/console cache:clear --no-warmup --env=prod call_benchmarks: diff --git a/benchmark/runall.sh b/benchmark/runall.sh index 0f2eeff874..7f0a5eeafb 100755 --- a/benchmark/runall.sh +++ b/benchmark/runall.sh @@ -30,5 +30,17 @@ elif [ "$SCENARIO" = "tracer" ]; then ## Request Startup/Shutdown Benchmarks make benchmarks_tea - cp tea/benchmarks/reports/tea-bench-results.json "$ARTIFACTS_DIR/tracer-tea-bench-results.json" + cp tea/benchmarks/reports/tracer-tea-bench-results.json "$ARTIFACTS_DIR/tracer-tea-bench-results.json" +elif [ "$SCENARIO" = "appsec" ]; then + # Run Appsec Benchmarks + cd .. + make composer_tests_update + make benchmarks_run_dependencies + make install_appsec + + ## Non-OPCache Benchmarks + BENCHMARK_EXTRA="--group=frameworks" make call_benchmarks + cp tests/Benchmarks/reports/tracer-bench-results.csv "$ARTIFACTS_DIR/appsec-bench-results.csv" + + make delete_ini fi diff --git a/docker-compose.yml b/docker-compose.yml index d5e4d00842..77927c155a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,6 +83,7 @@ services: '8.3-buster': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.3_buster' } 'php-master-buster': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-master_buster' } # --- Bookworm --- + '8.2-bookworm': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.2_bookworm-3' } '8.3-bookworm': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.3_bookworm-3' } # --- CentOS 6 --- '7.0-centos7': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-7.0_centos-7' } diff --git a/tests/Benchmarks/Integrations/EmptyFileBench.php b/tests/Benchmarks/Integrations/EmptyFileBench.php index ba58b35cf3..1de5efd968 100644 --- a/tests/Benchmarks/Integrations/EmptyFileBench.php +++ b/tests/Benchmarks/Integrations/EmptyFileBench.php @@ -6,11 +6,12 @@ use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; +use Benchmarks\Integrations\FrameworkBenchmarksCase; -class EmptyFileBench extends WebFrameworkTestCase +class EmptyFileBench extends FrameworkBenchmarksCase { /** - * @BeforeMethods("disabledTracing") + * @BeforeMethods("disableDatadog") * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) @@ -27,7 +28,7 @@ public function benchEmptyFileBaseline() } /** - * @BeforeMethods("enableTracing") + * @BeforeMethods("enableDatadog") * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) @@ -48,22 +49,8 @@ public static function getAppIndexScript() return __DIR__ . '/../../Frameworks/Custom/Version_Not_Autoloaded/index.php'; } - public function disabledTracing() - { - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 0, - ]); - } - public function afterMethod() { $this->TearDownAfterClass(); } - - public function enableTracing() - { - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 1, - ]); - } } diff --git a/tests/Benchmarks/Integrations/FrameworkBenchmarksCase.php b/tests/Benchmarks/Integrations/FrameworkBenchmarksCase.php new file mode 100644 index 0000000000..c3b0a9bd42 --- /dev/null +++ b/tests/Benchmarks/Integrations/FrameworkBenchmarksCase.php @@ -0,0 +1,36 @@ +getClassName(); + $this->setUpWebServer([ + 'DD_TRACE_ENABLED' => 0, + 'DD_APPSEC_ENABLED' => 0, + ], ['error_log' => "/tmp/logs/$name.log"]); + } + + public function enableDatadog() + { + $name = $this->getClassName(); + $this->setUpWebServer([ + 'DD_TRACE_ENABLED' => 1, + 'DD_APPSEC_ENABLED' => 1, + ], ['error_log' => "/tmp/logs/$name.log"]); + } +} diff --git a/tests/Benchmarks/Integrations/LaravelBench.php b/tests/Benchmarks/Integrations/LaravelBench.php index d411fa2181..eaf6e6bb2d 100644 --- a/tests/Benchmarks/Integrations/LaravelBench.php +++ b/tests/Benchmarks/Integrations/LaravelBench.php @@ -7,10 +7,13 @@ use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; -class LaravelBench extends WebFrameworkTestCase +/** +* @Groups({"frameworks"}) +*/ +class LaravelBench extends FrameworkBenchmarksCase { /** - * @BeforeMethods("disableLaravelTracing") + * @BeforeMethods("disableDatadog") * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) @@ -27,7 +30,7 @@ public function benchLaravelBaseline() } /** - * @BeforeMethods("enableLaravelTracing") + * @BeforeMethods("enableDatadog") * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) @@ -48,22 +51,8 @@ public static function getAppIndexScript() return __DIR__ . '/../../Frameworks/Laravel/Version_10_x/public/index.php'; } - public function disableLaravelTracing() - { - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 0, - ]); - } - public function afterMethod() { $this->TearDownAfterClass(); } - - public function enableLaravelTracing() - { - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 1, - ]); - } } diff --git a/tests/Benchmarks/Integrations/SymfonyBench.php b/tests/Benchmarks/Integrations/SymfonyBench.php index 634224cf55..b43deff579 100644 --- a/tests/Benchmarks/Integrations/SymfonyBench.php +++ b/tests/Benchmarks/Integrations/SymfonyBench.php @@ -7,10 +7,13 @@ use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; -class SymfonyBench extends WebFrameworkTestCase +/** +* @Groups({"frameworks"}) +*/ +class SymfonyBench extends FrameworkBenchmarksCase { /** - * @BeforeMethods("disableSymfonyTracing") + * @BeforeMethods("disableDatadog") * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) @@ -27,7 +30,7 @@ public function benchSymfonyBaseline() } /** - * @BeforeMethods("enableSymfonyTracing") + * @BeforeMethods("enableDatadog") * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) @@ -48,22 +51,8 @@ public static function getAppIndexScript() return __DIR__ . '/../../Frameworks/Symfony/Version_5_2/public/index.php'; } - public function disableSymfonyTracing() - { - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 0, - ]); - } - public function afterMethod() { $this->TearDownAfterClass(); } - - public function enableSymfonyTracing() - { - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 1, - ]); - } } diff --git a/tests/Benchmarks/Integrations/WordPressBench.php b/tests/Benchmarks/Integrations/WordPressBench.php index ff5aa223af..41b88d862c 100644 --- a/tests/Benchmarks/Integrations/WordPressBench.php +++ b/tests/Benchmarks/Integrations/WordPressBench.php @@ -7,10 +7,13 @@ use DDTrace\Tests\Common\WebFrameworkTestCase; use DDTrace\Tests\Frameworks\Util\Request\GetSpec; -class WordPressBench extends WebFrameworkTestCase +/** +* @Groups({"frameworks"}) +*/ +class WordPressBench extends FrameworkBenchmarksCase { /** - * @BeforeMethods("enableWordPressTracing") + * @BeforeMethods({"enableDatadog", "createDatabase"}) * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) @@ -31,14 +34,11 @@ public static function getAppIndexScript() return __DIR__ . '/../../Frameworks/WordPress/Version_6_1/index.php'; } - public function disableWordPressTracing() + public function createDatabase(): void { $pdo = new \PDO('mysql:host=mysql_integration', 'test', 'test'); $pdo->exec('CREATE DATABASE IF NOT EXISTS wp61'); $pdo->exec(file_get_contents(__DIR__ . '/../../Frameworks/WordPress/Version_6_1/scripts/wp_initdb.sql')); - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 0, - ]); } public function afterMethod() @@ -46,15 +46,8 @@ public function afterMethod() $this->TearDownAfterClass(); } - public function enableWordPressTracing() - { - $this->setUpWebServer([ - 'DD_TRACE_ENABLED' => 1 - ]); - } - /** - * @BeforeMethods("disableWordPressTracing") + * @BeforeMethods({"disableDatadog", "createDatabase"}) * @AfterMethods("afterMethod") * @Revs(10) * @Iterations(10) diff --git a/tests/Benchmarks/composer.json b/tests/Benchmarks/composer.json index 20a6aaf663..94a0188cc8 100644 --- a/tests/Benchmarks/composer.json +++ b/tests/Benchmarks/composer.json @@ -5,5 +5,10 @@ "phpbench/phpbench": "^1.0", "phpunit/phpunit": "<10", "symfony/process": "<5" + }, + "autoload-dev": { + "psr-4": { + "Benchmarks\\": "./" + } } } diff --git a/tests/bootstrap_common.php b/tests/bootstrap_common.php index bb63230d53..94a4597cb9 100644 --- a/tests/bootstrap_common.php +++ b/tests/bootstrap_common.php @@ -1,53 +1,117 @@ = 8) { + require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_typed.php'; + } else { + require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_untyped.php'; + } + function update_test_agent_session_token($token) { + if (defined('GLOBAL_AUTO_PREPEND_RSRC')) { + ini_set("datadog.trace.agent_test_session_token", $token); + ftruncate(GLOBAL_AUTO_PREPEND_RSRC, 0); + fseek(GLOBAL_AUTO_PREPEND_RSRC, 0); + fwrite(GLOBAL_AUTO_PREPEND_RSRC, " strlen(__DIR__)) { + if (file_exists("$path/vendor/autoload.php")) { + putenv("COMPOSER_ROOT_VERSION=1.0.0"); // silence composer + \DDTrace\Tests\Common\IntegrationTestCase::$autoloadPath = "$path/vendor/autoload.php"; + require_once \DDTrace\Tests\Common\IntegrationTestCase::$autoloadPath; + return; + } elseif (file_exists("$path/composer.json")) { + \DDTrace\Testing\trigger_error("Found $path/composer.json, but seems not installed", E_USER_ERROR); + } + $path = dirname($path); + } + }; + if (class_exists('PHPUnit\Runner\StandardTestSuiteLoader')) { + \DDTrace\hook_method('PHPUnit\Util\FileLoader', 'load', $hook); + \DDTrace\hook_method('PHPUnit\Runner\StandardTestSuiteLoader', 'load', $hook); + } elseif (method_exists('PHPUnit\Runner\TestSuiteLoader', 'loadSuiteClassFile')) { + \DDTrace\hook_method('PHPUnit\Runner\TestSuiteLoader', 'loadSuiteClassFile', $hook); + } else { + \DDTrace\hook_method('PHPUnit\Runner\TestSuiteLoader', 'load', $hook); + } +} diff --git a/tests/bootstrap_phpbench.php b/tests/bootstrap_phpbench.php index 245aa17a69..f02397139e 100644 --- a/tests/bootstrap_phpbench.php +++ b/tests/bootstrap_phpbench.php @@ -1,5 +1,7 @@ = 8) { - require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_typed.php'; -} else { - require __DIR__ . '/Common/MultiPHPUnitVersionAdapter_untyped.php'; -} - require_once __DIR__ . '/Appsec/Mock.php'; - -function update_test_agent_session_token($token) { - if (defined('GLOBAL_AUTO_PREPEND_RSRC')) { - ini_set("datadog.trace.agent_test_session_token", $token); - ftruncate(GLOBAL_AUTO_PREPEND_RSRC, 0); - fseek(GLOBAL_AUTO_PREPEND_RSRC, 0); - fwrite(GLOBAL_AUTO_PREPEND_RSRC, " strlen(__DIR__)) { - if (file_exists("$path/vendor/autoload.php")) { - putenv("COMPOSER_ROOT_VERSION=1.0.0"); // silence composer - \DDTrace\Tests\Common\IntegrationTestCase::$autoloadPath = "$path/vendor/autoload.php"; - require_once \DDTrace\Tests\Common\IntegrationTestCase::$autoloadPath; - return; - } elseif (file_exists("$path/composer.json")) { - \DDTrace\Testing\trigger_error("Found $path/composer.json, but seems not installed", E_USER_ERROR); - } - $path = dirname($path); - } -}; -if (class_exists('PHPUnit\Runner\StandardTestSuiteLoader')) { - \DDTrace\hook_method('PHPUnit\Util\FileLoader', 'load', $hook); - \DDTrace\hook_method('PHPUnit\Runner\StandardTestSuiteLoader', 'load', $hook); -} elseif (method_exists('PHPUnit\Runner\TestSuiteLoader', 'loadSuiteClassFile')) { - \DDTrace\hook_method('PHPUnit\Runner\TestSuiteLoader', 'loadSuiteClassFile', $hook); -} else { - \DDTrace\hook_method('PHPUnit\Runner\TestSuiteLoader', 'load', $hook); -} From 2fb5aac30f004e48d74d1a3b58720e7ba730eef9 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 26 Sep 2024 13:59:44 +0200 Subject: [PATCH 086/103] Fix crash with git metadata injection (#2862) * Fix crash: it is possible for property_commit or property_repository to be NULL * No need to addref asdd add_git_info will also do a copy * Remove read_git_file 2nd argument as it is always persistent --- ext/git.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ext/git.c b/ext/git.c index 230b1b83a7..cd90158116 100644 --- a/ext/git.c +++ b/ext/git.c @@ -45,7 +45,7 @@ static char *find_last_dir_separator(const char *path) { #endif } -zend_string *read_git_file(const char *path, bool persistent) { +zend_string *read_git_file(const char *path) { FILE *file = fopen(path, "r"); if (!file) { return NULL; @@ -61,14 +61,14 @@ zend_string *read_git_file(const char *path, bool persistent) { buffer[len] = '\0'; len = remove_trailing_newline(buffer); - return zend_string_init(buffer, len, persistent); + return zend_string_init(buffer, len, true); } zend_string *get_commit_sha(const char *git_dir) { char head_path[PATH_MAX]; snprintf(head_path, sizeof(head_path), "%s/HEAD", git_dir); - zend_string *head_content = read_git_file(head_path, true); + zend_string *head_content = read_git_file(head_path); if (!head_content) { return NULL; } @@ -78,7 +78,7 @@ zend_string *get_commit_sha(const char *git_dir) { char ref_path[PATH_MAX]; snprintf(ref_path, sizeof(ref_path), "%s/%s", git_dir, ZSTR_VAL(head_content) + strlen(ref_prefix)); zend_string_release(head_content); - return read_git_file(ref_path, true); + return read_git_file(ref_path); } return head_content; @@ -257,12 +257,6 @@ bool process_git_info(zend_string *git_dir, zend_string *cwd) { return success; } -void use_cached_metadata(git_metadata_t *git_metadata) { - zend_string_addref(git_metadata->property_commit); - zend_string_addref(git_metadata->property_repository); - add_git_info(git_metadata->property_commit, git_metadata->property_repository); -} - void replace_git_metadata(git_metadata_t *git_metadata, zend_string *commit_sha, zend_string *repository_url) { if (git_metadata->property_commit) { zend_string_release(git_metadata->property_commit); @@ -330,7 +324,7 @@ bool inject_from_git_dir() { git_metadata_t *git_metadata = zend_hash_find_ptr(&DDTRACE_G(git_metadata), cwd); if (git_metadata) { - use_cached_metadata(git_metadata); + add_git_info(git_metadata->property_commit, git_metadata->property_repository); zend_string_release(cwd); return true; } From 0d3f6773e8c3b805beb22c302d11d147bfa0b080 Mon Sep 17 00:00:00 2001 From: Laplie Anderson Date: Thu, 26 Sep 2024 11:40:58 -0400 Subject: [PATCH 087/103] Disable kubernetes injection tests (#2863) --- .gitlab-ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 53e28da7c7..d2ff93383b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -108,4 +108,13 @@ onboarding_tests_installer: parallel: matrix: - ONBOARDING_FILTER_WEBLOG: [test-app-php,test-app-php-container-83] - SCENARIO: SIMPLE_INSTALLER_AUTO_INJECTION \ No newline at end of file + SCENARIO: SIMPLE_INSTALLER_AUTO_INJECTION + +onboarding_tests_k8s_injection: + rules: + - when: never + parallel: + matrix: + - WEBLOG_VARIANT: + - dd-lib-php-init-test-83 + - dd-lib-php-init-test-alpine From 9d041954f90ff77b23f73c0108336279fbf04eb9 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Fri, 27 Sep 2024 15:21:31 +0200 Subject: [PATCH 088/103] Loader: disable the tracer in case of a potential incompatibility (extension/jit) (#2853) * Loader: don't inject the tracer if an incompatible extention is detected * Move the extension compatibility check in its own hook * Don't inject the tracer if Opcache JIT is enabled * Inject the tracer, but disabled in case of potential incompatibility * Simplify code by using strcasecmp * Fix memory leaks + add Valgrind support in tests * Use 'ddtrace.disable' instead of 'datadog.trace.enabled' * Fix memory leak in zend_abstract_interface/hook/hook.c --- .circleci/continue_config.yml | 25 +++- loader/compat_php.c | 11 ++ loader/compat_php.h | 1 + loader/dd_library_loader.c | 120 +++++++++++++++--- loader/php_dd_library_loader.h | 10 +- .../tests/functional/fixtures/opcache_jit.php | 23 ++++ loader/tests/functional/includes/autoload.php | 37 +++++- .../functional/test_incompatibility_jit.php | 85 +++++++++++++ .../test_incompatibility_xdebug.php | 28 ++++ loader/valgrind.supp | 18 +++ zend_abstract_interface/hook/hook.c | 5 +- 11 files changed, 337 insertions(+), 26 deletions(-) create mode 100644 loader/tests/functional/fixtures/opcache_jit.php create mode 100644 loader/tests/functional/test_incompatibility_jit.php create mode 100644 loader/tests/functional/test_incompatibility_xdebug.php create mode 100644 loader/valgrind.supp diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index a3dcce6447..98cc963e25 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3332,6 +3332,9 @@ jobs: switch_php_version: type: string default: none + use_valgrind: + type: boolean + default: false resource_class: type: string default: medium @@ -3354,6 +3357,18 @@ jobs: command: | set -xeuo pipefail + if [[ "<< parameters.php_major_minor >>" == "8.3" ]]; then + export XDEBUG_SO_NAME=xdebug-3.3.0.so + elif [[ "<< parameters.php_major_minor >>" == "8.2" ]]; then + export XDEBUG_SO_NAME=xdebug-3.2.2.so + elif [[ "<< parameters.php_major_minor >>" == "8.1" ]]; then + export XDEBUG_SO_NAME=xdebug-3.1.0.so + elif [[ "<< parameters.php_major_minor >>" == "8.0" ]]; then + export XDEBUG_SO_NAME=xdebug-3.0.0.so + elif [[ "<< parameters.php_major_minor >>" == "7.4" ]]; then + export XDEBUG_SO_NAME=xdebug-2.9.5.so + fi + rm -rf dd-library-php-ssi tar -xzf dd-library-php-ssi-*-linux.tar.gz export DD_LOADER_PACKAGE_PATH=${PWD}/dd-library-php-ssi @@ -3362,6 +3377,10 @@ jobs: mkdir -p modules cp ../dd-library-php-ssi/linux-gnu/loader/dd_library_loader.so modules/ ./bin/test.sh + if [[ "<< parameters.php_major_minor >>" == "8.3" ]]; then + true + <<# parameters.use_valgrind >>echo "Run with Valgrind" ; TEST_USE_VALGRIND=1 ./bin/test.sh<> + fi ./bin/check_glibc_version.sh "test_loader_alpine": @@ -3394,6 +3413,7 @@ jobs: set -xeuo pipefail apk add --no-cache curl-dev << parameters.alpine_packages >> + export XDEBUG_SO_NAME=xdebug.so rm -rf dd-library-php-ssi tar -xzf dd-library-php-ssi-*-linux.tar.gz @@ -4921,6 +4941,7 @@ workflows: name: "[Linux/x86_64] Loader PHP << matrix.php_major_minor >>-<< matrix.switch_php_version >>" docker_image: "datadog/dd-trace-ci:php-<< matrix.php_major_minor >>_buster" resource_class: "medium" + use_valgrind: true matrix: parameters: php_major_minor: @@ -4976,7 +4997,7 @@ workflows: name: "[Alpine/x86_64] Loader PHP Alpine << matrix.alpine_version >>" docker_image: "alpine:<< matrix.alpine_version >>" resource_class: "medium" - alpine_packages: "php83 php83-dev" + alpine_packages: "php83 php83-dev php83-pecl-xdebug" matrix: parameters: alpine_version: @@ -4990,7 +5011,7 @@ workflows: name: "[Alpine/aarch64] Loader PHP Alpine << matrix.alpine_version >>" docker_image: "alpine:<< matrix.alpine_version >>" resource_class: "arm.medium" - alpine_packages: "php83 php83-dev" + alpine_packages: "php83 php83-dev php83-pecl-xdebug" matrix: parameters: alpine_version: diff --git a/loader/compat_php.c b/loader/compat_php.c index 95bb842f11..07100312ec 100644 --- a/loader/compat_php.c +++ b/loader/compat_php.c @@ -190,3 +190,14 @@ zval *ddloader_zend_hash_update(HashTable *ht, zend_string *key, zval *pData) { return NULL; } + +// From PHP 8.0 +bool ddloader_zend_ini_parse_bool(zend_string *str) { + if ((ZSTR_LEN(str) == 4 && strcasecmp(ZSTR_VAL(str), "true") == 0) + || (ZSTR_LEN(str) == 3 && strcasecmp(ZSTR_VAL(str), "yes") == 0) + || (ZSTR_LEN(str) == 2 && strcasecmp(ZSTR_VAL(str), "on") == 0)) { + return true; + } else { + return atoi(ZSTR_VAL(str)) != 0; + } +} diff --git a/loader/compat_php.h b/loader/compat_php.h index 5973eeb335..6ac783cb62 100644 --- a/loader/compat_php.h +++ b/loader/compat_php.h @@ -10,5 +10,6 @@ zval *ddloader_zend_hash_set_bucket_key(int php_api_no, HashTable *ht, Bucket *b void ddloader_replace_zend_error_cb(int php_api_no); void ddloader_restore_zend_error_cb(); zval *ddloader_zend_hash_update(HashTable *ht, zend_string *key, zval *pData); +bool ddloader_zend_ini_parse_bool(zend_string *str); #endif /* DD_LIBRARY_LOADER_COMPAT_PHP_H */ diff --git a/loader/dd_library_loader.c b/loader/dd_library_loader.c index 93d0929409..c614bbb144 100644 --- a/loader/dd_library_loader.c +++ b/loader/dd_library_loader.c @@ -8,6 +8,7 @@ #include #include #include +#include
#include #include "compat_php.h" @@ -28,6 +29,8 @@ static bool injection_forced = false; # define OS_PATH "linux-gnu/" #endif +static void ddloader_telemetryf(telemetry_reason reason, const char *format, ...); + static char *ddtrace_pre_load_hook(void) { char *libddtrace_php; int res = asprintf(&libddtrace_php, "%s/%sloader/libddtrace_php.so", package_path, OS_PATH); @@ -60,6 +63,74 @@ static char *ddtrace_pre_load_hook(void) { return NULL; } +static bool ddloader_is_ext_loaded(const char *name) { + return zend_hash_str_find_ptr(&module_registry, name, strlen(name)) + || zend_get_extension(name) + ; +} + +static zval *ddloader_ini_get_configuration(const char *name, size_t name_len) { + HashTable *configuration_hash = php_ini_get_configuration_hash(); + if (!configuration_hash) { + return NULL; + } + zend_string *ini = ddloader_zend_string_init(php_api_no, name, name_len, 1); + zval *val = zend_hash_find(configuration_hash, ini); + ddloader_zend_string_release(php_api_no, ini); + + return val; +} + +static void ddloader_ini_set_configuration(const char *name, size_t name_len, const char *value, size_t value_len) { + HashTable *configuration_hash = php_ini_get_configuration_hash(); + if (!configuration_hash) { + return; + } + + zend_string *zstr_name = ddloader_zend_string_init(php_api_no, name, name_len, 1); + zend_string *zstr_value = ddloader_zend_string_init(php_api_no, value, value_len, 1); + + zval tmp; + ZVAL_STR(&tmp, zstr_value); + ddloader_zend_hash_update(configuration_hash, zstr_name, &tmp); + ddloader_zend_string_release(php_api_no, zstr_name); +} + +static bool ddloader_is_opcache_jit_enabled() { + // JIT is only PHP 8.0+ + if (php_api_no < 20200930 || !ddloader_is_ext_loaded("Zend OPcache")) { + return false; + } + + // opcache.enable = false (default: true) + zval *opcache_enable = ddloader_ini_get_configuration(ZEND_STRL("opcache.enable")); + if (opcache_enable && Z_TYPE_P(opcache_enable) == IS_STRING && !ddloader_zend_ini_parse_bool(Z_STR_P(opcache_enable))) { + return false; + } + if (strcmp("cli", sapi_module.name) == 0) { + // opcache.enable_cli = false (default: false) + zval *opcache_enable_cli = ddloader_ini_get_configuration(ZEND_STRL("opcache.enable_cli")); + if (!opcache_enable_cli || Z_TYPE_P(opcache_enable_cli) != IS_STRING || !ddloader_zend_ini_parse_bool(Z_STR_P(opcache_enable_cli))) { + return false; + } + } + if (php_api_no > 20230831) { // PHP > 8.3 (https://php.watch/versions/8.4/opcache-jit-ini-default-changes) + // opcache.jit == disable (default: disable) + zval *opcache_jit = ddloader_ini_get_configuration(ZEND_STRL("opcache.jit")); + if (!opcache_jit || Z_TYPE_P(opcache_jit) != IS_STRING || strcmp(Z_STRVAL_P(opcache_jit), "disable") == 0 || strcmp(Z_STRVAL_P(opcache_jit), "off") == 0) { + return false; + } + } else { + // opcache.jit_buffer_size = 0 (default: 0) + zval *opcache_jit_buffer_size = ddloader_ini_get_configuration(ZEND_STRL("opcache.jit_buffer_size")); + if (!opcache_jit_buffer_size || Z_TYPE_P(opcache_jit_buffer_size) != IS_STRING || strcmp(Z_STRVAL_P(opcache_jit_buffer_size), "0") == 0) { + return false; + } + } + + return true; +} + static void ddtrace_pre_minit_hook(void) { HashTable *configuration_hash = php_ini_get_configuration_hash(); if (configuration_hash) { @@ -69,13 +140,36 @@ static void ddtrace_pre_minit_hook(void) { } // Set 'datadog.trace.sources_path' setting - zend_string *name = ddloader_zend_string_init(php_api_no, ZEND_STRL("datadog.trace.sources_path"), 1); - zend_string *value = ddloader_zend_string_init(php_api_no, sources_path, strlen(sources_path), 1); + ddloader_ini_set_configuration(ZEND_STRL("datadog.trace.sources_path"), sources_path, strlen(sources_path)); free(sources_path); + } - zval tmp; - ZVAL_STR(&tmp, value); - ddloader_zend_hash_update(configuration_hash, name, &tmp); + // Load, but disable the tracer if runtime configuration is not safe for auto-injection + bool disable_tracer = false; + + char *incompatible_exts[] = {"Xdebug", "the ionCube PHP Loader", "newrelic", "blackfire", "pcov"}; + for (size_t i = 0; i < sizeof(incompatible_exts) / sizeof(incompatible_exts[0]); ++i) { + if (ddloader_is_ext_loaded(incompatible_exts[i])) { + if (force_load) { + LOG(WARN, "Potentially incompatible extension detected: %s. Ignoring as DD_INJECT_FORCE is enabled", incompatible_exts[i]); + } else { + LOG(WARN, "Potentially incompatible extension detected: %s. ddtrace will be disabled unless the environment DD_INJECT_FORCE is set to '1', 'true', 'yes' or 'on'", incompatible_exts[i]); + disable_tracer = true; + } + } + } + + if (ddloader_is_opcache_jit_enabled()) { + if (force_load) { + LOG(WARN, "OPcache JIT is enabled and may cause instability. Ignoring as DD_INJECT_FORCE is enabled"); + } else { + LOG(WARN, "OPcache JIT is enabled and may cause instability. ddtrace will be disabled unless the environment DD_INJECT_FORCE is set to '1', 'true', 'yes' or 'on'"); + disable_tracer = true; + } + } + + if (disable_tracer) { + ddloader_ini_set_configuration(ZEND_STRL("ddtrace.disable"), ZEND_STRL("1")); } } @@ -460,26 +554,19 @@ static int ddloader_load_extension(unsigned int php_api_no, char *module_build_i config->module_number = module_entry->module_number; config->version = (char *)module_entry->version; - return SUCCESS; + goto ok; abort_and_unload: LOG(INFO, "Unloading the library"); DL_UNLOAD(handle); abort: LOG(INFO, "Abort the loader"); +ok: free(ext_path); return SUCCESS; } -static void ddloader_strtolower(char *dest, char *src) { - while (*src) { - *dest = (char)tolower((int)*src); - ++dest; - ++src; - } -} - static bool ddloader_is_truthy(char *str) { if (!str) { return false; @@ -490,10 +577,7 @@ static bool ddloader_is_truthy(char *str) { return false; } - char lower[5] = {0}; - ddloader_strtolower(lower, str); - - return (strcmp(lower, "1") == 0 || strcmp(lower, "true") == 0 || strcmp(lower, "yes") == 0 || strcmp(lower, "on") == 0); + return (strcasecmp(str, "1") == 0 || strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0); } static inline void ddloader_configure() { diff --git a/loader/php_dd_library_loader.h b/loader/php_dd_library_loader.h index a8d0ed01b4..270f4c07c4 100644 --- a/loader/php_dd_library_loader.h +++ b/loader/php_dd_library_loader.h @@ -29,10 +29,12 @@ typedef enum { #define TELEMETRY(reason, format, ...) ddloader_telemetryf(reason, format, ##__VA_ARGS__); -#define DECLARE_INJECTED_EXT(name, dir, _pre_load_hook, _pre_minit_hook, deps) \ - { \ - .ext_name = name, .ext_dir = dir, .tmp_name = name "_injected", .tmp_deps = deps, .pre_load_hook = _pre_load_hook, .pre_minit_hook = _pre_minit_hook, .orig_module_startup_func = NULL, \ - .orig_module_deps = NULL, .orig_module_functions = NULL, .module_number = -1, .version = NULL \ +#define DECLARE_INJECTED_EXT(name, dir, _pre_load_hook, _pre_minit_hook, deps) \ + { \ + .ext_name = name, .ext_dir = dir, .tmp_name = name "_injected", .tmp_deps = deps, \ + .pre_load_hook = _pre_load_hook, .pre_minit_hook = _pre_minit_hook, \ + .orig_module_startup_func = NULL, .orig_module_deps = NULL, .orig_module_functions = NULL, \ + .module_number = -1, .version = NULL \ } typedef struct _injected_ext { diff --git a/loader/tests/functional/fixtures/opcache_jit.php b/loader/tests/functional/fixtures/opcache_jit.php new file mode 100644 index 0000000000..2106ad86cd --- /dev/null +++ b/loader/tests/functional/fixtures/opcache_jit.php @@ -0,0 +1,23 @@ +getTrace(); $file = $trace[0]['file'] ?: ''; $line = $trace[0]['line'] ?: ''; @@ -22,8 +22,20 @@ function runCLI($args, $useLoader = true, $env = [], $noIni = true) { if (!isset($_SERVER['DD_LOADER_PACKAGE_PATH'])) { $env[] = "DD_LOADER_PACKAGE_PATH=/home/circleci/app/dd-library-php"; } + + $valgrind = use_valgrind(); + if ($valgrind) { + $env[] = 'USE_ZEND_ALLOC=0'; + $env[] = 'ZEND_DONT_UNLOAD_MODULES=1'; + } + $cmd = implode(' ', $env).' '; + $valgrindLogFile = tempnam(sys_get_temp_dir(), 'valgrind_loader_test_');; + if ($valgrind) { + $cmd .= "valgrind -q --log-file=$valgrindLogFile --suppressions=./valgrind.supp --gen-suppressions=all --tool=memcheck --trace-children=no --undef-value-errors=no --vex-iropt-register-updates=allregs-at-mem-access --leak-check=full "; + } + $cmd .= 'php'; if ($noIni) { $cmd .= ' -n'; @@ -40,10 +52,22 @@ function runCLI($args, $useLoader = true, $env = [], $noIni = true) { } $res = exec($cmd, $output, $result_code); + + if ($valgrind) { + $valgrindOutput = file_get_contents($valgrindLogFile); + @unlink($valgrindLogFile); + } + if (!is_string($res) || $result_code !== 0) { throw new \Exception(sprintf('Error while executing "%s" (exit code: %d): \n\n', $cmd, $result_code, $output)); } + if ($valgrind) { + if (strlen($valgrindOutput)) { + throw new \Exception("Memory leak detected:\n".$valgrindOutput); + } + } + return implode("\n", $output); } @@ -55,6 +79,10 @@ function debug() { return (bool) (isset($_SERVER['DEBUG']) ? $_SERVER['DEBUG'] : false); } +function use_valgrind() { + return (bool) (isset($_SERVER['TEST_USE_VALGRIND']) ? $_SERVER['TEST_USE_VALGRIND'] : false); +} + function skip_if_php5() { if (PHP_MAJOR_VERSION <= 5) { echo "Skip: test is not compatible with PHP 5\n"; @@ -69,6 +97,13 @@ function skip_if_not_php5() { } } +function skip_if_not_php8() { + if (PHP_MAJOR_VERSION < 8) { + echo "Skip: test requires PHP 8\n"; + exit(0); + } +} + function skip_if_opcache_missing() { $output = runCLI('-dzend_extension=opcache -m'); if (strpos($output, 'Zend OPcache') === false) { diff --git a/loader/tests/functional/test_incompatibility_jit.php b/loader/tests/functional/test_incompatibility_jit.php new file mode 100644 index 0000000000..10cef519f0 --- /dev/null +++ b/loader/tests/functional/test_incompatibility_jit.php @@ -0,0 +1,85 @@ + "-dzend_extension=opcache -ddatadog.trace.cli_enabled=1", + "must_not_contain" => [$msg_disabled], + "must_contain" => [<< "-dzend_extension=opcache -dopcache.enable_cli=1 -ddatadog.trace.cli_enabled=1", + "must_not_contain" => [$msg_disabled], + "must_contain" => [<< "-dzend_extension=opcache -dopcache.enable_cli=1 -ddatadog.trace.cli_enabled=1 -dopcache.jit_buffer_size=32M", + "must_not_contain" => [], + "must_contain" => [ + $msg_disabled, + << "-dzend_extension=opcache -dopcache.enable_cli=1 -ddatadog.trace.cli_enabled=1 -dopcache.jit_buffer_size=32M", + "env" => ['DD_INJECT_FORCE=1'], + "must_not_contain" => [], + "must_contain" => [ + $msg_forced, + << + Memcheck:Leak + match-leak-kinds: possible + fun:malloc + fun:UnknownInlinedFun + fun:UnknownInlinedFun + fun:alloc + fun:alloc_impl + fun:allocate + fun:do_alloc + fun:new_uninitialized + fun:fallible_with_capacity + fun:prepare_resize + fun:resize_inner + fun:reserve_rehash_inner + ... +} diff --git a/zend_abstract_interface/hook/hook.c b/zend_abstract_interface/hook/hook.c index 20a5691360..ee9151a522 100644 --- a/zend_abstract_interface/hook/hook.c +++ b/zend_abstract_interface/hook/hook.c @@ -1226,7 +1226,10 @@ void zai_hook_rshutdown(void) { void zai_hook_gshutdown(void) { free(zai_hook_tls); } -void zai_hook_mshutdown(void) { zend_hash_destroy(&zai_hook_static); } /* }}} */ +void zai_hook_mshutdown(void) { + zend_hash_destroy(&zai_hook_static); + zend_hash_destroy(&zai_hook_static_inheritors); +} /* }}} */ /* {{{ */ zend_long zai_hook_install_resolved_generator(zend_function *function, From 0678d4f19868041cdbba56c7030820477cb140c3 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 1 Oct 2024 13:27:47 +0200 Subject: [PATCH 089/103] Allocate more memory for link_extension step (#2866) * Verify compilation actually properly passes Signed-off-by: Bob Weinand * Allocate more memory for link_extension step Signed-off-by: Bob Weinand --------- Signed-off-by: Bob Weinand --- .circleci/continue_config.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index ea6ddb1cef..ba43379720 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -3538,13 +3538,17 @@ jobs: name: Link sidecar and extension command: | sed -i 's/-export-symbols .*\/ddtrace\.sym/-Wl,--retain-symbols-file=ddtrace.sym/g' ddtrace_$(uname -m)<< parameters.libddtrace_suffix >>.ldflags + pids=() for archive in extensions_$(uname -m)/*.a; do ( gcc -shared -Wl,-whole-archive $archive -Wl,-no-whole-archive $(cat ddtrace_$(uname -m)<< parameters.libddtrace_suffix >>.ldflags) libddtrace_php_$(uname -m)<< parameters.libddtrace_suffix >>.a -Wl,-soname -Wl,ddtrace.so -o ${archive%.a}.so objcopy --compress-debug-sections ${archive%.a}.so ) & + pids+=($!) + done + for pid in "${pids[@]}"; do + wait $pid done - wait - persist_to_workspace: root: '.' paths: [ './extensions_*' ] @@ -4282,7 +4286,7 @@ workflows: - "Compile alpine x86_64 PHP 8.2" - "Compile alpine x86_64 PHP 8.3" libddtrace_suffix: "-alpine" - resource_class: "medium" + resource_class: "large" name: "Link x86_64 alpine" docker_image: "datadog/dd-trace-ci:php-compile-extension-alpine" - link_extension: @@ -4298,7 +4302,7 @@ workflows: - "Compile alpine aarch64 PHP 8.2" - "Compile alpine aarch64 PHP 8.3" libddtrace_suffix: "-alpine" - resource_class: "arm.medium" + resource_class: "arm.large" name: "Link aarch64 alpine" docker_image: "datadog/dd-trace-ci:php-compile-extension-alpine" - compile_extension_centos: From b8fde5771c3a690f90b662f3d15e44ea452b1f32 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Tue, 1 Oct 2024 13:29:14 +0200 Subject: [PATCH 090/103] Fix OpenTracing test flakiness (#2867) --- .../Frameworks/Custom/OpenTelemetry/index.php | 16 ++++++-------- tests/Frameworks/Custom/OpenTracing/index.php | 20 +++++++----------- tests/OpenTracing/InternalTelemetryTest.php | 21 ++++++++++++------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/tests/Frameworks/Custom/OpenTelemetry/index.php b/tests/Frameworks/Custom/OpenTelemetry/index.php index 10d3a610c3..b061f02ae1 100644 --- a/tests/Frameworks/Custom/OpenTelemetry/index.php +++ b/tests/Frameworks/Custom/OpenTelemetry/index.php @@ -4,13 +4,9 @@ require __DIR__ . '/vendor/autoload.php'; -try { - $tracerProvider = new TracerProvider(); - $tracer = $tracerProvider->getTracer('foo'); - $span = $tracer->spanBuilder('barbar') - ->startSpan() - ; - $span->end(); -} finally { - \dd_trace_internal_fn("finalize_telemetry"); -} +$tracerProvider = new TracerProvider(); +$tracer = $tracerProvider->getTracer('foo'); +$span = $tracer->spanBuilder('barbar') + ->startSpan() +; +$span->end(); diff --git a/tests/Frameworks/Custom/OpenTracing/index.php b/tests/Frameworks/Custom/OpenTracing/index.php index 9b2228f38b..4e98e06918 100644 --- a/tests/Frameworks/Custom/OpenTracing/index.php +++ b/tests/Frameworks/Custom/OpenTracing/index.php @@ -2,15 +2,11 @@ require __DIR__ . '/vendor/autoload.php'; -try { - $otTracer = new \DDTrace\OpenTracer1\Tracer(\DDTrace\GlobalTracer::get()); - $scope = $otTracer->startActiveSpan('web.request'); - $span = $scope->getSpan(); - $span->setTag('service.name', 'service_name'); - $span->setTag('resource.name', 'resource_name'); - $span->setTag('span.type', 'web'); - $span->setTag('http.method', $_SERVER['REQUEST_METHOD']); - $span->finish(); -} finally { - \dd_trace_internal_fn("finalize_telemetry"); -} +$otTracer = new \DDTrace\OpenTracer1\Tracer(\DDTrace\GlobalTracer::get()); +$scope = $otTracer->startActiveSpan('web.request'); +$span = $scope->getSpan(); +$span->setTag('service.name', 'service_name'); +$span->setTag('resource.name', 'resource_name'); +$span->setTag('span.type', 'web'); +$span->setTag('http.method', $_SERVER['REQUEST_METHOD']); +$span->finish(); diff --git a/tests/OpenTracing/InternalTelemetryTest.php b/tests/OpenTracing/InternalTelemetryTest.php index 197c7fc320..436ea93ffe 100644 --- a/tests/OpenTracing/InternalTelemetryTest.php +++ b/tests/OpenTracing/InternalTelemetryTest.php @@ -37,18 +37,25 @@ public function testInternalMetricWithOpenTracing() $this->executeCommand(); - $requests = $this->retrieveDumpedData($this->untilTelemetryRequest("spans_created")); + $requests = $this->retrieveDumpedData($this->untilTelemetryRequest("spans_created"), true); $payloads = $this->readTelemetryPayloads($requests); $isMetric = function (array $payload) { return 'generate-metrics' === $payload['request_type']; }; - $metrics = array_values(array_filter($payloads, $isMetric)); + $metricRequests = array_values(array_filter($payloads, $isMetric)); - $this->assertCount(1, $metrics); - $this->assertEquals("generate-metrics", $metrics[0]["request_type"]); - $this->assertEquals("tracers", $metrics[0]["payload"]["series"][0]["namespace"]); - $this->assertEquals("spans_created", $metrics[0]["payload"]["series"][0]["metric"]); - $this->assertEquals(["integration_name:opentracing"], $metrics[0]["payload"]["series"][0]["tags"]); + $this->assertCount(1, $metricRequests); + $this->assertEquals("generate-metrics", $metricRequests[0]["request_type"]); + + $metrics = []; + foreach ($metricRequests[0]['payload']['series'] as $serie) { + $metrics[$serie['metric']][] = $serie; + } + + $this->assertCount(1, $metrics['spans_created']); + $this->assertEquals("tracers", $metrics['spans_created'][0]["namespace"]); + $this->assertEquals("spans_created", $metrics['spans_created'][0]["metric"]); + $this->assertEquals(["integration_name:opentracing"], $metrics['spans_created'][0]["tags"]); } } From 925edd6e89a54bbc79d99e04256adeb2a1ed75f1 Mon Sep 17 00:00:00 2001 From: William Conti Date: Tue, 1 Oct 2024 14:46:36 -0400 Subject: [PATCH 091/103] fix env vars --- .circleci/continue_config.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index ba43379720..858035eb11 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -4892,10 +4892,8 @@ workflows: git checkout conti/fix-php-errors ./build.sh php run: | - export AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID - export AWS_SECRET_ACCESS_KEY=$SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY - export AWS_REGION=us-east-1 - export AWS_DEFAULT_REGION=us-east-1 + export SYSTEM_TESTS_AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID + export SYSTEM_TESTS_AWS_SECRET_ACCESS_KEY=$SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY cd system-tests git checkout conti/fix-php-errors DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh INTEGRATIONS @@ -4908,10 +4906,8 @@ workflows: git checkout conti/fix-php-errors ./build.sh php run: | - export AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID - export AWS_SECRET_ACCESS_KEY=$SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY - export AWS_REGION=us-east-1 - export AWS_DEFAULT_REGION=us-east-1 + export SYSTEM_TESTS_AWS_ACCESS_KEY_ID=$SYSTEM_TESTS_IDM_AWS_ACCESS_KEY_ID + export SYSTEM_TESTS_AWS_SECRET_ACCESS_KEY=$SYSTEM_TESTS_IDM_AWS_SECRET_ACCESS_KEY cd system-tests git checkout conti/fix-php-errors DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh CROSSED_TRACING_LIBRARIES From f87446c4d4269bb04e7b19216b063dcab0f8cbc3 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 2 Oct 2024 18:46:35 +0200 Subject: [PATCH 092/103] Dynamic configuration and live debugger (#2796) Exception replay, dynamic configuration and live-debugger method probe evaluation --- .circleci/continue_config.yml | 38 +- .gitignore | 2 +- Cargo.lock | 166 ++- Makefile | 21 +- appsec/src/extension/helper_process.c | 5 +- appsec/src/extension/helper_process.h | 2 +- cbindgen.toml | 14 +- components-rs/Cargo.toml | 9 + components-rs/common.h | 504 ++++++- components-rs/crashtracker.h | 8 +- components-rs/ddtrace.h | 61 + components-rs/lib.rs | 4 +- components-rs/live-debugger.h | 139 ++ components-rs/remote_config.rs | 404 +++++ components-rs/sidecar.h | 14 +- components-rs/sidecar.rs | 85 +- config.m4 | 13 +- config.w32 | 35 +- ddtrace.sym | 1 + docker-compose.yml | 23 +- .../alpine_compile_extension/base.Dockerfile | 5 +- dockerfiles/ci/bookworm/Dockerfile | 15 +- dockerfiles/ci/buster/Dockerfile | 14 +- dockerfiles/ci/buster/build-extensions.sh | 9 +- dockerfiles/ci/windows/basetools.Dockerfile | 8 + dockerfiles/ci/windows/vc14.Dockerfile | 3 +- dockerfiles/ci/windows/vs14_buildtools.exe | Bin 0 -> 3759592 bytes dockerfiles/ci/xfail_tests/7.0.list | 2 + dockerfiles/ci/xfail_tests/7.1.list | 2 + dockerfiles/ci/xfail_tests/7.2.list | 3 + dockerfiles/ci/xfail_tests/7.3.list | 4 + dockerfiles/ci/xfail_tests/7.4.list | 5 + dockerfiles/ci/xfail_tests/8.0.list | 6 + dockerfiles/ci/xfail_tests/8.1.list | 7 + dockerfiles/ci/xfail_tests/8.2.list | 8 + dockerfiles/ci/xfail_tests/8.3.list | 8 + dockerfiles/ci/xfail_tests/README.md | 15 + .../services/request-replayer/src/index.php | 3 + ext/collect_backtrace.c | 568 +++++++ ext/collect_backtrace.h | 13 + ext/compat_string.c | 14 +- ext/compat_string.h | 2 +- ext/compatibility.h | 183 ++- ext/configuration.c | 32 +- ext/configuration.h | 28 +- ext/ddtrace.c | 172 ++- ext/ddtrace.h | 36 +- ext/exception_serialize.c | 518 +++++++ ext/exception_serialize.h | 15 + ext/handlers_curl.c | 36 +- ext/handlers_exception.c | 166 ++- ext/hook/uhook.c | 6 +- ext/hook/uhook.h | 1 + ext/integrations/integrations.h | 76 +- ext/live_debugger.c | 1305 +++++++++++++++++ ext/live_debugger.h | 16 + ext/logging.c | 8 +- ext/logging.h | 4 +- ext/priority_sampling/priority_sampling.c | 33 +- ext/priority_sampling/priority_sampling.h | 2 + ext/random.c | 4 +- ext/random.h | 2 +- ext/remote_config.c | 116 ++ ext/remote_config.h | 13 + ext/serializer.c | 154 +- ext/serializer.h | 3 + ext/sidecar.c | 126 +- ext/sidecar.h | 20 +- ext/signals.c | 18 +- ext/span.c | 29 +- ext/span.h | 5 +- ext/telemetry.c | 24 +- ext/threads.c | 79 + ext/threads.h | 16 + libdatadog | 2 +- tests/OpenTracing/InternalTelemetryTest.php | 2 +- tests/ext/appsec/sca_flag_is_sent_01.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_02.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_03.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_04.phpt | 1 + tests/ext/appsec/sca_flag_is_sent_05.phpt | 1 + .../ext/background-sender/agent_headers.phpt | 1 + .../agent_headers_unix_domain_socket.phpt | 6 +- .../agent_sampling_sidecar.phpt | 2 +- tests/ext/crashtracker_segfault.phpt | 2 + ...f_class_or_function_exists_by_default.phpt | 2 +- tests/ext/dogstatsd/metrics_over_uds.phpt | 41 +- tests/ext/dogstatsd/metrics_uds.inc | 42 + tests/ext/extension_no_static_tls.phpt | 2 +- tests/ext/includes/request_replayer.inc | 6 +- .../ext/live-debugger/debugger_log_probe.phpt | 252 ++++ .../live-debugger/debugger_metric_probe.phpt | 48 + .../debugger_span_decoration_probe.phpt | 146 ++ .../live-debugger/debugger_span_probe.phpt | 39 + .../debugger_span_probe_class.phpt | 85 ++ .../live-debugger/exception-replay_001.phpt | 234 +++ .../live-debugger/exception-replay_002.phpt | 1033 +++++++++++++ tests/ext/live-debugger/live_debugger.inc | 143 ++ .../dynamic_config_auto_update.phpt | 44 + .../remote_config/dynamic_config_update.phpt | 70 + tests/ext/remote_config/remote_config.inc | 58 + ...f_class_or_function_exists_by_default.phpt | 2 +- tests/ext/segfault_backtrace_disabled.phpt | 3 + tests/ext/segfault_backtrace_enabled.phpt | 3 + tests/ext/telemetry/composer.phpt | 1 + tests/ext/telemetry/config.phpt | 1 + tests/ext/telemetry/disabled.phpt | 1 + tests/ext/telemetry/integration.phpt | 1 + tests/ext/telemetry/metrics_logs_created.phpt | 1 + .../ext/telemetry/metrics_spans_created.phpt | 1 + tests/ext/telemetry/simple.phpt | 1 + tests/ext/valgrind/suppressions.txt | 6 +- zend_abstract_interface/config/config.c | 3 +- zend_abstract_interface/config/config_ini.c | 2 +- zend_abstract_interface/config/config_ini.h | 4 +- .../exceptions/exceptions.c | 1 + .../exceptions/exceptions.h | 14 +- zend_abstract_interface/hook/hook.c | 40 +- zend_abstract_interface/hook/hook.h | 4 +- .../interceptor/php7/interceptor.c | 14 + .../interceptor/php7/interceptor.h | 6 + 121 files changed, 7363 insertions(+), 519 deletions(-) create mode 100644 components-rs/live-debugger.h create mode 100644 components-rs/remote_config.rs create mode 100644 dockerfiles/ci/windows/vs14_buildtools.exe create mode 100644 ext/collect_backtrace.c create mode 100644 ext/collect_backtrace.h create mode 100644 ext/exception_serialize.c create mode 100644 ext/exception_serialize.h create mode 100644 ext/live_debugger.c create mode 100644 ext/live_debugger.h create mode 100644 ext/remote_config.c create mode 100644 ext/remote_config.h create mode 100644 ext/threads.c create mode 100644 ext/threads.h create mode 100644 tests/ext/dogstatsd/metrics_uds.inc create mode 100644 tests/ext/live-debugger/debugger_log_probe.phpt create mode 100644 tests/ext/live-debugger/debugger_metric_probe.phpt create mode 100644 tests/ext/live-debugger/debugger_span_decoration_probe.phpt create mode 100644 tests/ext/live-debugger/debugger_span_probe.phpt create mode 100644 tests/ext/live-debugger/debugger_span_probe_class.phpt create mode 100644 tests/ext/live-debugger/exception-replay_001.phpt create mode 100644 tests/ext/live-debugger/exception-replay_002.phpt create mode 100644 tests/ext/live-debugger/live_debugger.inc create mode 100644 tests/ext/remote_config/dynamic_config_auto_update.phpt create mode 100644 tests/ext/remote_config/dynamic_config_update.phpt create mode 100644 tests/ext/remote_config/remote_config.inc diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 858035eb11..b996e3a43e 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -605,7 +605,7 @@ commands: -e DD_AGENT_HOST=127.0.0.1 \ -e DATADOG_HAVE_DEV_ENV=1 \ -e BASH_ENV=/home/circleci/bashenv/bash.sh \ - -e ASAN_OPTIONS=abort_on_error=1 \ + -e ASAN_OPTIONS="abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1" \ -e CIRCLE_SHA1 \ -e CIRCLE_BRANCH \ -e CODECOV_TOKEN \ @@ -820,6 +820,7 @@ jobs: executor: name: with_httpbin_and_request_replayer docker_image: << parameters.docker_image >> + resource_class: medium steps: - restore_cache: keys: @@ -960,7 +961,7 @@ jobs: name: Run tests command: | set -euo pipefail - DD_TRACE_SIDECAR_TRACE_SENDER=1 make test_c RUST_DEBUG_BUILD=1 PHPUNIT_OPTS="--log-junit test-results/php-unit/results.xml" TESTS=tests/ext/background-sender 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } + DD_TRACE_SIDECAR_TRACE_SENDER=1 _DD_DEBUG_SIDECAR_LOG_METHOD="file://$(pwd)/tmp/build_extension/tests/ext/sidecar.log" make test_c RUST_DEBUG_BUILD=1 PHPUNIT_OPTS="--log-junit test-results/php-unit/results.xml" TESTS=tests/ext/background-sender 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } - <<: *STEP_STORE_TEST_RESULTS - run: command: | @@ -1013,12 +1014,13 @@ jobs: name: Run tests command: | set -euo pipefail - make test_c_observer RUST_DEBUG_BUILD=1 PHPUNIT_OPTS="--log-junit test-results/php-unit/results.xml" 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } + _DD_DEBUG_SIDECAR_LOG_LEVEL=trace _DD_DEBUG_SIDECAR_LOG_METHOD="file://$(pwd)/tmp/build_extension/tests/ext/sidecar.log" make test_c_observer RUST_DEBUG_BUILD=1 PHPUNIT_OPTS="--log-junit test-results/php-unit/results.xml" 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } - <<: *STEP_STORE_TEST_RESULTS - run: command: | mkdir -p /tmp/artifacts/core_dumps - find tmp -name "core.*" | xargs -I % -n 1 cp % /tmp/artifacts/core_dumps + find /tmp -name "core.*" | xargs -I % -n 1 cp % /tmp/artifacts/core_dumps + cp -a tmp/build_extension/tests/ext /tmp/artifacts/tests when: on_fail - store_artifacts: path: /tmp/artifacts @@ -1088,6 +1090,10 @@ jobs: sudo useradd -u 3434 docker-circleci sudo chown -R docker-circleci . tests # - <<: *STEP_WAIT_REQUEST_REPLAYER + - run: + name: Set core pattern + command: | + sudo sh -c "echo '/tmp/core.%e.%p.%t' > /proc/sys/kernel/core_pattern" - run: name: Run tests command: | @@ -1095,6 +1101,7 @@ jobs: -e DDAGENT_HOSTNAME=127.0.0.1 \ -e DD_AGENT_HOST=127.0.0.1 \ -e DATADOG_HAVE_DEV_ENV=1 \ + -e ASAN_OPTIONS="abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1" \ -e PHP_VARIANT=<< parameters.switch_php_version >> \ -e PHPUNIT_OPTS="--log-junit test-results/php-unit/results.xml" \ << parameters.php_major_minor >>-buster-arm64 \ @@ -1107,12 +1114,12 @@ jobs: # name: Run tests # command: | # set -euo pipefail - # make << parameters.make_target >> PHPUNIT_OPTS="--log-junit test-results/php-unit/results.xml" 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } + # _DD_DEBUG_SIDECAR_LOG_LEVEL=trace _DD_DEBUG_SIDECAR_LOG_METHOD="file://$(pwd)/tmp/build_extension/tests/ext/sidecar.log" make << parameters.make_target >> PHPUNIT_OPTS="--log-junit test-results/php-unit/results.xml" 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } - <<: *STEP_STORE_TEST_RESULTS - run: command: | mkdir -p /tmp/artifacts/core_dumps - find tmp -name "core.*" | xargs -I % -n 1 cp % /tmp/artifacts/core_dumps + find /tmp -name "core.*" | xargs -I % -n 1 cp % /tmp/artifacts/core_dumps cp -a tmp/build_extension/tests/ext /tmp/artifacts/tests when: on_fail - store_artifacts: @@ -1152,7 +1159,7 @@ jobs: name: Run extension tests shell: powershell.exe command: | - docker exec php powershell.exe 'cd app; $env:_DD_DEBUG_SIDECAR_LOG_METHOD="""file://${pwd}\sidecar.log"""; C:\php\php.exe -n -d memory_limit=-1 -d output_buffering=0 run-tests.php -g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP --show-diff -p C:\php\php.exe -d "extension=${pwd}\x64\Release\php_ddtrace.dll" "${pwd}\tests\ext"' + docker exec php powershell.exe 'cd app; $env:_DD_DEBUG_SIDECAR_LOG_LEVEL=trace; $env:_DD_DEBUG_SIDECAR_LOG_METHOD="""file://${pwd}\sidecar.log"""; C:\php\php.exe -n -d memory_limit=-1 -d output_buffering=0 run-tests.php -g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP --show-diff -p C:\php\php.exe -d "extension=${pwd}\x64\Release\php_ddtrace.dll" "${pwd}\tests\ext"' # - run: # name: Install the extension and setup composer # shell: powershell.exe @@ -1198,6 +1205,10 @@ jobs: - source-v1-{{ .Branch }}-{{ .Revision }} - git_checkout - <<: *STEP_ATTACH_WORKSPACE + - run: + name: Set core pattern + command: | + sudo sh -c "echo '/tmp/core.%e.%p.%t' > /proc/sys/kernel/core_pattern" - setup_docker: docker_image: datadog/dd-trace-ci:php-<< parameters.php_major_minor >>_buster extra: with_httpbin_and_request_replayer @@ -1214,7 +1225,7 @@ jobs: command: | set -euo pipefail if [[ << parameters.switch_php_version >> == *asan* ]]; then export TEST_PHP_JUNIT=$(pwd)/asan-extension-test.xml; fi - make << parameters.make_target >> RUST_DEBUG_BUILD=1 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } + _DD_DEBUG_SIDECAR_LOG_LEVEL=trace _DD_DEBUG_SIDECAR_LOG_METHOD="file://$(pwd)/tmp/build_extension/tests/ext/sidecar.log" make << parameters.make_target >> RUST_DEBUG_BUILD=1 2>&1 | tee /dev/stderr | { ! grep -qe "=== Total [0-9]+ memory leaks detected ==="; } - when: # codecov uploader only on amd64 condition: @@ -1234,7 +1245,7 @@ jobs: - run: command: | mkdir -p /tmp/artifacts/core_dumps - find tmp -name "core.*" | xargs -I % -n 1 cp % /tmp/artifacts/core_dumps + find /tmp -name "core.*" | xargs -I % -n 1 cp % /tmp/artifacts/core_dumps cp -a tmp/build_extension/tests/ext /tmp/artifacts/tests when: on_fail - store_artifacts: @@ -1806,6 +1817,7 @@ jobs: executor: name: with_agent docker_image: << parameters.docker_image >> + resource_class: medium steps: - restore_cache: keys: @@ -1990,6 +2002,8 @@ jobs: - run: name: Run << parameters.ext_name >> integration tests with ext/<< parameters.ext_name >> as shared lib + leak detection command: | + export _DD_DEBUG_SIDECAR_LOG_LEVEL=trace + export _DD_DEBUG_SIDECAR_LOG_METHOD="file://$(pwd)/tmp/build_extension/tests/ext/sidecar.log" make test_extension_ci \ RUST_DEBUG_BUILD=1 \ BUILD_DIR=$(pwd)/tmp/build_extension \ @@ -2055,11 +2069,11 @@ jobs: command: | switch-php debug export DDTRACE_PKG_SO="/opt/datadog-php/extensions/ddtrace-$(php -i | awk '/^PHP[ \t]+API[ \t]+=>/ { print $NF }')-debug.so" - make run_tests TESTS="-d 'extension=$DDTRACE_PKG_SO'" + make run_tests TESTS="-d 'extension=$DDTRACE_PKG_SO' tests/ext" MAX_TEST_PARALLELISM=8 - run: name: Run phpt tests against build from source command: | - make test_c + make test_c MAX_TEST_PARALLELISM=8 - <<: *STEP_STORE_TEST_RESULTS - run: command: | @@ -5255,7 +5269,7 @@ workflows: - integration: requires: [ 'Prepare Code', 'Compile << matrix.php_major_minor >> extension for testing', 'Compile rust code for testing' ] - resource_class: medium+ + resource_class: large matrix: parameters: php_major_minor: diff --git a/.gitignore b/.gitignore index 74ae930873..1ee4690f12 100644 --- a/.gitignore +++ b/.gitignore @@ -46,7 +46,7 @@ modules/* php_test_results* results.bin run-tests.php -/target +/target* tests/ext/*.php tests/ext/*.out tests/ext/*.diff diff --git a/Cargo.lock b/Cargo.lock index 19df62f047..22ccbb5d89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1031,6 +1031,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6" +[[package]] +name = "constcat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5cd0c57ef83705837b1cb872c973eff82b070846d3e23668322b2c0f8246d0" + [[package]] name = "core-foundation" version = "0.9.4" @@ -1340,6 +1346,7 @@ dependencies = [ "spawn_worker", "tarpc", "tempfile", + "tinybytes", "tokio", "tokio-serde", "tokio-util 0.6.10", @@ -1357,6 +1364,43 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "datadog-live-debugger" +version = "0.0.1" +dependencies = [ + "anyhow", + "constcat", + "ddcommon 0.0.1", + "hyper 0.14.28", + "json", + "lazy_static", + "percent-encoding", + "regex", + "regex-automata 0.4.5", + "serde", + "serde_json", + "smallvec", + "sys-info", + "tokio", + "uuid", +] + +[[package]] +name = "datadog-live-debugger-ffi" +version = "0.0.1" +dependencies = [ + "build_common", + "datadog-live-debugger", + "ddcommon 0.0.1", + "ddcommon-ffi", + "log", + "percent-encoding", + "serde_json", + "tokio", + "tokio-util 0.7.10", + "uuid", +] + [[package]] name = "datadog-php-profiling" version = "0.0.0" @@ -1430,6 +1474,7 @@ dependencies = [ "anyhow", "base64 0.21.7", "datadog-dynamic-configuration", + "datadog-live-debugger", "datadog-trace-protobuf", "ddcommon 0.0.1", "futures", @@ -1464,6 +1509,7 @@ dependencies = [ "datadog-dynamic-configuration", "datadog-ipc", "datadog-ipc-macros", + "datadog-live-debugger", "datadog-remote-config", "datadog-sidecar-macros", "datadog-trace-normalization", @@ -1471,6 +1517,7 @@ dependencies = [ "datadog-trace-utils", "ddcommon 0.0.1", "ddtelemetry", + "dogstatsd-client", "futures", "hashbrown 0.12.3", "http 0.2.11", @@ -1498,6 +1545,7 @@ dependencies = [ "spawn_worker", "sys-info", "tempfile", + "tinybytes", "tokio", "tokio-util 0.7.10", "tracing", @@ -1506,6 +1554,7 @@ dependencies = [ "uuid", "winapi 0.3.9", "windows 0.51.1", + "windows-sys 0.52.0", "zwohash", ] @@ -1514,6 +1563,7 @@ name = "datadog-sidecar-ffi" version = "0.0.1" dependencies = [ "datadog-ipc", + "datadog-live-debugger", "datadog-remote-config", "datadog-sidecar", "datadog-trace-utils", @@ -1521,6 +1571,7 @@ dependencies = [ "ddcommon-ffi", "ddtelemetry", "ddtelemetry-ffi", + "dogstatsd-client", "hyper 0.14.28", "libc 0.2.154", "paste", @@ -1572,6 +1623,7 @@ dependencies = [ "criterion", "datadog-trace-normalization", "datadog-trace-protobuf", + "datadog-trace-utils", "ddcommon 0.0.1", "flate2", "futures", @@ -1587,6 +1639,7 @@ dependencies = [ "serde", "serde_json", "testcontainers", + "tinybytes", "tokio", ] @@ -1705,6 +1758,11 @@ dependencies = [ "cbindgen", "const-str", "datadog-crashtracker-ffi", + "datadog-dynamic-configuration", + "datadog-ipc", + "datadog-live-debugger", + "datadog-live-debugger-ffi", + "datadog-remote-config", "datadog-sidecar", "datadog-sidecar-ffi", "ddcommon 0.0.1", @@ -1712,10 +1770,14 @@ dependencies = [ "ddtelemetry", "ddtelemetry-ffi", "env_logger 0.10.2", + "itertools 0.11.0", "lazy_static", "log", "paste", + "regex", + "regex-automata 0.4.5", "serde", + "serde_json", "serde_with", "simd-json", "spawn_worker", @@ -1825,6 +1887,22 @@ dependencies = [ "serde_json", ] +[[package]] +name = "dogstatsd-client" +version = "0.0.1" +dependencies = [ + "anyhow", + "cadence", + "datadog-ddsketch", + "datadog-trace-normalization", + "datadog-trace-protobuf", + "ddcommon 0.0.1", + "http 0.2.11", + "hyper 0.14.28", + "serde", + "tracing", +] + [[package]] name = "dunce" version = "1.0.4" @@ -2325,6 +2403,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ + "ahash 0.8.11", "allocator-api2", ] @@ -2789,6 +2868,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -3793,6 +3878,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bitflags 2.4.2", + "lazy_static", + "num-traits", + "rand 0.8.5", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.8.4", + "unarray", +] + [[package]] name = "prost" version = "0.11.9" @@ -3997,6 +4098,15 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "raw-cpuid" version = "10.7.0" @@ -4441,9 +4551,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.196" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -4459,9 +4569,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -4470,11 +4580,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.118" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -4897,6 +5008,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-case" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d6cf5a7dffb3f9dceec8e6b8ca528d9bd71d36c9f074defb548ce161f598c0" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-macros" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26" +dependencies = [ + "cfg-if", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "testcontainers" version = "0.17.0" @@ -5021,6 +5154,19 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinybytes" +version = "0.0.1" +dependencies = [ + "once_cell", + "pretty_assertions", + "proptest", + "serde", + "serde_json", + "test-case", + "tinybytes", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -5170,6 +5316,8 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "futures-util", + "hashbrown 0.14.3", "pin-project-lite", "slab", "tokio", @@ -5374,6 +5522,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" version = "2.7.0" diff --git a/Makefile b/Makefile index ce0cb714a4..9f1e60e3f5 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ ASAN ?= $(shell ldd $(shell which php) 2>/dev/null | grep -q libasan && echo 1) SHELL = /bin/bash APPSEC_SOURCE_DIR = $(PROJECT_ROOT)/appsec/ BUILD_SUFFIX = extension -BUILD_DIR = $(PROJECT_ROOT)/tmp/build_$(BUILD_SUFFIX) +BUILD_DIR_NAME = tmp/build_$(BUILD_SUFFIX) +BUILD_DIR = $(PROJECT_ROOT)/$(BUILD_DIR_NAME) BUILD_DIR_APPSEC = $(BUILD_DIR)/appsec/ ZAI_BUILD_DIR = $(PROJECT_ROOT)/tmp/build_zai$(if $(ASAN),_asan) TEA_BUILD_DIR = $(PROJECT_ROOT)/tmp/build_tea$(if $(ASAN),_asan) @@ -30,6 +31,7 @@ QUIET_TESTS := ${CIRCLE_SHA1} RUST_DEBUG_BUILD ?= $(shell [ -n "${DD_TRACE_DOCKER_DEBUG}" ] && echo 1) EXTRA_CONFIGURE_OPTIONS ?= ASSUME_COMPILED := ${DD_TRACE_ASSUME_COMPILED} +MAX_TEST_PARALLELISM ?= $(shell nproc) VERSION := $(shell cat VERSION) @@ -38,11 +40,11 @@ INI_FILE := $(shell ASAN_OPTIONS=detect_leaks=0 php -i | awk -F"=>" '/Scan this RUN_TESTS_IS_PARALLEL ?= $(shell test $(PHP_MAJOR_MINOR) -ge 74 && echo 1) # shuffle parallel tests to evenly distribute test load, avoiding a batch of 32 tests being request-replayer tests -RUN_TESTS_CMD := REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJECT_ROOT) USE_TRACKED_ALLOC=1 php -n -d 'memory_limit=-1' $(BUILD_DIR)/run-tests.php $(if $(QUIET_TESTS),,-g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP) $(if $(ASAN), --asan) --show-diff -n -p $(shell which php) -q $(if $(RUN_TESTS_IS_PARALLEL), --shuffle -j$(shell nproc)) +RUN_TESTS_CMD := REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJECT_ROOT) USE_TRACKED_ALLOC=1 php -n -d 'memory_limit=-1' $(BUILD_DIR)/run-tests.php $(if $(QUIET_TESTS),,-g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP) $(if $(ASAN), --asan) --show-diff -n -p $(shell which php) -q $(if $(RUN_TESTS_IS_PARALLEL), --shuffle -j$(MAX_TEST_PARALLELISM)) C_FILES = $(shell find components components-rs ext src/dogstatsd zend_abstract_interface -name '*.c' -o -name '*.h' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_FILES = $(shell find tests/ext -name '*.php*' -o -name '*.inc' -o -name '*.json' -o -name 'CONFLICTS' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) -RUST_FILES = $(BUILD_DIR)/Cargo.toml $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{alloc,build-common,crashtracker,crashtracker-ffi,ddcommon,ddcommon-ffi,ddsketch,ddtelemetry,ddtelemetry-ffi,dynamic-configuration,ipc,remote-config,sidecar,sidecar-ffi,spawn_worker,tools/{cc_utils,sidecar_mockgen},trace-*,Cargo.toml} -type f \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/ipc/build.rs" -not -path "*/sidecar-ffi/build.rs") +RUST_FILES = $(BUILD_DIR)/Cargo.toml $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{alloc,build-common,crashtracker,crashtracker-ffi,ddcommon,ddcommon-ffi,ddsketch,ddtelemetry,ddtelemetry-ffi,dogstatsd-client,dynamic-configuration,ipc,live-debugger,live-debugger-ffi,remote-config,sidecar,sidecar-ffi,spawn_worker,tinybytes,tools/{cc_utils,sidecar_mockgen},trace-*,Cargo.toml} -type f \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/ipc/build.rs" -not -path "*/sidecar-ffi/build.rs") ALL_OBJECT_FILES = $(C_FILES) $(RUST_FILES) $(BUILD_DIR)/Makefile TEST_OPCACHE_FILES = $(shell find tests/opcache -name '*.php*' -o -name '.gitkeep' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_STUB_FILES = $(shell find tests/ext -type d -name 'stubs' -exec find '{}' -type f \; | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) @@ -151,10 +153,10 @@ install_appsec: install_all: install install_ini run_tests: $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php - $(RUN_TESTS_CMD) $(BUILD_DIR)/$(TESTS) + DD_TRACE_CLI_ENABLED=1 DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) $(TESTS) test_c: $(SO_FILE) $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php - $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_CLI_ENABLED=1 DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) $(BUILD_DIR)/$(TESTS) + $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_CLI_ENABLED=1 DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) $(BUILD_DIR)/$(subst $(BUILD_DIR_NAME)/,,$(TESTS)) test_c_coverage: dist_clean DD_TRACE_DOCKER_DEBUG=1 EXTRA_CFLAGS="-fprofile-arcs -ftest-coverage" $(MAKE) test_c || exit 0 @@ -402,9 +404,9 @@ clang_format_fix: cbindgen: remove_cbindgen generate_cbindgen remove_cbindgen: - rm -f components-rs/ddtrace.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h + rm -f components-rs/ddtrace.h components-rs/live-debugger.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h -generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h +generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h components-rs/live-debugger.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h ( \ $(command rustup && echo run nightly --) cbindgen --crate ddtrace-php \ --config cbindgen.toml \ @@ -413,6 +415,9 @@ generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h componen $(command rustup && echo run nightly --) cbindgen --crate ddcommon-ffi \ --config ddcommon-ffi/cbindgen.toml \ --output $(PROJECT_ROOT)/components-rs/common.h; \ + $(command rustup && echo run nightly --) cbindgen --crate datadog-live-debugger-ffi \ + --config live-debugger-ffi/cbindgen.toml \ + --output $(PROJECT_ROOT)/components-rs/live-debugger.h; \ $(command rustup && echo run nightly --) cbindgen --crate ddtelemetry-ffi \ --config ddtelemetry-ffi/cbindgen.toml \ --output $(PROJECT_ROOT)/components-rs/telemetry.h; \ @@ -426,7 +431,7 @@ generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h componen mkdir -pv "$(BUILD_DIR)"; \ export CARGO_TARGET_DIR="$(BUILD_DIR)/target"; \ fi; \ - cargo run -p tools -- $(PROJECT_ROOT)/components-rs/common.h $(PROJECT_ROOT)/components-rs/ddtrace.h $(PROJECT_ROOT)/components-rs/telemetry.h $(PROJECT_ROOT)/components-rs/sidecar.h $(PROJECT_ROOT)/components-rs/crashtracker.h \ + cargo run -p tools -- $(PROJECT_ROOT)/components-rs/common.h $(PROJECT_ROOT)/components-rs/ddtrace.h $(PROJECT_ROOT)/components-rs/live-debugger.h $(PROJECT_ROOT)/components-rs/telemetry.h $(PROJECT_ROOT)/components-rs/sidecar.h $(PROJECT_ROOT)/components-rs/crashtracker.h \ ) cbindgen_binary: diff --git a/appsec/src/extension/helper_process.c b/appsec/src/extension/helper_process.c index 8d0e166723..06f7a76443 100644 --- a/appsec/src/extension/helper_process.c +++ b/appsec/src/extension/helper_process.c @@ -130,9 +130,10 @@ dd_conn *nullable dd_helper_mgr_cur_conn(void) } // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -bool dd_on_runtime_path_update(zval *nullable old_val, zval *nonnull new_val) +bool dd_on_runtime_path_update(zval *nullable old_val, zval *nonnull new_val, zend_string *nonnull new_str) { UNUSED(old_val); + UNUSED(new_str); uid_t uid = getuid(); char *base = Z_STRVAL_P(new_val); @@ -173,7 +174,7 @@ static void _read_settings() zval runtime_path; ZVAL_STR(&runtime_path, get_DD_APPSEC_HELPER_RUNTIME_PATH()); - dd_on_runtime_path_update(NULL, &runtime_path); + dd_on_runtime_path_update(NULL, &runtime_path, NULL); } __attribute__((visibility("default"))) void dd_appsec_maybe_enable_helper( diff --git a/appsec/src/extension/helper_process.h b/appsec/src/extension/helper_process.h index 750bf70f44..ebf6b989eb 100644 --- a/appsec/src/extension/helper_process.h +++ b/appsec/src/extension/helper_process.h @@ -28,6 +28,6 @@ dd_conn *nullable dd_helper_mgr_cur_conn(void); void dd_helper_close_conn(void); bool dd_on_runtime_path_update( - zval *nullable old_value, zval *nonnull new_value); + zval *nullable old_value, zval *nonnull new_value, zend_string *nonnull new_str); #endif // DD_HELPER_MGR_H diff --git a/cbindgen.toml b/cbindgen.toml index 7bc3f5b99e..16bb97fa53 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -25,4 +25,16 @@ rename_variants = "ScreamingSnakeCase" [parse] parse_deps = true -include = ["ddcommon", "ddtelemetry", "ddcommon-ffi", "ddtelemetry-ffi", "datadog-crashtracker-ffi", "datadog-sidecar", "datadog-ipc", "uuid"] +include = [ + "ddcommon", + "ddtelemetry", + "ddtelemetry-ffi", + "ddcommon-ffi", + "datadog-crashtracker-ffi", + "datadog-sidecar", + "datadog-ipc", + "datadog-live-debugger", + "datadog-live-debugger-ffi", + "datadog-remote-config", + "uuid" +] diff --git a/components-rs/Cargo.toml b/components-rs/Cargo.toml index 8b58a28dd0..824e09eeb5 100644 --- a/components-rs/Cargo.toml +++ b/components-rs/Cargo.toml @@ -12,12 +12,18 @@ ddcommon = { path = "../libdatadog/ddcommon" } ddcommon-ffi = { path = "../libdatadog/ddcommon-ffi", default-features = false } ddtelemetry = { path = "../libdatadog/ddtelemetry" } ddtelemetry-ffi = { path = "../libdatadog/ddtelemetry-ffi", default-features = false } +datadog-dynamic-configuration = { path = "../libdatadog/dynamic-configuration" } +datadog-live-debugger = { path = "../libdatadog/live-debugger" } +datadog-live-debugger-ffi = { path = "../libdatadog/live-debugger-ffi", default-features = false } +datadog-ipc = { path = "../libdatadog/ipc" } +datadog-remote-config = { path = "../libdatadog/remote-config" } datadog-sidecar = { path = "../libdatadog/sidecar" } datadog-sidecar-ffi = { path = "../libdatadog/sidecar-ffi" } datadog-crashtracker-ffi = { path = "../libdatadog/crashtracker-ffi", default-features = false, features = ["collector"] } spawn_worker = { path = "../libdatadog/spawn_worker" } anyhow = { version = "1.0" } const-str = "0.5.6" +itertools = "0.11.0" serde = "1.0.196" simd-json = "0.13.8" serde_with = "3.6.0" @@ -34,6 +40,9 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [ "fmt", "env-filter", ] } +serde_json = "1.0.113" +regex = "1.10.5" +regex-automata = "0.4.5" [build-dependencies] cbindgen = "0.26" diff --git a/components-rs/common.h b/components-rs/common.h index e0e28bea30..7881c5feef 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -73,7 +73,9 @@ typedef struct ddog_Error { typedef struct ddog_Slice_CChar { /** - * Must be non-null and suitably aligned for the underlying type. + * Should be non-null and suitably aligned for the underlying type. It is + * allowed but not recommended for the pointer to be null when the len is + * zero. */ const char *ptr; /** @@ -257,6 +259,8 @@ typedef struct ddog_Vec_Tag_ParseResult { #define ddog_LOG_ONCE (1 << 3) +#define ddog_MultiTargetFetcher_DEFAULT_CLIENTS_LIMIT 100 + typedef enum ddog_ConfigurationOrigin { DDOG_CONFIGURATION_ORIGIN_ENV_VAR, DDOG_CONFIGURATION_ORIGIN_CODE, @@ -265,6 +269,17 @@ typedef enum ddog_ConfigurationOrigin { DDOG_CONFIGURATION_ORIGIN_DEFAULT, } ddog_ConfigurationOrigin; +typedef enum ddog_EvaluateAt { + DDOG_EVALUATE_AT_ENTRY, + DDOG_EVALUATE_AT_EXIT, +} ddog_EvaluateAt; + +typedef enum ddog_InBodyLocation { + DDOG_IN_BODY_LOCATION_NONE, + DDOG_IN_BODY_LOCATION_START, + DDOG_IN_BODY_LOCATION_END, +} ddog_InBodyLocation; + typedef enum ddog_Log { DDOG_LOG_ERROR = 1, DDOG_LOG_WARN = 2, @@ -279,6 +294,13 @@ typedef enum ddog_Log { DDOG_LOG_HOOK_TRACE = (5 | (4 << 4)), } ddog_Log; +typedef enum ddog_MetricKind { + DDOG_METRIC_KIND_COUNT, + DDOG_METRIC_KIND_GAUGE, + DDOG_METRIC_KIND_HISTOGRAM, + DDOG_METRIC_KIND_DISTRIBUTION, +} ddog_MetricKind; + typedef enum ddog_MetricNamespace { DDOG_METRIC_NAMESPACE_TRACERS, DDOG_METRIC_NAMESPACE_PROFILERS, @@ -293,11 +315,75 @@ typedef enum ddog_MetricNamespace { DDOG_METRIC_NAMESPACE_SIDECAR, } ddog_MetricNamespace; +typedef enum ddog_ProbeStatus { + DDOG_PROBE_STATUS_RECEIVED, + DDOG_PROBE_STATUS_INSTALLED, + DDOG_PROBE_STATUS_EMITTING, + DDOG_PROBE_STATUS_ERROR, + DDOG_PROBE_STATUS_BLOCKED, + DDOG_PROBE_STATUS_WARNING, +} ddog_ProbeStatus; + +typedef enum ddog_RemoteConfigCapabilities { + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_ACTIVATION = 1, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_IP_BLOCKING = 2, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_DD_RULES = 3, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_EXCLUSIONS = 4, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_REQUEST_BLOCKING = 5, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RESPONSE_BLOCKING = 6, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_USER_BLOCKING = 7, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_RULES = 8, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_BLOCKING_RESPONSE = 9, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_TRUSTED_IPS = 10, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_API_SECURITY_SAMPLE_RATE = 11, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_SAMPLE_RATE = 12, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_LOGS_INJECTION = 13, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_HTTP_HEADER_TAGS = 14, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_CUSTOM_TAGS = 15, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_PROCESSOR_OVERRIDES = 16, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_DATA_SCANNERS = 17, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_EXCLUSION_DATA = 18, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_ENABLED = 19, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_DATA_STREAMS_ENABLED = 20, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SQLI = 21, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_LFI = 22, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SSRF = 23, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SHI = 24, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_XXE = 25, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_RCE = 26, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_NOSQLI = 27, + DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_XSS = 28, + DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_SAMPLE_RULES = 29, + DDOG_REMOTE_CONFIG_CAPABILITIES_CSM_ACTIVATION = 30, +} ddog_RemoteConfigCapabilities; + +typedef enum ddog_RemoteConfigProduct { + DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, + DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, +} ddog_RemoteConfigProduct; + +typedef enum ddog_SpanProbeTarget { + DDOG_SPAN_PROBE_TARGET_ACTIVE, + DDOG_SPAN_PROBE_TARGET_ROOT, +} ddog_SpanProbeTarget; + +typedef struct ddog_DebuggerPayload ddog_DebuggerPayload; + +typedef struct ddog_DslString ddog_DslString; + /** * `InstanceId` is a structure that holds session and runtime identifiers. */ typedef struct ddog_InstanceId ddog_InstanceId; +typedef struct ddog_MaybeShmLimiter ddog_MaybeShmLimiter; + +typedef struct ddog_ProbeCondition ddog_ProbeCondition; + +typedef struct ddog_ProbeValue ddog_ProbeValue; + +typedef struct ddog_RemoteConfigState ddog_RemoteConfigState; + typedef struct ddog_SidecarActionsBuffer ddog_SidecarActionsBuffer; /** @@ -311,6 +397,360 @@ typedef struct ddog_SidecarActionsBuffer ddog_SidecarActionsBuffer; */ typedef struct ddog_SidecarTransport ddog_SidecarTransport; +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_CChar { + const char *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_CChar; + +typedef enum ddog_IntermediateValue_Tag { + DDOG_INTERMEDIATE_VALUE_STRING, + DDOG_INTERMEDIATE_VALUE_NUMBER, + DDOG_INTERMEDIATE_VALUE_BOOL, + DDOG_INTERMEDIATE_VALUE_NULL, + DDOG_INTERMEDIATE_VALUE_REFERENCED, +} ddog_IntermediateValue_Tag; + +typedef struct ddog_IntermediateValue { + ddog_IntermediateValue_Tag tag; + union { + struct { + ddog_CharSlice string; + }; + struct { + double number; + }; + struct { + bool bool_; + }; + struct { + const void *referenced; + }; + }; +} ddog_IntermediateValue; + +typedef struct ddog_VoidCollection { + intptr_t count; + const void *elements; + void (*free)(struct ddog_VoidCollection); +} ddog_VoidCollection; + +typedef struct ddog_Evaluator { + bool (*equals)(void*, struct ddog_IntermediateValue, struct ddog_IntermediateValue); + bool (*greater_than)(void*, struct ddog_IntermediateValue, struct ddog_IntermediateValue); + bool (*greater_or_equals)(void*, struct ddog_IntermediateValue, struct ddog_IntermediateValue); + const void *(*fetch_identifier)(void*, const ddog_CharSlice*); + const void *(*fetch_index)(void*, const void*, struct ddog_IntermediateValue); + const void *(*fetch_nested)(void*, const void*, struct ddog_IntermediateValue); + uintptr_t (*length)(void*, const void*); + struct ddog_VoidCollection (*try_enumerate)(void*, const void*); + ddog_CharSlice (*stringify)(void*, const void*); + ddog_CharSlice (*get_string)(void*, const void*); + intptr_t (*convert_index)(void*, const void*); + bool (*instanceof)(void*, const void*, const ddog_CharSlice*); +} ddog_Evaluator; + +typedef struct ddog_CharSliceVec { + const ddog_CharSlice *strings; + uintptr_t string_count; +} ddog_CharSliceVec; + +typedef enum ddog_Option_CharSlice_Tag { + DDOG_OPTION_CHAR_SLICE_SOME_CHAR_SLICE, + DDOG_OPTION_CHAR_SLICE_NONE_CHAR_SLICE, +} ddog_Option_CharSlice_Tag; + +typedef struct ddog_Option_CharSlice { + ddog_Option_CharSlice_Tag tag; + union { + struct { + ddog_CharSlice some; + }; + }; +} ddog_Option_CharSlice; + +typedef struct ddog_ProbeTarget { + ddog_CharSlice type_name; + ddog_CharSlice method_name; + ddog_CharSlice source_file; + struct ddog_Option_CharSlice signature; + const uint32_t *lines; + uint32_t lines_count; + enum ddog_InBodyLocation in_body_location; +} ddog_ProbeTarget; + +typedef struct ddog_MetricProbe { + enum ddog_MetricKind kind; + ddog_CharSlice name; + const struct ddog_ProbeValue *value; +} ddog_MetricProbe; + +typedef struct ddog_CaptureConfiguration { + uint32_t max_reference_depth; + uint32_t max_collection_size; + uint32_t max_length; + uint32_t max_field_count; +} ddog_CaptureConfiguration; + +typedef struct ddog_LogProbe { + const struct ddog_DslString *segments; + const struct ddog_ProbeCondition *when; + const struct ddog_CaptureConfiguration *capture; + bool capture_snapshot; + uint32_t sampling_snapshots_per_second; +} ddog_LogProbe; + +typedef struct ddog_Tag { + ddog_CharSlice name; + const struct ddog_DslString *value; +} ddog_Tag; + +typedef struct ddog_SpanProbeTag { + struct ddog_Tag tag; + bool next_condition; +} ddog_SpanProbeTag; + +typedef struct ddog_SpanDecorationProbe { + enum ddog_SpanProbeTarget target; + const struct ddog_ProbeCondition *const *conditions; + const struct ddog_SpanProbeTag *span_tags; + uintptr_t span_tags_num; +} ddog_SpanDecorationProbe; + +typedef enum ddog_ProbeType_Tag { + DDOG_PROBE_TYPE_METRIC, + DDOG_PROBE_TYPE_LOG, + DDOG_PROBE_TYPE_SPAN, + DDOG_PROBE_TYPE_SPAN_DECORATION, +} ddog_ProbeType_Tag; + +typedef struct ddog_ProbeType { + ddog_ProbeType_Tag tag; + union { + struct { + struct ddog_MetricProbe metric; + }; + struct { + struct ddog_LogProbe log; + }; + struct { + struct ddog_SpanDecorationProbe span_decoration; + }; + }; +} ddog_ProbeType; + +typedef struct ddog_Probe { + ddog_CharSlice id; + uint64_t version; + ddog_CharSlice language; + struct ddog_CharSliceVec tags; + struct ddog_ProbeTarget target; + enum ddog_EvaluateAt evaluate_at; + struct ddog_ProbeType probe; + ddog_CharSlice diagnostic_msg; + enum ddog_ProbeStatus status; + ddog_CharSlice status_msg; + ddog_CharSlice status_exception; + ddog_CharSlice status_stacktrace; +} ddog_Probe; + +typedef struct ddog_LiveDebuggerCallbacks { + int64_t (*set_probe)(struct ddog_Probe probe, const struct ddog_MaybeShmLimiter *limiter); + void (*remove_probe)(int64_t id); +} ddog_LiveDebuggerCallbacks; + +typedef struct ddog_LiveDebuggerSetup { + const struct ddog_Evaluator *evaluator; + struct ddog_LiveDebuggerCallbacks callbacks; +} ddog_LiveDebuggerSetup; + +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_DebuggerPayload { + const struct ddog_DebuggerPayload *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_DebuggerPayload; + +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_RemoteConfigProduct { + const enum ddog_RemoteConfigProduct *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_RemoteConfigProduct; + +typedef struct ddog_Vec_RemoteConfigProduct ddog_VecRemoteConfigProduct; + +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_RemoteConfigCapabilities { + const enum ddog_RemoteConfigCapabilities *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_RemoteConfigCapabilities; + +typedef struct ddog_Vec_RemoteConfigCapabilities ddog_VecRemoteConfigCapabilities; + +typedef struct ddog_DebuggerCapture ddog_DebuggerCapture; +typedef struct ddog_DebuggerValue ddog_DebuggerValue; + + +#define ddog_EVALUATOR_RESULT_UNDEFINED (const void*)0 + +#define ddog_EVALUATOR_RESULT_INVALID (const void*)-1 + +#define ddog_EVALUATOR_RESULT_REDACTED (const void*)-2 + +typedef enum ddog_DebuggerType { + DDOG_DEBUGGER_TYPE_DIAGNOSTICS, + DDOG_DEBUGGER_TYPE_LOGS, +} ddog_DebuggerType; + +typedef enum ddog_FieldType { + DDOG_FIELD_TYPE_STATIC, + DDOG_FIELD_TYPE_ARG, + DDOG_FIELD_TYPE_LOCAL, +} ddog_FieldType; + +typedef struct ddog_Entry ddog_Entry; + +typedef struct ddog_HashMap_CowStr__Value ddog_HashMap_CowStr__Value; + +typedef struct ddog_InternalIntermediateValue ddog_InternalIntermediateValue; + +typedef struct ddog_SenderHandle ddog_SenderHandle; + +typedef struct ddog_SnapshotEvaluationError ddog_SnapshotEvaluationError; + +typedef struct ddog_String ddog_String; + +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_SnapshotEvaluationError { + const struct ddog_SnapshotEvaluationError *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_SnapshotEvaluationError; + +typedef enum ddog_ConditionEvaluationResult_Tag { + DDOG_CONDITION_EVALUATION_RESULT_SUCCESS, + DDOG_CONDITION_EVALUATION_RESULT_FAILURE, + DDOG_CONDITION_EVALUATION_RESULT_ERROR, +} ddog_ConditionEvaluationResult_Tag; + +typedef struct ddog_ConditionEvaluationResult { + ddog_ConditionEvaluationResult_Tag tag; + union { + struct { + struct ddog_Vec_SnapshotEvaluationError *error; + }; + }; +} ddog_ConditionEvaluationResult; + +typedef enum ddog_ValueEvaluationResult_Tag { + DDOG_VALUE_EVALUATION_RESULT_SUCCESS, + DDOG_VALUE_EVALUATION_RESULT_ERROR, +} ddog_ValueEvaluationResult_Tag; + +typedef struct ddog_ValueEvaluationResult { + ddog_ValueEvaluationResult_Tag tag; + union { + struct { + struct ddog_InternalIntermediateValue *success; + }; + struct { + struct ddog_Vec_SnapshotEvaluationError *error; + }; + }; +} ddog_ValueEvaluationResult; + +typedef struct ddog_FilterList { + struct ddog_CharSliceVec package_prefixes; + struct ddog_CharSliceVec classes; +} ddog_FilterList; + +typedef struct ddog_ServiceConfiguration { + ddog_CharSlice id; + struct ddog_FilterList allow; + struct ddog_FilterList deny; + uint32_t sampling_snapshots_per_second; +} ddog_ServiceConfiguration; + +typedef enum ddog_LiveDebuggingData_Tag { + DDOG_LIVE_DEBUGGING_DATA_NONE, + DDOG_LIVE_DEBUGGING_DATA_PROBE, + DDOG_LIVE_DEBUGGING_DATA_SERVICE_CONFIGURATION, +} ddog_LiveDebuggingData_Tag; + +typedef struct ddog_LiveDebuggingData { + ddog_LiveDebuggingData_Tag tag; + union { + struct { + struct ddog_Probe probe; + }; + struct { + struct ddog_ServiceConfiguration service_configuration; + }; + }; +} ddog_LiveDebuggingData; + +typedef struct ddog_LiveDebuggingParseResult { + struct ddog_LiveDebuggingData data; + struct ddog_LiveDebuggingData *opaque_data; +} ddog_LiveDebuggingParseResult; + +typedef struct ddog_HashMap_CowStr__Value ddog_Fields; + +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_DebuggerValue { + const ddog_DebuggerValue *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_DebuggerValue; + +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_Entry { + const struct ddog_Entry *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_Entry; + +typedef struct ddog_CaptureValue { + ddog_CharSlice type; + ddog_CharSlice value; + ddog_Fields *fields; + struct ddog_Vec_DebuggerValue elements; + struct ddog_Vec_Entry entries; + bool is_null; + bool truncated; + ddog_CharSlice not_captured_reason; + ddog_CharSlice size; +} ddog_CaptureValue; + +typedef struct ddog_OwnedCharSlice { + ddog_CharSlice slice; + void (*free)(ddog_CharSlice); +} ddog_OwnedCharSlice; + typedef enum ddog_LogLevel { DDOG_LOG_LEVEL_ERROR, DDOG_LOG_LEVEL_WARN, @@ -377,46 +817,6 @@ typedef struct ddog_ContextKey { enum ddog_MetricType _1; } ddog_ContextKey; -#define ddog_MultiTargetFetcher_DEFAULT_CLIENTS_LIMIT 100 - -typedef enum ddog_RemoteConfigCapabilities { - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_ACTIVATION = 1, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_IP_BLOCKING = 2, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_DD_RULES = 3, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_EXCLUSIONS = 4, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_REQUEST_BLOCKING = 5, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RESPONSE_BLOCKING = 6, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_USER_BLOCKING = 7, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_RULES = 8, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_BLOCKING_RESPONSE = 9, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_TRUSTED_IPS = 10, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_API_SECURITY_SAMPLE_RATE = 11, - DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_SAMPLE_RATE = 12, - DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_LOGS_INJECTION = 13, - DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_HTTP_HEADER_TAGS = 14, - DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_CUSTOM_TAGS = 15, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_PROCESSOR_OVERRIDES = 16, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_CUSTOM_DATA_SCANNERS = 17, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_EXCLUSION_DATA = 18, - DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_ENABLED = 19, - DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_DATA_STREAMS_ENABLED = 20, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SQLI = 21, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_LFI = 22, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SSRF = 23, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_SHI = 24, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_XXE = 25, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_RCE = 26, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_NOSQLI = 27, - DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_RASP_XSS = 28, - DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_SAMPLE_RULES = 29, - DDOG_REMOTE_CONFIG_CAPABILITIES_CSM_ACTIVATION = 30, -} ddog_RemoteConfigCapabilities; - -typedef enum ddog_RemoteConfigProduct { - DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, - DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, -} ddog_RemoteConfigProduct; - typedef struct ddog_AgentRemoteConfigReader ddog_AgentRemoteConfigReader; typedef struct ddog_AgentRemoteConfigWriter_ShmHandle ddog_AgentRemoteConfigWriter_ShmHandle; @@ -461,6 +861,7 @@ typedef enum ddog_crasht_DemangleOptions { typedef enum ddog_crasht_NormalizedAddressTypes { DDOG_CRASHT_NORMALIZED_ADDRESS_TYPES_NONE = 0, DDOG_CRASHT_NORMALIZED_ADDRESS_TYPES_ELF, + DDOG_CRASHT_NORMALIZED_ADDRESS_TYPES_PDB, } ddog_crasht_NormalizedAddressTypes; /** @@ -530,7 +931,9 @@ typedef struct ddog_crasht_Result { typedef struct ddog_crasht_Slice_CharSlice { /** - * Must be non-null and suitably aligned for the underlying type. + * Should be non-null and suitably aligned for the underlying type. It is + * allowed but not recommended for the pointer to be null when the len is + * zero. */ const ddog_CharSlice *ptr; /** @@ -560,7 +963,9 @@ typedef struct ddog_crasht_EnvVar { typedef struct ddog_crasht_Slice_EnvVar { /** - * Must be non-null and suitably aligned for the underlying type. + * Should be non-null and suitably aligned for the underlying type. It is + * allowed but not recommended for the pointer to be null when the len is + * zero. */ const struct ddog_crasht_EnvVar *ptr; /** @@ -653,7 +1058,9 @@ typedef struct ddog_crasht_StackFrameNames { typedef struct ddog_crasht_Slice_StackFrameNames { /** - * Must be non-null and suitably aligned for the underlying type. + * Should be non-null and suitably aligned for the underlying type. It is + * allowed but not recommended for the pointer to be null when the len is + * zero. */ const struct ddog_crasht_StackFrameNames *ptr; /** @@ -665,7 +1072,9 @@ typedef struct ddog_crasht_Slice_StackFrameNames { typedef struct ddog_Slice_U8 { /** - * Must be non-null and suitably aligned for the underlying type. + * Should be non-null and suitably aligned for the underlying type. It is + * allowed but not recommended for the pointer to be null when the len is + * zero. */ const uint8_t *ptr; /** @@ -683,6 +1092,7 @@ typedef struct ddog_Slice_U8 ddog_ByteSlice; typedef struct ddog_crasht_NormalizedAddress { uint64_t file_offset; ddog_ByteSlice build_id; + uint64_t age; ddog_CharSlice path; enum ddog_crasht_NormalizedAddressTypes typ; } ddog_crasht_NormalizedAddress; @@ -699,7 +1109,9 @@ typedef struct ddog_crasht_StackFrame { typedef struct ddog_crasht_Slice_StackFrame { /** - * Must be non-null and suitably aligned for the underlying type. + * Should be non-null and suitably aligned for the underlying type. It is + * allowed but not recommended for the pointer to be null when the len is + * zero. */ const struct ddog_crasht_StackFrame *ptr; /** diff --git a/components-rs/crashtracker.h b/components-rs/crashtracker.h index 8012dfcac4..42c18798c3 100644 --- a/components-rs/crashtracker.h +++ b/components-rs/crashtracker.h @@ -163,7 +163,7 @@ DDOG_CHECK_RETURN struct ddog_crasht_Result ddog_crasht_clear_trace_ids(void); * * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html - * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * We're currently locked into 1.76.0, have to do an ugly workaround involving 2 64 bit ints * until we can upgrade. * * # Safety @@ -188,7 +188,7 @@ struct ddog_crasht_UsizeResult ddog_crasht_insert_trace_id(uint64_t id_high, * * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html - * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * We're currently locked into 1.76.0, have to do an ugly workaround involving 2 64 bit ints * until we can upgrade. * * # Safety @@ -214,7 +214,7 @@ struct ddog_crasht_UsizeResult ddog_crasht_insert_span_id(uint64_t id_high, * * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html - * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * We're currently locked into 1.76.0, have to do an ugly workaround involving 2 64 bit ints * until we can upgrade. * * # Safety @@ -241,7 +241,7 @@ struct ddog_crasht_Result ddog_crasht_remove_span_id(uint64_t id_high, * * Note: 128 bit ints in FFI were not stabilized until Rust 1.77 * https://blog.rust-lang.org/2024/03/30/i128-layout-update.html - * We're currently locked into 1.71, have to do an ugly workaround involving 2 64 bit ints + * We're currently locked into 1.76.0, have to do an ugly workaround involving 2 64 bit ints * until we can upgrade. * * # Safety diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index 5355d883bb..776c13459f 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -8,6 +8,10 @@ #include "telemetry.h" #include "sidecar.h" +typedef struct ddog_Vec_CChar *(*ddog_DynamicConfigUpdate)(ddog_CharSlice config, + ddog_CharSlice value, + bool return_old); + /** * `QueueId` is a struct that represents a unique identifier for a queue. * It contains a single field, `inner`, which is a 64-bit unsigned integer. @@ -125,8 +129,14 @@ extern ddog_Uuid ddtrace_runtime_id; extern void (*ddog_log_callback)(ddog_CharSlice); +extern ddog_VecRemoteConfigProduct DDTRACE_REMOTE_CONFIG_PRODUCTS; + +extern ddog_VecRemoteConfigCapabilities DDTRACE_REMOTE_CONFIG_CAPABILITIES; + extern const uint8_t *DDOG_PHP_FUNCTION; +extern struct ddog_SidecarTransport *ddtrace_sidecar; + /** * # Safety * Must be called from a single-threaded context, such as MINIT. @@ -155,6 +165,48 @@ void ddog_reset_logger(void); uint32_t ddog_get_logs_count(ddog_CharSlice level); +void ddog_init_remote_config(bool live_debugging_enabled); + +struct ddog_RemoteConfigState *ddog_init_remote_config_state(const struct ddog_Endpoint *endpoint); + +void ddog_process_remote_configs(struct ddog_RemoteConfigState *remote_config); + +bool ddog_type_can_be_instrumented(const struct ddog_RemoteConfigState *remote_config, + ddog_CharSlice typename_); + +bool ddog_global_log_probe_limiter_inc(const struct ddog_RemoteConfigState *remote_config); + +struct ddog_Vec_CChar *ddog_CharSlice_to_owned(ddog_CharSlice str); + +bool ddog_remote_configs_service_env_change(struct ddog_RemoteConfigState *remote_config, + ddog_CharSlice service, + ddog_CharSlice env, + ddog_CharSlice version); + +bool ddog_remote_config_alter_dynamic_config(struct ddog_RemoteConfigState *remote_config, + ddog_CharSlice config, + ddog_CharSlice new_value); + +void ddog_setup_remote_config(ddog_DynamicConfigUpdate update_config, + const struct ddog_LiveDebuggerSetup *setup); + +void ddog_rinit_remote_config(struct ddog_RemoteConfigState *remote_config); + +void ddog_rshutdown_remote_config(struct ddog_RemoteConfigState *remote_config); + +void ddog_shutdown_remote_config(struct ddog_RemoteConfigState*); + +void ddog_log_debugger_data(const struct ddog_Vec_DebuggerPayload *payloads); + +void ddog_log_debugger_datum(const struct ddog_DebuggerPayload *payload); + +ddog_MaybeError ddog_send_debugger_diagnostics(const struct ddog_RemoteConfigState *remote_config_state, + struct ddog_SidecarTransport **transport, + const struct ddog_InstanceId *instance_id, + ddog_QueueId queue_id, + const struct ddog_Probe *probe, + uint64_t timestamp); + ddog_MaybeError ddog_sidecar_enable_appsec(ddog_CharSlice shared_lib_path, ddog_CharSlice socket_file_path, ddog_CharSlice lock_file_path, @@ -166,6 +218,15 @@ ddog_MaybeError ddog_sidecar_connect_php(struct ddog_SidecarTransport **connecti ddog_CharSlice log_level, bool enable_telemetry); +void ddtrace_sidecar_reconnect(struct ddog_SidecarTransport **transport, + struct ddog_SidecarTransport *(*factory)(void)); + +bool ddog_shm_limiter_inc(const struct ddog_MaybeShmLimiter *limiter, uint32_t limit); + +bool ddog_exception_hash_limiter_inc(struct ddog_SidecarTransport *connection, + uint64_t hash, + uint32_t granularity_seconds); + bool ddtrace_detect_composer_installed_json(struct ddog_SidecarTransport **transport, const struct ddog_InstanceId *instance_id, const ddog_QueueId *queue_id, diff --git a/components-rs/lib.rs b/components-rs/lib.rs index 1e70a825e2..49db14b504 100644 --- a/components-rs/lib.rs +++ b/components-rs/lib.rs @@ -1,8 +1,9 @@ +#![allow(internal_features)] #![feature(allow_internal_unstable)] -#![feature(local_key_cell_methods)] #![feature(linkage)] pub mod log; +pub mod remote_config; pub mod sidecar; pub mod telemetry; @@ -10,7 +11,6 @@ use std::borrow::Cow; use std::ffi::c_char; use std::ptr::null_mut; use ddcommon::entity_id::{get_container_id, set_cgroup_file}; -use ddcommon_ffi::CharSlice; use uuid::Uuid; pub use datadog_crashtracker_ffi::*; diff --git a/components-rs/live-debugger.h b/components-rs/live-debugger.h new file mode 100644 index 0000000000..bb3a67a892 --- /dev/null +++ b/components-rs/live-debugger.h @@ -0,0 +1,139 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021-Present Datadog, Inc. + +#ifndef DDOG_LIVE_DEBUGGER_H +#define DDOG_LIVE_DEBUGGER_H + +#include +#include +#include +#include +#include "common.h" + +void drop_span_decoration_probe(struct ddog_SpanDecorationProbe); + +struct ddog_CaptureConfiguration ddog_capture_defaults(void); + +void ddog_register_expr_evaluator(const struct ddog_Evaluator *eval); + +struct ddog_ConditionEvaluationResult ddog_evaluate_condition(const struct ddog_ProbeCondition *condition, + void *context); + +void ddog_drop_void_collection_string(struct ddog_VoidCollection void_); + +struct ddog_VoidCollection ddog_evaluate_unmanaged_string(const struct ddog_DslString *segments, + void *context, + struct ddog_Vec_SnapshotEvaluationError **errors); + +struct ddog_ValueEvaluationResult ddog_evaluate_value(const struct ddog_ProbeValue *value, + void *context); + +struct ddog_IntermediateValue ddog_evaluated_value_get(const struct ddog_InternalIntermediateValue *value); + +void ddog_evaluated_value_drop(struct ddog_InternalIntermediateValue*); + +struct ddog_VoidCollection ddog_evaluated_value_into_unmanaged_string(struct ddog_InternalIntermediateValue *value, + void *context); + +struct ddog_LiveDebuggingParseResult ddog_parse_live_debugger_json(ddog_CharSlice json); + +void ddog_drop_live_debugger_parse_result(struct ddog_LiveDebuggingParseResult); + +ddog_DebuggerCapture *ddog_create_exception_snapshot(struct ddog_Vec_DebuggerPayload *buffer, + ddog_CharSlice service, + ddog_CharSlice language, + ddog_CharSlice id, + ddog_CharSlice exception_id, + ddog_CharSlice exception_hash, + uint32_t frame_index, + ddog_CharSlice type_name, + ddog_CharSlice method_name, + uint64_t timestamp); + +struct ddog_DebuggerPayload *ddog_create_log_probe_snapshot(const struct ddog_Probe *probe, + const ddog_CharSlice *message, + ddog_CharSlice service, + ddog_CharSlice language, + uint64_t timestamp); + +void ddog_update_payload_message(struct ddog_DebuggerPayload *payload, ddog_CharSlice message); + +ddog_DebuggerCapture *ddog_snapshot_entry(struct ddog_DebuggerPayload *payload); + +ddog_DebuggerCapture *ddog_snapshot_lines(struct ddog_DebuggerPayload *payload, uint32_t line); + +ddog_DebuggerCapture *ddog_snapshot_exit(struct ddog_DebuggerPayload *payload); + +bool ddog_snapshot_redacted_name(ddog_CharSlice name); + +void ddog_snapshot_add_redacted_name(ddog_CharSlice name); + +bool ddog_snapshot_redacted_type(ddog_CharSlice name); + +void ddog_snapshot_add_redacted_type(ddog_CharSlice name); + +void ddog_snapshot_add_field(ddog_DebuggerCapture *capture, + enum ddog_FieldType type, + ddog_CharSlice name, + struct ddog_CaptureValue value); + +void ddog_capture_value_add_element(struct ddog_CaptureValue *value, + struct ddog_CaptureValue element); + +void ddog_capture_value_add_entry(struct ddog_CaptureValue *value, + struct ddog_CaptureValue key, + struct ddog_CaptureValue element); + +void ddog_capture_value_add_field(struct ddog_CaptureValue *value, + ddog_CharSlice key, + struct ddog_CaptureValue element); + +void ddog_snapshot_format_new_uuid(uint8_t (*buf)[36]); + +ddog_CharSlice ddog_evaluation_error_first_msg(const struct ddog_Vec_SnapshotEvaluationError *vec); + +void ddog_evaluation_error_drop(struct ddog_Vec_SnapshotEvaluationError*); + +struct ddog_DebuggerPayload *ddog_evaluation_error_snapshot(const struct ddog_Probe *probe, + ddog_CharSlice service, + ddog_CharSlice language, + struct ddog_Vec_SnapshotEvaluationError *errors, + uint64_t timestamp); + +void ddog_serialize_debugger_payload(const struct ddog_DebuggerPayload *payload, + void (*callback)(ddog_CharSlice)); + +void ddog_drop_debugger_payload(struct ddog_DebuggerPayload*); + +struct ddog_DebuggerPayload *ddog_debugger_diagnostics_create(const struct ddog_Probe *probe, + ddog_CharSlice service, + ddog_CharSlice runtime_id, + uint64_t timestamp); + +void ddog_debugger_diagnostics_set_parent_id(struct ddog_DebuggerPayload *payload, + ddog_CharSlice parent_id); + +struct ddog_String *ddog_live_debugger_build_tags(ddog_CharSlice debugger_version, + ddog_CharSlice env, + ddog_CharSlice version, + ddog_CharSlice runtime_id, + struct ddog_Vec_Tag global_tags); + +struct ddog_String *ddog_live_debugger_tags_from_raw(ddog_CharSlice tags); + +ddog_MaybeError ddog_live_debugger_spawn_sender(const ddog_Endpoint *endpoint, + struct ddog_String *tags, + struct ddog_SenderHandle **handle); + +bool ddog_live_debugger_send_raw_data(struct ddog_SenderHandle *handle, + enum ddog_DebuggerType debugger_type, + struct ddog_OwnedCharSlice data); + +bool ddog_live_debugger_send_payload(struct ddog_SenderHandle *handle, + const struct ddog_DebuggerPayload *data); + +void ddog_live_debugger_drop_sender(struct ddog_SenderHandle *sender); + +void ddog_live_debugger_join_sender(struct ddog_SenderHandle *sender); + +#endif /* DDOG_LIVE_DEBUGGER_H */ diff --git a/components-rs/remote_config.rs b/components-rs/remote_config.rs new file mode 100644 index 0000000000..68a648c133 --- /dev/null +++ b/components-rs/remote_config.rs @@ -0,0 +1,404 @@ +use std::borrow::Cow; +use datadog_live_debugger::{FilterList, LiveDebuggingData, ServiceConfiguration}; +use datadog_live_debugger_ffi::data::{Probe}; +use datadog_live_debugger_ffi::evaluator::{ddog_register_expr_evaluator, Evaluator}; +use datadog_dynamic_configuration::{Configs, data::TracingSamplingRuleProvenance}; +use datadog_remote_config::{RemoteConfigCapabilities, RemoteConfigData, RemoteConfigProduct, Target}; +use datadog_remote_config::fetch::ConfigInvariants; +use datadog_sidecar::shm_remote_config::{RemoteConfigManager, RemoteConfigUpdate}; +use ddcommon::Endpoint; +use ddcommon_ffi::slice::AsBytes; +use ddcommon_ffi::{CharSlice, MaybeError}; +use itertools::Itertools; +use std::collections::{HashMap, HashSet}; +use std::collections::hash_map::Entry; +use std::ffi::c_char; +use std::mem; +use std::sync::Arc; +use serde::Serialize; +use crate::sidecar::MaybeShmLimiter; +use regex_automata::dfa::regex::Regex; +use tracing::debug; +use datadog_live_debugger::debugger_defs::{DebuggerData, DebuggerPayload}; +use datadog_live_debugger_ffi::send_data::{ddog_debugger_diagnostics_create_unboxed, ddog_snapshot_redacted_type}; +use datadog_sidecar::service::blocking::SidecarTransport; +use datadog_sidecar::service::{InstanceId, QueueId}; +use datadog_sidecar_ffi::ddog_sidecar_send_debugger_data; + +type DynamicConfigUpdate = for <'a> extern "C" fn(config: CharSlice, value: CharSlice, return_old: bool) -> *mut Vec; + +static mut LIVE_DEBUGGER_CALLBACKS: Option = None; +static mut DYNAMIC_CONFIG_UPDATE: Option = None; + +type VecRemoteConfigProduct = ddcommon_ffi::Vec; +#[no_mangle] +pub static mut DDTRACE_REMOTE_CONFIG_PRODUCTS: VecRemoteConfigProduct = ddcommon_ffi::Vec::new(); + +type VecRemoteConfigCapabilities = ddcommon_ffi::Vec; +#[no_mangle] +pub static mut DDTRACE_REMOTE_CONFIG_CAPABILITIES: VecRemoteConfigCapabilities = ddcommon_ffi::Vec::new(); + +#[derive(Default)] +struct DynamicConfig { + active_config_path: Option, + configs: Vec, + old_config_values: HashMap>, +} + +pub struct RemoteConfigState { + manager: RemoteConfigManager, + live_debugger: LiveDebuggerState, + dynamic_config: DynamicConfig, +} + +#[repr(C)] +pub struct LiveDebuggerSetup<'a> { + pub evaluator: &'a Evaluator, + pub callbacks: LiveDebuggerCallbacks, +} + +#[repr(C)] +#[derive(Clone)] +pub struct LiveDebuggerCallbacks { + pub set_probe: extern "C" fn(probe: Probe, limiter: &MaybeShmLimiter) -> i64, + pub remove_probe: extern "C" fn(id: i64), +} + +#[derive(Default)] +pub struct LiveDebuggerState { + pub spans_map: HashMap, + pub active: HashMap>, + pub config_id: String, + pub allow_dfa: Option, + pub deny_dfa: Option, +} + +#[no_mangle] +pub unsafe extern "C" fn ddog_init_remote_config(live_debugging_enabled: bool) { + + DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::ApmTracing); + DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingCustomTags); + DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingEnabled); + DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingHttpHeaderTags); + DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingLogsInjection); + DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingSampleRate); + DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingSampleRules); + + if live_debugging_enabled { + DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::LiveDebugger) + } +} + +// Per-thread state +#[no_mangle] +pub unsafe extern "C" fn ddog_init_remote_config_state( + endpoint: &Endpoint, +) -> Box { + Box::new(RemoteConfigState { + manager: RemoteConfigManager::new(ConfigInvariants { + language: "php".to_string(), + tracer_version: include_str!("../VERSION").into(), + endpoint: endpoint.clone(), + products: DDTRACE_REMOTE_CONFIG_PRODUCTS.to_vec(), + capabilities: DDTRACE_REMOTE_CONFIG_CAPABILITIES.to_vec(), + }), + live_debugger: LiveDebuggerState::default(), + dynamic_config: Default::default(), + }) +} + +#[derive(Serialize)] +struct SampleRule<'a> { + #[serde(skip_serializing_if = "Option::is_none")] + name: Option<&'a str>, + service: &'a str, + resource: &'a str, + #[serde(skip_serializing_if = "HashMap::is_empty")] + tags: HashMap<&'a str, &'a str>, + #[serde(rename = "_provenance")] + provenance: TracingSamplingRuleProvenance, + sample_rate: f64, +} + +fn map_config(config: &Configs) -> (&'static str, String) { + match config { + Configs::TracingHeaderTags(tags) => { + ("datadog.trace.header_tags", tags.iter().map(|(k, _)| k).join(",")) + } + Configs::TracingSampleRate(rate) => { + ("datadog.trace.sample_rate", rate.to_string()) + } + Configs::LogInjectionEnabled(enabled) => { + ("datadog.logs_injection", (if *enabled { "1" } else { "0" }).to_string()) + } + Configs::TracingTags(tags) => { + ("datadog.tags", tags.join(",")) + } + Configs::TracingEnabled(enabled) => { + ("datadog.trace.enabled", (if *enabled { "1" } else { "0" }).to_string()) + } + Configs::TracingSamplingRules(rules) => { + let map: Vec<_> = rules.iter().map(|r| SampleRule { + name: r.name.as_deref(), + service: r.service.as_str(), + resource: r.resource.as_str(), + tags: r.tags.iter().map(|t| (t.key.as_str(), t.value_glob.as_str())).collect(), + provenance: r.provenance, + sample_rate: r.sample_rate, + }).collect(); + ("datadog.trace.sampling_rules", serde_json::to_string(&map).unwrap()) + } + } +} + +fn remove_old_configs(remote_config: &mut RemoteConfigState) { + for (name, val) in remote_config.dynamic_config.old_config_values.drain() { + unsafe { DYNAMIC_CONFIG_UPDATE }.unwrap()(name.as_str().into(), (&val).into(), false); + } + remote_config.dynamic_config.old_config_values.clear(); + remote_config.dynamic_config.active_config_path = None; +} + +fn insert_new_configs(old_config_values: &mut HashMap>, old_configs: &mut Vec, new_configs: Vec) { + let mut found_configs = HashSet::new(); + for config in new_configs.iter() { + let (name, val) = map_config(config); + let is_update = old_config_values.contains_key(name); + let original = unsafe { DYNAMIC_CONFIG_UPDATE }.unwrap()(name.into(), val.as_str().into(), !is_update); + if !original.is_null() { + old_config_values.insert(name.into(), *unsafe { Box::from_raw(original) }); + } + found_configs.insert(mem::discriminant(config)); + } + for config in old_configs.iter() { + if !found_configs.contains(&mem::discriminant(config)) { + let (name, _) = map_config(config); + if let Some(val) = old_config_values.remove(name) { + unsafe { DYNAMIC_CONFIG_UPDATE }.unwrap()(name.into(), (&val).into(), false); + } + } + } + *old_configs = new_configs; +} + +#[no_mangle] +pub extern "C" fn ddog_process_remote_configs(remote_config: &mut RemoteConfigState) { + loop { + match remote_config.manager.fetch_update() { + RemoteConfigUpdate::None => break, + RemoteConfigUpdate::Add { value, limiter_index } => match value.data { + RemoteConfigData::LiveDebugger(debugger) => { + let val = Box::new((debugger, MaybeShmLimiter::open(limiter_index))); + let rc_ref = unsafe { mem::transmute(remote_config as *mut _) }; // sigh, borrow checker + let entry = remote_config.live_debugger.active.entry(value.config_id); + let (debugger, limiter) = &mut **match entry { + Entry::Occupied(mut e) => { + e.insert(val); + e.into_mut() + } + Entry::Vacant(e) => { + e.insert(val) + } + }; + apply_config(rc_ref, debugger, limiter); + }, + RemoteConfigData::DynamicConfig(config_data) => { + let configs: Vec = config_data.lib_config.into(); + if !configs.is_empty() { + insert_new_configs(&mut remote_config.dynamic_config.old_config_values, &mut remote_config.dynamic_config.configs, configs); + remote_config.dynamic_config.active_config_path = Some(value.config_id); + } + }, + }, + RemoteConfigUpdate::Remove(path) => match path.product { + RemoteConfigProduct::LiveDebugger => { + if let Some(boxed) = remote_config.live_debugger.active.remove(&path.config_id) { + remove_config(remote_config, &boxed.0); + } + }, + RemoteConfigProduct::ApmTracing => { + if Some(path.config_id) == remote_config.dynamic_config.active_config_path { + remove_old_configs(remote_config); + } + }, + }, + } + } +} + +fn apply_config(remote_config: &mut RemoteConfigState, debugger: &LiveDebuggingData, limiter: &MaybeShmLimiter) { + if let Some(callbacks) = unsafe { &LIVE_DEBUGGER_CALLBACKS } { + match debugger { + LiveDebuggingData::Probe(probe) => { + debug!("Applying live debugger probe {probe:?}"); + let hook_id = (callbacks.set_probe)(probe.into(), limiter); + if hook_id >= 0 { + remote_config + .live_debugger + .spans_map + .insert(probe.id.clone(), hook_id); + } + }, + LiveDebuggingData::ServiceConfiguration(config) => { + debug!("Applying live debugger service config {config:?}"); + fn build_regex(list: &FilterList) -> Option { + if list.classes.is_empty() && list.package_prefixes.is_empty() { + None + } else { + let mut regex = "".to_string(); + for s in list.classes.iter() { + if !regex.is_empty() { + regex.push('|'); + } + regex.push_str(®ex::escape(s.as_str())); + } + for s in list.package_prefixes.iter() { + if !regex.is_empty() { + regex.push('|'); + } + regex.push_str(®ex::escape(s.as_str())); + regex.push_str(".*"); + } + Some(Regex::new(regex.as_str()).unwrap()) + } + } + remote_config.live_debugger.config_id = config.id.clone(); + remote_config.live_debugger.allow_dfa = build_regex(&config.allow); + remote_config.live_debugger.deny_dfa = build_regex(&config.deny); + } + } + } +} + +fn remove_config(remote_config: &mut RemoteConfigState, debugger: &LiveDebuggingData) { + if let Some(callbacks) = unsafe { &LIVE_DEBUGGER_CALLBACKS } { + match debugger { + LiveDebuggingData::Probe(probe) => { + if let Some(id) = remote_config.live_debugger.spans_map.remove(&probe.id) { + debug!("Removing live debugger probe {}", probe.id); + (callbacks.remove_probe)(id); + } + }, + LiveDebuggingData::ServiceConfiguration(ServiceConfiguration { id, .. }) => { + // There can only be one active service configuration, but I don't want to rely on the order of adding and removing service configurations + if id == &remote_config.live_debugger.config_id { + debug!("Resetting live-debugger service config"); + remote_config.live_debugger.allow_dfa = None; + remote_config.live_debugger.deny_dfa = None; + } + } + } + } +} + +#[no_mangle] +pub extern "C" fn ddog_type_can_be_instrumented(remote_config: &RemoteConfigState, typename: CharSlice) -> bool { + if ddog_snapshot_redacted_type(typename) { + return false; + } + + if let Some(regex) = &remote_config.live_debugger.allow_dfa { + if !regex.is_match(typename.as_bytes()) { + return false; + } + } + + if let Some(regex) = &remote_config.live_debugger.deny_dfa { + if regex.is_match(typename.as_bytes()) { + return false; + } + } + + true +} + +#[no_mangle] +pub extern "C" fn ddog_global_log_probe_limiter_inc(remote_config: &RemoteConfigState) -> bool { + if let Some(boxed) = remote_config.live_debugger.active.get(&remote_config.live_debugger.config_id) { + if let (LiveDebuggingData::ServiceConfiguration(config), limiter) = &**boxed { + limiter.inc(config.sampling_snapshots_per_second) + } else { + true + } + } else { + true + } +} + +#[no_mangle] +pub unsafe extern "C" fn ddog_CharSlice_to_owned(str: CharSlice) -> *mut Vec { + Box::into_raw(Box::new(str.as_slice().into())) +} + +#[no_mangle] +pub extern "C" fn ddog_remote_configs_service_env_change(remote_config: &mut RemoteConfigState, service: CharSlice, env: CharSlice, version: CharSlice) -> bool { + let new_target = Target { + service: service.to_utf8_lossy().to_string(), + env: env.to_utf8_lossy().to_string(), + app_version: version.to_utf8_lossy().to_string(), + }; + + if let Some(target) = remote_config.manager.get_target() { + if **target == new_target { + return false; + } + } + + remote_config.manager.track_target(&Arc::new(new_target)); + ddog_process_remote_configs(remote_config); + + true +} + +#[no_mangle] +pub unsafe extern "C" fn ddog_remote_config_alter_dynamic_config(remote_config: &mut RemoteConfigState, config: CharSlice, new_value: CharSlice) -> bool { + if let Some(entry) = remote_config.dynamic_config.old_config_values.get_mut(config.try_to_utf8().unwrap()) { + *entry = new_value.as_slice().into(); + return false; + } + true +} + +#[no_mangle] +pub unsafe extern "C" fn ddog_setup_remote_config(update_config: DynamicConfigUpdate, setup: &LiveDebuggerSetup) { + ddog_register_expr_evaluator(setup.evaluator); + DYNAMIC_CONFIG_UPDATE = Some(update_config); + LIVE_DEBUGGER_CALLBACKS = Some(setup.callbacks.clone()); +} + +#[no_mangle] +pub extern "C" fn ddog_rinit_remote_config(remote_config: &mut RemoteConfigState) { + ddog_process_remote_configs(remote_config); +} + +#[no_mangle] +pub extern "C" fn ddog_rshutdown_remote_config(remote_config: &mut RemoteConfigState) { + remote_config.live_debugger.spans_map.clear(); + remote_config.dynamic_config.old_config_values.clear(); + remote_config.manager.unload_configs(&[RemoteConfigProduct::ApmTracing, RemoteConfigProduct::LiveDebugger]); +} + +#[no_mangle] +pub extern "C" fn ddog_shutdown_remote_config(_: Box) {} + +#[no_mangle] +pub extern "C" fn ddog_log_debugger_data(payloads: &Vec) { + if !payloads.is_empty() { + debug!("Submitting debugger data: {}", serde_json::to_string(payloads).unwrap()); + } +} + +#[no_mangle] +pub extern "C" fn ddog_log_debugger_datum(payload: &DebuggerPayload) { + debug!("Submitting debugger data: {}", serde_json::to_string(payload).unwrap()); +} + +#[no_mangle] +pub unsafe extern "C" fn ddog_send_debugger_diagnostics<'a>(remote_config_state: &RemoteConfigState, transport: &mut Box, instance_id: &InstanceId, queue_id: QueueId, probe: &'a Probe, timestamp: u64) -> MaybeError { + let service = Cow::Borrowed(remote_config_state.manager.get_target().map_or("", |t| t.service.as_str())); + let mut payload = ddog_debugger_diagnostics_create_unboxed(probe, service, Cow::Borrowed(&instance_id.runtime_id), timestamp); + let DebuggerData::Diagnostics(ref mut diagnostics) = payload.debugger else { unreachable!(); }; + diagnostics.parent_id = Some(Cow::Borrowed(remote_config_state.manager.current_runtime_id.as_str())); + debug!("Submitting debugger diagnostics data: {:?}", serde_json::to_string(&payload).unwrap()); + ddog_sidecar_send_debugger_data(transport, instance_id, queue_id, vec![payload]) +} diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index 259bff28a0..4d03e11a46 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -168,6 +168,7 @@ ddog_MaybeError ddog_sidecar_session_set_config(struct ddog_SidecarTransport **t ddog_CharSlice language, ddog_CharSlice tracer_version, uint32_t flush_interval_milliseconds, + uint32_t remote_config_poll_interval_millis, uint32_t telemetry_heartbeat_interval_millis, uintptr_t force_flush_size, uintptr_t force_drop_size, @@ -196,12 +197,23 @@ ddog_MaybeError ddog_sidecar_send_trace_v04_bytes(struct ddog_SidecarTransport * ddog_CharSlice data, const struct ddog_TracerHeaderTags *tracer_header_tags); +ddog_MaybeError ddog_sidecar_send_debugger_data(struct ddog_SidecarTransport **transport, + const struct ddog_InstanceId *instance_id, + ddog_QueueId queue_id, + struct ddog_Vec_DebuggerPayload payloads); + +ddog_MaybeError ddog_sidecar_send_debugger_datum(struct ddog_SidecarTransport **transport, + const struct ddog_InstanceId *instance_id, + ddog_QueueId queue_id, + struct ddog_DebuggerPayload *payload); + ddog_MaybeError ddog_sidecar_set_remote_config_data(struct ddog_SidecarTransport **transport, const struct ddog_InstanceId *instance_id, const ddog_QueueId *queue_id, ddog_CharSlice service_name, ddog_CharSlice env_name, - ddog_CharSlice app_version); + ddog_CharSlice app_version, + const struct ddog_Vec_Tag *global_tags); /** * Dumps the current state of the sidecar. diff --git a/components-rs/sidecar.rs b/components-rs/sidecar.rs index 5db98fb09a..b1759eb3a4 100644 --- a/components-rs/sidecar.rs +++ b/components-rs/sidecar.rs @@ -2,15 +2,21 @@ use std::ffi::{c_char, CStr, OsStr}; use std::ops::DerefMut; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; +use lazy_static::{lazy_static, LazyStatic}; +use tracing::warn; #[cfg(windows)] use std::os::windows::ffi::OsStrExt; use std::sync::Mutex; +use std::time::Duration; use datadog_sidecar::config::{self, AppSecConfig, LogMethod}; -use datadog_sidecar::service::blocking::SidecarTransport; +use datadog_sidecar::service::blocking::{acquire_exception_hash_rate_limiter, SidecarTransport}; +use ddcommon::rate_limiter::{Limiter, LocalLimiter}; +use datadog_ipc::rate_limiter::{AnyLimiter, ShmLimiterMemory}; +use datadog_sidecar::service::exception_hash_rate_limiter::ExceptionHashRateLimiter; +use datadog_sidecar::tracer::shm_limiter_path; use ddcommon_ffi::slice::AsBytes; use ddcommon_ffi::{CharSlice, self as ffi, MaybeError}; use ddtelemetry_ffi::try_c; -use lazy_static::lazy_static; #[cfg(any(windows, php_shared_build))] use spawn_worker::LibDependency; #[cfg(windows)] @@ -124,8 +130,81 @@ pub extern "C" fn ddog_sidecar_connect_php( let log_level = OsStr::from_bytes(log_level.as_bytes()).into(); cfg.child_env.insert(OsStr::new("DD_TRACE_LOG_LEVEL").into(), log_level); } - let stream = Box::new(try_c!(run_sidecar(cfg))); + let mut stream = Box::new(try_c!(run_sidecar(cfg))); + // Generally the Send buffer ought to be big enough for instantaneous transmission + _ = stream.set_write_timeout(Some(Duration::from_millis(100))); + _ = stream.set_read_timeout(Some(Duration::from_secs(1))); *connection = Box::into_raw(stream); MaybeError::None } + +#[no_mangle] +#[allow(non_upper_case_globals)] +pub static mut ddtrace_sidecar: *mut SidecarTransport = std::ptr::null_mut(); + +#[no_mangle] +pub extern "C" fn ddtrace_sidecar_reconnect( + transport: &mut Box, + factory: unsafe extern "C" fn() -> Option>, +) { + transport.reconnect(|| unsafe { + let sidecar = factory(); + if sidecar.is_some() { + LazyStatic::initialize(&SHM_LIMITER); + } + sidecar + }); +} + + +lazy_static! { + pub static ref SHM_LIMITER: Option = ShmLimiterMemory::open(&shm_limiter_path()).map_or_else(|e| { + warn!("Attempt to use the SHM_LIMITER failed: {e:?}"); + None + }, Some); + + pub static ref EXCEPTION_HASH_LIMITER: Option = ExceptionHashRateLimiter::open().map_or_else(|e| { + warn!("Attempt to use the EXCEPTION_HASH_LIMITER failed: {e:?}"); + None + }, Some); +} + +pub struct MaybeShmLimiter(Option); + +impl MaybeShmLimiter { + pub fn open(index: u32) -> Self { + MaybeShmLimiter(if index == 0 { + None + } else { + match &*SHM_LIMITER { + Some(limiter) => limiter.get(index).map(AnyLimiter::Shm), + None => Some(AnyLimiter::Local(LocalLimiter::default())), + } + }) + } + + pub fn inc(&self, limit: u32) -> bool { + if let Some(ref limiter) = self.0 { + limiter.inc(limit) + } else { + true + } + } +} + +#[no_mangle] +pub extern "C" fn ddog_shm_limiter_inc(limiter: &MaybeShmLimiter, limit: u32) -> bool { + limiter.inc(limit) +} + +#[no_mangle] +pub extern "C" fn ddog_exception_hash_limiter_inc(connection: &mut SidecarTransport, hash: u64, granularity_seconds: u32) -> bool { + if let Some(limiter) = &*EXCEPTION_HASH_LIMITER { + if let Some(limiter) = limiter.find(hash) { + return limiter.inc(); + } + } + let _ = acquire_exception_hash_rate_limiter(connection, hash, Duration::from_secs(granularity_seconds as u64)); + true +} diff --git a/config.m4 b/config.m4 index c29a3c9357..e8f1f1c652 100644 --- a/config.m4 +++ b/config.m4 @@ -65,7 +65,7 @@ if test "$PHP_DDTRACE" != "no"; then fi if test "$PHP_DDTRACE_SANITIZE" != "no"; then - EXTRA_LDFLAGS="-fsanitize=address" + EXTRA_LDFLAGS="-fsanitize=address -lasan" EXTRA_CFLAGS="-fsanitize=address -fno-omit-frame-pointer" fi @@ -141,6 +141,7 @@ if test "$PHP_DDTRACE" != "no"; then ext/arrays.c \ ext/auto_flush.c \ ext/autoload_php_files.c \ + ext/collect_backtrace.c \ ext/comms_php.c \ ext/compat_string.c \ ext/coms.c \ @@ -151,6 +152,7 @@ if test "$PHP_DDTRACE" != "no"; then ext/dogstatsd_client.c \ ext/engine_api.c \ ext/engine_hooks.c \ + ext/exception_serialize.c \ ext/excluded_modules.c \ ext/git.c \ ext/handlers_api.c \ @@ -160,6 +162,7 @@ if test "$PHP_DDTRACE" != "no"; then ext/integrations/exec_integration.c \ ext/integrations/integrations.c \ ext/ip_extraction.c \ + ext/live_debugger.c \ ext/logging.c \ ext/limiter/limiter.c \ ext/memory_limit.c \ @@ -167,12 +170,14 @@ if test "$PHP_DDTRACE" != "no"; then ext/priority_sampling/priority_sampling.c \ ext/profiling.c \ ext/random.c \ + ext/remote_config.c \ ext/serializer.c \ ext/sidecar.c \ ext/signals.c \ ext/span.c \ ext/startup_logging.c \ ext/telemetry.c \ + ext/threads.c \ ext/tracer_tag_propagation/tracer_tag_propagation.c \ ext/user_request.c \ ext/hook/uhook.c \ @@ -303,7 +308,7 @@ EOT pushdef([PHP_GEN_GLOBAL_MAKEFILE], [ popdef([PHP_GEN_GLOBAL_MAKEFILE]) PHP_GEN_GLOBAL_MAKEFILE - sed -i $({ sed --version 2>&1 || echo ''; } | grep GNU >/dev/null || echo "''") -e '/^distclean:/a\'$'\n\t''rm -rf target/' -e '/.*\.a /{s/| xargs rm -f/! -path ".\/target\/*" | xargs rm -f/'$'\n}' Makefile + [sed -i $({ sed --version 2>&1 || echo ''; } | grep GNU >/dev/null || echo "''") -e '/.*\.[ao] /{s/| xargs rm -f/! -path ".\/target*\/*" | xargs rm -f/'$'\n}' -e '/^distclean:/a\'$'\n\t''rm -rf target/ target_mockgen/' Makefile] DDTRACE_GEN_GLOBAL_MAKEFILE_WRAP ]) ]) @@ -324,7 +329,7 @@ EOT ddtrace_rust_lib="\$(builddir)/target/$ddtrace_cargo_profile/libddtrace_php.a" cat <> Makefile.fragments -$ddtrace_rust_lib: $( (find "$ext_srcdir/components-rs" -name "*.rs" -o -name "Cargo.toml"; find "$ext_srcdir/../../libdatadog" -name "*.rs" -not -path "*/target/*"; find "$ext_srcdir/libdatadog" -name "*.rs" -not -path "*/target/*") 2>/dev/null | xargs ) +$ddtrace_rust_lib: $( (find "$ext_srcdir/components-rs" -name "*.rs" -o -name "Cargo.toml"; find "$ext_srcdir/../../libdatadog" -name "*.rs" -not -path "*/target/*"; find "$ext_srcdir/libdatadog" -name "*.rs" -not -path "*/target/*") 2>/dev/null | tr '\n' ' ' ) (cd "$ext_srcdir"; CARGO_TARGET_DIR=\$(builddir)/target/ SHARED=$(test "$ext_shared" = "yes" && echo 1) PROFILE="$ddtrace_cargo_profile" host_os="$host_os" DDTRACE_CARGO=\$(DDTRACE_CARGO) $(if test "$PHP_DDTRACE_SANITIZE" != "no"; then echo COMPILE_ASAN=1; fi) sh ./compile_rust.sh \$(shell echo "\$(MAKEFLAGS)" | $EGREP -o "[[-]]j[[0-9]]+")) EOT fi @@ -336,7 +341,7 @@ EOT if test "$PHP_DDTRACE_SIDECAR_MOCKGEN" != "-"; then ddtrace_mockgen_invocation="HOST= TARGET= $PHP_DDTRACE_SIDECAR_MOCKGEN" else - ddtrace_mockgen_invocation="cd \"$ext_srcdir/components-rs/php_sidecar_mockgen\"; HOST= TARGET= CARGO_TARGET_DIR=\$(builddir)/target/ \$(DDTRACE_CARGO) run" + ddtrace_mockgen_invocation="cd \"$ext_srcdir/components-rs/php_sidecar_mockgen\"; HOST= TARGET= CARGO_TARGET_DIR=\$(builddir)/target_mockgen/ \$(DDTRACE_CARGO) run" fi cat <> Makefile.fragments diff --git a/config.w32 b/config.w32 index 7aa9fb102a..9f37c9ef8f 100644 --- a/config.w32 +++ b/config.w32 @@ -15,7 +15,40 @@ if (PHP_DDTRACE != 'no') { var version = PHP_VERSION * 100 + PHP_MINOR_VERSION; - var DDTRACE_EXT_SOURCES = "arrays.c auto_flush.c autoload_php_files.c compat_string.c configuration.c distributed_tracing_headers.c ddshared.c dogstatsd.c engine_api.c engine_hooks.c excluded_modules.c git.c handlers_api.c handlers_curl" + (version < 800 ? "_php7" : "") + ".c handlers_exception.c handlers_internal.c handlers_pcntl.c ip_extraction.c logging.c memory_limit.c otel_config.c profiling.c random.c serializer.c sidecar.c span.c startup_logging.c telemetry.c user_request.c"; + var DDTRACE_EXT_SOURCES = "arrays.c"; + DDTRACE_EXT_SOURCES += " auto_flush.c"; + DDTRACE_EXT_SOURCES += " autoload_php_files.c"; + DDTRACE_EXT_SOURCES += " collect_backtrace.c"; + DDTRACE_EXT_SOURCES += " compat_string.c"; + DDTRACE_EXT_SOURCES += " configuration.c"; + DDTRACE_EXT_SOURCES += " distributed_tracing_headers.c"; + DDTRACE_EXT_SOURCES += " ddshared.c"; + DDTRACE_EXT_SOURCES += " dogstatsd.c"; + DDTRACE_EXT_SOURCES += " engine_api.c"; + DDTRACE_EXT_SOURCES += " engine_hooks.c"; + DDTRACE_EXT_SOURCES += " exception_serialize.c"; + DDTRACE_EXT_SOURCES += " excluded_modules.c"; + DDTRACE_EXT_SOURCES += " git.c"; + DDTRACE_EXT_SOURCES += " handlers_api.c"; + DDTRACE_EXT_SOURCES += " handlers_curl" + (version < 800 ? "_php7" : "") + ".c"; + DDTRACE_EXT_SOURCES += " handlers_exception.c"; + DDTRACE_EXT_SOURCES += " handlers_internal.c"; + DDTRACE_EXT_SOURCES += " handlers_pcntl.c"; + DDTRACE_EXT_SOURCES += " ip_extraction.c"; + DDTRACE_EXT_SOURCES += " live_debugger.c"; + DDTRACE_EXT_SOURCES += " logging.c"; + DDTRACE_EXT_SOURCES += " memory_limit.c"; + DDTRACE_EXT_SOURCES += " otel_config.c"; + DDTRACE_EXT_SOURCES += " profiling.c"; + DDTRACE_EXT_SOURCES += " random.c"; + DDTRACE_EXT_SOURCES += " remote_config.c"; + DDTRACE_EXT_SOURCES += " serializer.c"; + DDTRACE_EXT_SOURCES += " sidecar.c"; + DDTRACE_EXT_SOURCES += " span.c"; + DDTRACE_EXT_SOURCES += " startup_logging.c"; + DDTRACE_EXT_SOURCES += " telemetry.c"; + DDTRACE_EXT_SOURCES += " threads.c"; + DDTRACE_EXT_SOURCES += " user_request.c"; if (version >= 800 && version < 802) { DDTRACE_EXT_SOURCES += " weakrefs.c"; } diff --git a/ddtrace.sym b/ddtrace.sym index 4777cd0f4f..c3e558bafd 100644 --- a/ddtrace.sym +++ b/ddtrace.sym @@ -5,5 +5,6 @@ ddtrace_set_priority_sampling_on_span_zobj ddtrace_runtime_id ddtrace_user_req_add_listeners ddtrace_ip_extraction_find +ddtrace_set_all_thread_vm_interrupt get_module ddog_daemon_entry_point diff --git a/docker-compose.yml b/docker-compose.yml index 77927c155a..9375dcfb9e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,9 +13,10 @@ x-aliases: - .scenarios.lock:/home/circleci/app/.scenarios.lock - agent-socket:/var/run/datadog - .cargo-registry:/rust/cargo/registry - tmpfs: [ '/home/circleci/app/tmp:uid=3434,gid=3434,exec' ] + tmpfs: [ '/home/circleci/app/tmp:uid=3434,gid=3434,exec,size=12G' ] depends_on: - agent + #- staging_agent - ddagent_integration - request-replayer - test-agent @@ -200,9 +201,29 @@ services: - DD_APM_ENABLED=true - DD_DOGSTATSD_NON_LOCAL_TRAFFIC=1 - DD_APM_RECEIVER_SOCKET=/var/run/datadog/apm.socket + - DD_REMOTE_CONFIGURATION_ENABLED=true ports: - "8126:8126" + staging_agent: + image: datadog/agent:latest + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /proc/:/host/proc/:ro + - /sys/fs/cgroup/:/host/sys/fs/cgroup:ro + - agent-socket:/var/run/datadog + environment: + - DD_API_KEY=${DATADOG_STAGING_API_KEY} + - DD_SITE=datad0g.com + - DD_APM_ENABLED=true + - DD_DOGSTATSD_NON_LOCAL_TRAFFIC=1 + - DD_APM_RECEIVER_SOCKET=/var/run/datadog/apm.socket + - DD_REMOTE_CONFIGURATION_ENABLED=true + - DD_REMOTE_CONFIGURATION_REFRESH_INTERVAL=5s + - DD_APM_RECEIVER_PORT=8125 + ports: + - "8125:8125" + packager: image: datadog/docker-library:ddtrace_php_fpm_packaging working_dir: /app diff --git a/dockerfiles/ci/alpine_compile_extension/base.Dockerfile b/dockerfiles/ci/alpine_compile_extension/base.Dockerfile index d78150c5a0..612325e793 100644 --- a/dockerfiles/ci/alpine_compile_extension/base.Dockerfile +++ b/dockerfiles/ci/alpine_compile_extension/base.Dockerfile @@ -22,7 +22,8 @@ RUN set -eux; \ libsodium-dev \ libxml2-dev \ gnu-libiconv-dev \ - oniguruma-dev + oniguruma-dev \ + cmake # Profiling deps # Minimum: libclang. Nice-to-have: full toolchain including linker to play @@ -33,4 +34,6 @@ RUN apk add --no-cache rust-stdlib RUN apk add --no-cache cargo RUN apk add --no-cache clang git protoc unzip +RUN cargo install --force --locked bindgen-cli && mv /root/.cargo/bin/bindgen /usr/local/bin/ && rm -rf /root/.cargo + CMD ["bash"] diff --git a/dockerfiles/ci/bookworm/Dockerfile b/dockerfiles/ci/bookworm/Dockerfile index 3a5f6a91b8..2b02b0fc41 100644 --- a/dockerfiles/ci/bookworm/Dockerfile +++ b/dockerfiles/ci/bookworm/Dockerfile @@ -9,10 +9,6 @@ ENV ACCEPT_EULA=Y # with cross-language link-time optimization. Needs to match rustc -Vv's llvm # version. ENV DEVLIBS \ - catch2 \ - clang-16 \ - cmake \ - lcov \ libclang-16-dev \ llvm-16-dev \ lld-16 \ @@ -24,6 +20,7 @@ ENV DEVLIBS \ libjpeg-dev \ libmcrypt-dev \ libmemcached-dev \ + libodbc1 \ libonig-dev \ libpq-dev \ libpng-dev \ @@ -34,13 +31,12 @@ ENV DEVLIBS \ libxml2-dev \ libxslt1-dev \ libzip-dev \ - lsof \ + odbcinst1debian2 \ zlib1g-dev \ libasan6 \ gnupg \ unixodbc-dev \ - unixodbc \ - valgrind + unixodbc #netcat ENV RUNTIME_DEPS \ @@ -52,6 +48,7 @@ ENV RUNTIME_DEPS \ gdb \ git \ less \ + lsof \ netbase \ nginx \ strace \ @@ -65,10 +62,14 @@ ENV RUNTIME_DEPS \ ENV PHPIZE_DEPS \ autoconf \ bison \ + catch2 \ + clang-16 \ + cmake \ dpkg-dev \ g++ \ gcc \ file \ + lcov \ libc-dev \ make \ pkg-config \ diff --git a/dockerfiles/ci/buster/Dockerfile b/dockerfiles/ci/buster/Dockerfile index 89a99918d6..ea2af3d9db 100644 --- a/dockerfiles/ci/buster/Dockerfile +++ b/dockerfiles/ci/buster/Dockerfile @@ -9,7 +9,6 @@ ENV ACCEPT_EULA=Y # with cross-language link-time optimization. Needs to match rustc -Vv's llvm # version. ENV DEVLIBS \ - clang-16 \ libclang-16-dev \ libclang-rt-16-dev \ llvm-16-dev \ @@ -23,6 +22,7 @@ ENV DEVLIBS \ libjpeg-dev \ libmcrypt-dev \ libmemcached-dev \ + libodbc1 \ libonig-dev \ libpq-dev \ libpng-dev \ @@ -33,24 +33,24 @@ ENV DEVLIBS \ libxml2-dev \ libxslt1-dev \ libzip-dev \ + odbcinst1debian2 \ zlib1g-dev \ libasan5 \ - gnupg \ - gpg \ unixodbc-dev \ unixodbc +#netcat ENV RUNTIME_DEPS \ apache2 \ apache2-dev \ ca-certificates \ - clang \ clang-format \ curl \ debian-goodies \ gdb \ git \ less \ + lsof \ netbase \ netcat \ nginx \ @@ -65,10 +65,16 @@ ENV RUNTIME_DEPS \ ENV PHPIZE_DEPS \ autoconf \ bison \ + clang-16 \ + cmake \ dpkg-dev \ file \ g++ \ gcc \ + gnupg \ + gpg \ + file \ + lcov \ libc-dev \ make \ pkg-config \ diff --git a/dockerfiles/ci/buster/build-extensions.sh b/dockerfiles/ci/buster/build-extensions.sh index cd912e1f31..3fe52c7961 100755 --- a/dockerfiles/ci/buster/build-extensions.sh +++ b/dockerfiles/ci/buster/build-extensions.sh @@ -132,7 +132,14 @@ else # ext-swoole needs PHP 8 if [[ $PHP_VERSION_ID -ge 80 ]]; then - pecl install swoole-5.1.2; # we don't install swoole here + pushd /tmp + pecl download swoole-5.1.2; # we don't install swoole here + tar xzf swoole-5.1.2.tgz + cd swoole-5.1.2 + phpize + ./configure --host=$HOST_ARCH-linux-gnu + make install + popd fi # We don't install any redis.so to inis, but allow selection at runtime. diff --git a/dockerfiles/ci/windows/basetools.Dockerfile b/dockerfiles/ci/windows/basetools.Dockerfile index 028f5a3689..4765bcb4cb 100644 --- a/dockerfiles/ci/windows/basetools.Dockerfile +++ b/dockerfiles/ci/windows/basetools.Dockerfile @@ -1,6 +1,8 @@ ARG vsVersion FROM datadog/dd-trace-ci:windows-base-$vsVersion +RUN powershell.exe "Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; $Env:chocolateyVersion = '0.10.15'; $Env:chocolateyUseWindowsCompression = 'false'; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')); ''" + # I really need some sane file editing utilities ADD https://ftp.nluug.nl/pub/vim/pc/vim90w32.zip /tmp/vim90w32.zip RUN powershell.exe Expand-Archive /tmp/vim90w32.zip /tmp @@ -18,6 +20,12 @@ RUN git config --global --add safe.directory C:/php-sdk ADD https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe /tmp/rustup-init.exe RUN /tmp/rustup-init.exe -y --default-toolchain=1.71.0 +RUN choco install -y cmake +RUN choco install -y nasm +RUN choco install -y llvm + +RUN powershell "[Environment]::SetEnvironmentVariable('PATH', $env:PATH + ';C:\Program Files\NASM;C:\Program Files\CMake\bin', 'Machine')" + # initial setup WORKDIR /php-sdk diff --git a/dockerfiles/ci/windows/vc14.Dockerfile b/dockerfiles/ci/windows/vc14.Dockerfile index 6570f45a95..1649c44441 100644 --- a/dockerfiles/ci/windows/vc14.Dockerfile +++ b/dockerfiles/ci/windows/vc14.Dockerfile @@ -1,4 +1,5 @@ FROM mcr.microsoft.com/windows/servercore:1809 -ADD https://aka.ms/vs/14/release/vs_buildtools.exe /tmp/vs_buildtools.exe +# https://aka.ms/vs/14/release/vs_buildtools.exe has been removed +ADD vs14_buildtools.exe /tmp/vs_buildtools.exe RUN /tmp/vs_buildtools.exe --quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.Net.Component.4.7.SDK --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.17763 diff --git a/dockerfiles/ci/windows/vs14_buildtools.exe b/dockerfiles/ci/windows/vs14_buildtools.exe new file mode 100644 index 0000000000000000000000000000000000000000..beda411485d682a2276d5c6668ff5468e3e808a0 GIT binary patch literal 3759592 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НAuc5#98rL#kN-G}8Utn|nTlz~} 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

AbVygZE{<_Kzc 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%0D~V6Ow^!w~tbpipOt=N@IR7PZa)+uLJBVpxHB=Fg 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 z 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°txiLXk`72X14clBl%Nd9hf4IM))N*|MXjoN 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.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⁣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%0AG6j2qgI=~87Ixu|I#xXwE{Y^;d7MQAhnAk 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�`|b19L7~%7gtzG(ei=7k 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 z≻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�oyaJ^JcYYW`%`V|vaLVQB7Hg^{MIEq0d>W+)DOKo33LH-<}Fctrp~jZ}Y$rO**u4>n{q#PX%-> zICbbyAmWYJ9lm`M2z_fD-47^6&!FUNIhcCJI}sfV`lT5+?Cz_OiGvws1)G#?nmSD0 zStunvS_67)oi%p3*_&aVFoi*ROOs#DR(@F$Zz3`h1qt*mAA2|r7%XOvPR^pR zR8b#?k17WI+ja_*i@Wv;qBp2Xr!JPLHPR%Kq8t-3)s?-MoR;MrO}20h3kGg|CR9&t zH0t0?7!anMPqPk^h3YJs9Ah*C7aar|vK?wZ!07*JqUD%)a9VN!l4>2as zwL}sY6imy3UPHBi0yM0g?Q@1%UhoOmSBrQ_J#2&OwaL2)@-*Rjp6 zW-FVjRQ08EK4(>(iN0D#8MWLOSGH5)-*TN6!yI9^e+c>jPk(e7!DWWTRW+`f67vJB zDtQ%O&&H#4oqWZz1cOYA-50T5QH=0UmcN=~Jo_T&(k7$hSfkbp5F%P6-bt51AgoZ! zfq}@*`HnK}&hA=7Av_Eo**l>wf_qtXE)o8mQ!NR~-1|~bTm07&5;rXG(k-916?ja` z$Wl$--!-m`l#}?@e^Rg4O=Mi8si%55;^Q1A_rCw7=vu)ySb_ND14}1`&FE-}Z0L6~ zmQ1C)7@cgJI?ZWjXc;t@EI?yH!Q%=o)q%p0Juwa@&I)Tw=wh|g0aI9D3qJst5Tdv9 zv9bv9J9}wI4^EZdKJe8%t?COdq3%7d$5m%$$wbBj_{XtxzeMh1IAL5QGzY~CMrCFN z5VXuLXRIR_B(M0XZ050Bd;zcd`-An40E9II&GUB7o{a^|vr}q-YTgz<9~^;?Eeg-k z3cF3ZJ%9Yv7raoC2=wnx%uYSi|(+Cgem_CRTO~bIgP^)C2wnB1+mtpR5~p7YBHPzIk3U^mm1Y@3%pN5%EJjLC1z`G! zP>_puPXY@-LMFC1i+UzbW?gjP{6`(CPodQhjy=RdKw`oe3|me{Nc`9q+Ga=2t~6j% z?tB1*)|Fl)A4S?qA1D1kQR4(n`Guv>6NyWob(?qRgo%(7@}t@sB#JrHFhNM*5bNkB zK7$uZcuooH+sCHMzdH4CPw}0U|IItu-*)VK(IBKQh6eO1O# z1iSl%i8-x9i`@1uvCF@=N~6sV5#w|gog-}_x8it@Z^mKuF#rwKS=gCvASKPKx`-<4 z5sSrAzu@}k2O+YBkNIX|)3U8-p9f>%NVn-+5> zCN(oZMU|jy$qkvXID)>fdU_szQm|^71HOZ*Hawc4CXjAK;H-zq`n)r*kg$E_Se7ic zK8Y&N+O6P)h1QtuW}fpeih2(de-s#~@6XM~ z>Y*AxvNAMe0b>rF5B!1K2Sq7Qnp~iWfz_*$k?=^=ony##y?hL7v&MQ-zo}ygNOxCp zqdXDnnHD9rwMBScdxLtT8|bBa;$T!8)$wgjs9(*S1S-yxnC*!vvn0@P9eC-HjS>hQ zX-DJEOs{*)56JHE&9IL9!cs!;j3G^!&-mcdhTfk*KKkoe|9o=1(TaRDUnWDjgx?fTGW z(ig$?8Y<;N50X1^K5p2Q+z=~D+7e^ds_T-V2gdc2t{&6FkPlPo#@z8;9lCC>#yW^l z*^R27=>cBMyjM0xGp4C^dW$lscDw&N zhrD#M$gOOhft~qdpSzm5Iytbj+L=RRy>US;HEe*H-@qQrQw^+hOgOR3W=71&gvsbH@*FNroF$!%9`6MU5=!ri@q`(>zeB|ucZ3IWNPnzbfZl|hhtYsY<{4m& zp)1124%Ea(7%7zY;(^kVd#VhbA_H$}^}h;-Be5A82?o6TdNAnM%#pvO!@oC&N3l0d zjwCJHJwe4Rj{2wwsqQ!uV(M7ikk6OG2dXbiwjS6`4mq}v6?J%id2$@hEX=q`bsj9o z;(9tPpm=TO5R^Vt2?oB{;U9}Pp$a$?T(^0)Ixm_rdJ%d@fb_fi=pM>!W#hXt@fKWv z?)BT6x`85Hiu&Te4?=KA?@&P{$8bWDuWDY`6k$^Ox_F32EkHO-8$f2knpn~dj*&Pd z1R|?gE&GElH2ekZ7kjTyVA*r<`6SY}&|7<8z?%A61%N!)JDF(^m*7Lu_nV!)wKgL! z1nTT5BpVC!w-z^2!6n4W!O??00Gy`mo?*`%eg&7)fHtJd|az<#F4Cy(2gEj6YoP57DSBN=&1 zhg(W7e97q3h@>KaPvykTC0bPn4db z;m@3O{)61Uqsv9Kh{|TLoM3=8e49}WcA9jevi(`HGyVv&>EHSezLadg*_6I2mcRN4 zM8XLDv@qC{Zl~%Y?u-vH#oUd=f&PWe7QU*7xKP@@M|@KLOH0r@?;d9rKGo;}Jt*p` zJMD*{+sZDx|k=O1M*-C5LudoV_`NDFTbCI#=hVEl|y9jb1*2>ULXSko0Z*|q?G<&)hy|L*I> z>IJh&Bhx55-jvQ8Z^8Ii&sk`2GP7 zZ<_lxkY5rGqY$d+{g*4ozAW7W5!tshSD)aY{7f?tgzOKxgkI!@=&>C(O2M$3o$=G? zBy+1^KWU}D^DYw+o$`l+TmYtoHhhtJrF%{nG+&R~SjQ-8;V_Iwji5+L~s{k>O3~HEE9@1~ea`(%(Q)A^kr^A2aSZ z7W&u1A98n@rCXw^D=`0dV)(XRoz_2+vvRjG@k2$0#eobpq`oM(LfKnK()C@(AKSC;f(9wI%Jxu%9@Zx~Dz;bPuc>hAeONvR8Ao&T7Wv4qy zsM1DRH3w207y`0fauTvyQB9BxcOoT7mZY%lDyvU}U79%ZVJIphUDNuJU*MPB14O}R z&d`z+UhyT27@THhbRzIlBS;&Fampu(!r6mvBqT6|rbLu(EXchMaPcklV-M-F&+fKa zpxP&$sp_9aKxlq53En?zGQuB&5!T!wL-Ps=Vqgbx8c^Ao6NI2Z2gg;JC>zts{PAJsy* zUgNUbd2MMQ5lWs&XpXsyJ2(6_v?TPpJnz*=t%+gsBw8((o?omQt3z{?vc(urz77t0 zCm#+kvy?a82Aj8UDkrmzUTZ7{7ia14+gamnkgbn#ju<^?Hl(`5$d$U|skX*m&d|qe z)BjDyPiHg#A%>eC=+Aq9e^_&&Dfc1GXqroiQ?!~vaP(2T1Q4rt-RZyl+=w=Ff~}^W zVCf)JSRHGpIdF$WW=|h2X?I9k)2=%R+a<%lN-%GoTS2i8r{m=Ky6Qo3zPj?R*WBz* zBj(^@hMuh!#*7waF`d*R|NA>7h>gx>8&4+tYmN6$tEvTGf!TgX1jjYQn@a%w|9de? z0)kt$d=IpY=P-!hJOzvv8+tgSOxsBu0{MvlpYF3QE?o&Qw1rA6xvn2p9X=RXngOt`Op8$>}nGo89cWK zhAwG&Y>00&oNlSuZuQmJnU3)fYM!bsv+%8EhwAyRWtmXrU93b`vgSjx`~(QppZ;MS zw<_d^2`+PJ6WBZ-VB=c_0s%OcYf+FGr)7S52pftRI34hn>`MKq>n>O!L5fD>;w8HJ z$#m|7cR3#pB=~0#dr^GsuJRozIF3XL0C4KBZ8&b1nJ^BOw&Ar>EW}FKVDXO0B0Ei& zL0YRK7b$HOY~TtjQ{5#Z-0%7u<;e&VzIdhBGph3Jj3>)ksOj<(DZRLJ!|SIPUsdi~ zj`ZVxQb@I>Mf+k zUhswbidaZx4LNS|=DaF^7z&`r-rm%qZ7YF%vM83k#^r7k6Eu=ynvOi+Mvg@+)yjvA zL4X?_d>n?FcM@di^9%r!?>%|2#iZWreM9m<`sb~nO48&GOH^cr#0k`>a+}(n8!M&) z33UULM)`3D_)+DUAIgj$68M61d1Dy~J7sq-xryD3_D>0w88@1{1YVE(T`5xeiq^rL zd9W9upgn(o4q}8tApt08NjEUI*3S;Z`@^#gVAq{0&p!mW5E9gC6OxF@ZpbC{1d}oz zWxuJI<9PPjP5zsTx93JsRqJ!7PQr<;Llu`CVbuE%OR$4%y2+rWO|0dOjVSv1wb5`z z8{E5VmPGH#?*RT%XWy!=ch**u7cB&Mz#zs~>jIG)Y%h{c`6+$evHQw0+^&eCXc6Fc z_X#k!P|Krad&?P}p?lC|^x^?#KteSQ<;yozc@?&&=AC$qglm^DSR$8PZXW`mVKT3H zE?KOg(t@!S&>#=)^IfH5pdHrs!~5~g^DwKTplFdGqCtC2OLiiQe8NAVPkgo9BnlMt z5^*V^N(q{k6dOX-az34f>b<(BPs5sMc|o#Dy%PJE<#k6eiqNIsro8@6w2qqy1kVm7 zBDLd(^tXH(d9JD9TYugFFrn1=2oJ)S*PVBtcJww%vY`<%CERztjEMB6F?a-775F3O zEIQ#nbbqm?K-sYtc7HC&1L1cIpB$q9g39q*`9j*F)y)XKbU;L-99Z8$nokMfx?T>! z{2N{J{RmB@T{|!;xvb8e6*HW9M*)SvybvmM6aWkiMV*rX$>$1X%PR-^K=Vs|>B$!- zT?(R*MnN#f;<_faP*&i8O55V)8z+gsyShpF!#ApS`F z-^l*!-YX`rnafZMdY_Ujh&^v8566Z*H_{#~#oLaf&~MD*h)`(Vo)*Z^LX3n-AsiXq zfxOhS!bdO$^TRV1y`SBls$YtNz9d&046U9EU`jXE93FJx-GTp3jdhM_D1s4Dk%bMd z?l$y4rHU@z{g0Lt z#?5EljA7T~_m{QD$3R54rsK3s_9k4e6Gt5PKwB6Rc`kZXR+?CeJMX||4j<|tQHXFB zI_TXr9PAfMx-6hp=lUxmp32$oHO;Xtk0qMfX|r~|7q4D5jpULE)!!pMu}ntS8G?+w zs0GZIy^|Nw7a!o669C*iLS#KCIs$O`wH{bA)DGw6ziq$W{+IxTa<}OA2%Q$F!4T#$wVBaY3wmT(+lXzn&qE7kOnSxD!z#P4yQcba&d>!I-L?R|-s* zhFkwY*!uTIS|Y&v9S`0il(z%vUL*KfIG(AorJ3)7VnqRNC%YIktt4OsEszQxVZB|Z zyN?}Yr9oee9l$hzd_3tNWZ_E&6@<#R-?CBLm8E&((ZPb%f!ZVJ%|B!Hoga2gj5k}O ze^ZPAg|nM9N3VHJ7cXP)hrvzYtiLdiuK!Jz%4vNg(HJ$|tn*pWZ?^KqiIqa8JN$wC z-qca)Dbod=?LVzB=b?+0ISy(wD`r2vt8x7HE!%Sd1+qZX0hRa|J`yAP?mY4}A+=n# zL;tvci-}|Si~p6P9gt54eB)FJ`C2N})>fE=&>%JBccqhm(1f4LD zgIm9wMfd!-1u|WmIX%2BPNq1m^hKw=i~#WtN;l`}xRs0;^W*k<=qD(P!gL~-E}nas zvCuN*usV5t(jQ&C2{9wf^pDM%PJiV-hH5M_3hPk2+bmApOU2=d0lc>de@zQ$MCTi0 zyV3V;7onvWjiD!eUSZ%ZLF4V;-S9f49T@oqUoSUrO4NM z?RGw$yi%3kjbS2Uv0&+VRs;EO46umRvxP{Aji@wuBB}Fc29|0aDOeM$NG_3gNu3mY znp;3z=HV|1+HFWPRcS~vL+B(cBkU~{Iy5W9D7Fz;Zn!5Caa&juJvf|;TNw}gE$X}l z!qFrJ-Ryf@R9#>=ATbMO^dY+iozqf{58X=+sEUmLKyUo<>gg_B^Ep+Aj3aq|TWp7i zldjrJADa7FC@%neR`qzMg2=H1{ogmE0#NO;zqc9iAUO$dv%iT69Ixp=sTdF7ti+`tvE{>N{enz@J?ND3Mr55_2{u7hDg>|vB7SLTuX4#Zbsf+zt zpbSbiTazi@RnL4Khp}o<#K^$!d9JhU}D;A>Ibx|6H-;*dfh@im`oK^BXWt?9O1!qz7DY z#|fo>Cej`ud5NHj*yF6#D)+&y&mcztWUA>3nCV1>&D82#FxZG_?~(u{oHr z7)|8Dm$y!Ut>qJmTM39vkZ^SJ$K5*4yAr!BIZl3?0vLJxT95oo) z6j6UBvW;X9m?l(ubx-VcM#+i<#6iF;@|Sud4_3Nb`csY4FS^Ry&$nyi{2BWy8EI-M z4nio7S;FDvL%n`OzS6w7FwY&7(!{fC>|8O`szl96=N<*Pm+(!U(ATrfH^$?BuihhM znwfsJ)rrV%VJNVRVPQ-2LuljglRLY7>ve7&Ya#>SZ9jq_8~{{CRoYsd4;*6H$=TQn zKKR0V3d(+nv$+{?t`jOO0|W~(@S8$>>FP&UlI0t5-VKDEV@=}y^?A(uXiW~=CuS1* z#X<46?PfaTc|HA;KzoF@LGBRThw~IB=c63^+jD$OKieNtTuBPm#Ur%ceAefA_d8zO z0Iy%RC`FP~%|mp}zfa$_0Z0Zw_T+L%&LBji7MYJm-eIi)w?n`WH#VAl+M=VY>#2jF z8J9O26Ueh<=9uiVT7Y))ygTtr-2GHe?C;j9>`E&gZ-$P|SsdBF%PyGDd5@Rt(9$N1 z_CoWgi}3)eJvKc_FjXxf%Mh*p)yYEn04yln3>t@>Z1)8q)uuE;EL7Iz6ezf;`{QSnuYMmRF z;(cEc9;Vlh00yD9qnSC>ffniDs&3!rL)TfWA$oN-6t!%DhmPXd1OwjFE{=^GM&@%& zNSD%ETMJx|P)4(<7m9Myne!ii69WrG@Sp`sU~0G#aE_@fiYUs=xOS%qX>>-}Ubw zs}}`S^!Cgr6BC*&95QFxVLQ5#TcP_8^*NlrVTK)qUf_uT5X>sh)F!d~%muzAB16 z%UckcCxOHjpL?Qh%Xbm&_o?pYnrf9jE33v>8X#H&1^>K|{hLz9AuK{(B`DBPFrjSv zq%GS(yU)gbqG*XnfiM*3c34gdrZq?}MwJvOL}N!?SITMzN>_<6xzGTByQsm)qIUY3 z!2xtz)*Z72xjWBn<)_wxL4L4sduC&Vcu#@?<(IWcm`n4Tx_c9;@y57rA#neunbs=K zo>gcnRG}jgva=#xe$P^SlXD2uq{J?RjI?v%of=~q{uV!_^;P=b8&p$+H1A4ab*)C5 zL`n<37^Z{#A*h5qR#Uq^r{Y7N z1N#m(;pzg^g|=@*pCsu`E;M^I2&j%$p5*L#t+o!|F+f3Aaoco=W0V@AASF#;97^X*4QAE`GsNcc+!5_NJBLq-#(V*c~-H4Z95xhi*_xET+-Z zq#`LOi_{V>!C<^I=e0pWP_(En8?v6FV%sv@WJ6B-PlYZ)V()hk z?*V>12>C;~-%?W{AK^N-MIx_ExWAGZd79xoE9GR+YZrS&B^#6JA`n0`f9o5$dp@Qq zpt>8)hWg&^CPLXDMm`_unOzz(>@ga|jw%^5!uP!*2Y8j}L%F_{dZdrxbG zhO-f_-`3vb;?DPPH=vko@+xY3Ey`?2n7x2?Dqh9=Sokj$t={p{9oB>kvyvJ_H)wl)}JHhA67qJM&>;+tMhg^JncP}+*}9$vXT#p(lZxzzpU z^r=cYF>S9Aq{^`P`PI?f^L-kuq@b`Uqc6Mm_jDfc%^V!XSGsQq+S)KE??{`TGRbwQ z(X2@@vVHAm{BK)1J~jA%;1-h-5TO?~y^JvNdN)WBcRx;DBdo-EAMmm7bt75##+gyv zkFF+4h%6ATVQFhKh~mvcv^HXTrU(D23%=M_k;TMmO-Kz}o1q{hX3`KCc>pnnyk-RH zJ|qPH`^7kG{JLMA!QV9j>?eChoI*0xv7m8ACBW%>UD`A>Tml9`dpSeVT--UysU~1F8N~?bq4i^buxo(X^;&nbA;9 zpUulS#-1(xYKF^s=_FAB)ZMRfcPQ$%H7~X#uAAKff}XgXpN1U#Mf4uT`Kux#Q83ih zuDYHJURV@9-K{)=`ira7{OV?8>TW*n>=U9S{U4YMWRI2_(>m`&Sgrs8&i6?8o`_PN z1ul<-(%(!1U@m_Y^-s-kc*t1|*6Kt{d|Oe~CCV@%H;Ap*4aWJ$X>5Dd zUViVNo|+lVkLg&?z1iDqV=EGJf&t44!W#xiDQy61-iTJDy{u%`qd=_J#y@CRsH43Y zeRS6K;%x|sqi2#aU~IP+C|@HwSbCzr)jKRMN|{FB2JLZfk)14WsXSdd>fAT`>H7&b ze8)Nx743?Nsv`U?@>9MrjkQt-FGH0RTiP@0ccaDD>53a5lX+gRvx7` zM~sm2{;3HV^VN>aH?FGj*k&b$C4|X1%67aTfi0p!gTtJOzd@ z+r$q7`Z)a%6sQb#YFCVR4txxmeveF46nHzb&bi=R5RsF2b^$8<_2&s8RVtQ4E$6Sl z!O_A!LkU=t{zEw1HzPq^2ZZ;r&Q<`BO;@qWQ#Y6&z$gBwuW*Hp~{3i zdri|#ZCX!a4RgiciVe({G+%xpGOp?EMX2{tBntA%zdxDCsd2ih0{C^}FBfIzxs@Xb z#r6^3nXX*JPYYBw2=`0EkzyYGp%-eKI|s!ME55e?UhoM` zU>X3Rp7N4DiyC=2^^N-Ek0`4K&sshk9_a$Dz#Y@KaVKEbhU-$PzRu46sHG#BC4r~& znwyc7*_g4UMwaMs3#EhrJVK&KDXb7xOI*Wbl!zbOAb{`WvMBG+U^bYpoCh2&LyHGm z9o#P=Z9*`X3N(;7ns>J5t(7`HO8%#5U;)TXd%Fe2u+xq0Fg}9YaCiaTB2v)s;1DY> zocpO`E}!YH6~Pi&5Y%GNFX#smk9WWJ>UMv>L>7b6SL`x=jzZvwS`&2EF`yCl@;oSe zCFm`BzpLCA>(h89*u2YSeij!>?GZ?o^7B~)PH_@>YBy3w+$cLhhm+btC2J)v^ta{y zt*o|PlBVMkkvqu#EKb7&;(Kqr%1Qx`+6H8c8*rRy-!ctlfPs#`7$f?A9rj@)GN)-( zYCy4R;^m-KRR#g&naYpyPuJ|R?|;vHtr>s}rr&*Za!}8uT#bbsEGXp-)8>ZU!noH8 zxyQLNk?i)9#k$~sxhwyih;&HEF+R9f87Y^Ua24Jc{JBi)9zbBBnZa&oL`a47eQm>k z9l~QfDdCn|-cU^HZp`eIi*CEB;c!XX#w*C>fS)hTxHWKi+2&XWcZEvmfH_M;ir5bT z>ZHfYQz+ie3fnp;&tPVM`w$?R;X`*R7Ox38+?69`d_YLF*0;32Va-3W1Rnm7BDfe= zM>v@(lN0?6TtoS*F1s9gB^6uR1XR>vcCc8Ma%*3@XjLN&zf}M?!n>P?uyy?kxT<1I zly)|W+G*$D+1!1a!6~JynOZ+88h_Sp3kJZqy&h2|)stSwc4l*5kT4G9WMx=I<`NHF z{)VA)rGBIQv2WI_G^D3yA5ExH;|82%`2re&YMh@n+>H%s*r*j;Oq%_zSwNm5-cyw1 zxrD^LkWB!Es1+A11h-K)u(hv5q{*~NTT|DboyNTGaVMGca1yRhPoU`6W_$|m`SfE7 zuMA9mP?5Ae;3)h-8ocy6I^eoC8Kt{z8djAFc|2$*@O2AwJj^ELJ_xEUg8#h=$jB6H zwhor+)V46J-Soz{mblQ&7O&Oj3SUvzE|>PIIKlSj%LD&0ddXB#(pI=2w?TxRUacO@ z&0X_98m|^bu{y>>-2{LKn4ODd4jaapOrck96;z7r!&exTByh?p(B+uh3T2f3QHr&y zBT#BsB3}atZP-A3vf6tV_UY8^Gyc!94__poXsvWT(;le!EJMmsryfXtkqDgMHTkzn z0}BG&8bTszt)Q?z1y?ADfC|9}D>;S~N(7!VlaKpv8nNM%*jv?i)%w3!fmq4$AHpv9 z0?}Py<#;I*-y#QCD8Cx_-mJLgoqVQkF)O7EOX8d~$2d}{x=ok-SI=yu$0#4uHH7}f z-nes#E4_{iR_Q7B11F2+=yMKi3(q%EL$qS zKTVM&_piDBc3>~&$$p$R5swZQbv~~dFkM);Y;uTnR$^`^Oa7ypb{6K7UrySb#QqRp z2sTX4ISB7$Rc&mxL3Q`YzQS_Xnp{^*&B4xqs4ayz9`%r3m=7X?9gl(~9;!p4!hGH} zVtTjY@X&%D$ZwsVv~eKuQqMax;rMl1;!#q+X@DYCDUt8#sLtaM-Oy@rnW{dUyBU22 zicu9`mNw49>w|DITWrSHrv5blu?44NMs;d=YkH`dv4VRAjj0k9A6yg_G;?rr0hczl zKX*a(V5GV;M*abys}?9Q*zwo#1D};**FnLyQqIPFM+T1Xyd5t{RE$rs+gN+A{&}Fu zLVqLH{)$8?#}SD^+#-}3gF7(!-H*7lf+cM%AMzM<4ck?B{9rd1(zpOi+e&yQYVCBh z31OdwHdTUG$U86YQn_ge=HXheEtInbz(1}iD$Tm8zPyj#sPbRVnEy!K=F(v)qNgrFyOYSLH$W#DE~9Y97hin#+)t*36A4u0h-ayN zJK%6&XK+)ev_ybHJ zCX_frcvj1J&8Ifq{49z@h~g9ynB^2cR`Z2>P6mOq-*#Oj%}`(`2+P8$ZF6VAKyJ<@ z$sSI1BnRp)_l)@cf!K~?p%eNmn7HUndP=~)n}kHXmZXPR9bkqUiFDb;tUtdwq*zNF z&hT=2A)69UUDYq4;I#%vN`oR7Am7{a+Dy-x>HiVGW1K-SG3vw|W0y=aZ@qnYrH`>NH zk4Rs<&$%U%w|Rh}TJl_eRVy=Y~n zlHeOi`Ds*eI`}n6q8wa&po+{_X+&~YWBVT=w>Xa)2LXLzeQ#I1Lkf62)xS{xoUkVX zG{yGwn5E-YER3tEj!yBD#A04d;OK)&Y?8AbGU%zA62ZKewZKv{7utogtLN;?0G&W$Or-2=XmhSB5t1<$y zqy3pMb!RYMAC7x*(i&ep#KXQ7w_|ECnQ;3nWlqD^cs+hAS=aFuCcs?vfU2b z<^1=pijD~xhuei;sHvyk0}_0-f>AqZ{))Ic#6zs+@L=I(mCeiETmQu3D_EoH(NCM) zQ+dH5TF7B@OL7 z8d_{0Cla|IEI0CGb|R~ux1OA1d)Wb~8W=#K)a$yneH$+|_A2Zy)o4t5G9+#A^2@3h zDV`b-0v8duJKElt-TURf{f1-0C~jE-60ZqzKe;ivOgWE?c`sRcZ>!BjDF z@F(`)S3DrsnxAGtH#fXTd+lx6lzx936`6uIAwE~uA!@hRo@vczEp9qE`;^LY6b~|P zyJL=0Et^l|kz_v&7Bp{k}P+hlY-2h+YB&RBlN+XrLz} z9y#r4M6eRC${^2O;^D$xei#jw9aAp7L} z&YX39>X8(?@U>y(XfTZbZc7@poEbbr8#vS=Cp41vCPGXpDlDd8(lhhk?Ztlv% zbrfL2^0{5JA<|wkqL-QTRn2Aw*tGDUbqd5v>~RGLi{Lfu}mwq5=GR(KcZ? zkJ_uR1-V*K;@&6u13Rn>PqBSYYo5o}L;J^^L+6m?15^qvkpS9`5hx3)i(waU2F7V} zV~!vs&8m2*p@(Fgk%wnsM&Z_9GO$xCy-F|)cg89;Ww9GWkY{s6X}N`>&AsW>Iq0Xomu+dK?z~ zZyjbg!=qe5CYZ&UFh~QBpLE_GhLPBYguTK*YYrgA(UI2AS{vxAtTIP4wB>F83uJ0*G8F6RNiaM^c*b~RtZ{*w6;E|g zOrf*2;C_=@JE{F!`IKlaLG2*!t_xs-CeHr*QIq7F5wM5A41cc4#FvhlU`l(~S&!2a z%EeoQH7Yn{2OW2468`8fUGFW&Oxbnm89_6r$SkC!kUaAQI^Q#k>z0j2TYrs+#@kpo zZz<{ZnS(J0!39$)VJl1;z-5`C6X2SUcz|Mi>tFs_xEU=oK18Ul+q852F71d`f&>T) z8MEDZNV{YhRl}nEOW# zt?14MxrVkgDxC_Y6Cue&rjcJr@I9h$=7=j2+>L#H%>RGnm0NG7mAVQFCyJ;~1Q{yc z&D%Wbz9Hwb#n}m%-LSQD;~rV5C6ipE|v zz(DmKuM&yx>4`?MKs;gyq1bd@@={nzkI7|&)1lUBGFKJxFRv=${y3x(jhAw(R>=kX zkIBh^?1J$POycaWL)KUr$dsZ`h0syJhd~&Y=d1iNk{3tn;%yNJdiM(14jtv~4>+Rk zK(&1SYZZl=Pc8)GmDEDjYCovEcpN_P@f%kK5@ZK%;Zxyw-u5hps$NU%{^(~bPIExn z-!25*AcZI+F~0U(NnYK1m`mr?>86&Yy8Q?wF@xCLl37>)&M^$upQB_q)oVj+=93xr z^?tKgU4bif3=VF4%=5$l)}iO{(B+dvdGJ%t-a6-Gd9Ed)nIx_ez0iMZXs)mHrrb-@ z{lfZ}Ul!Fw>dg}q)9|%~o4P6U1CM|ug@UTvEtE<9pW{>(6^JoM2$9x+wVhS0r_j%e z@4w8)Ohj0DeCw;*gb=G@h@3kngJg;a^VksKZ~pLlueGPvkmY7sk%G4@%TzxbTXd~n z@E@=wkTU^NgXz2h=`>hGT{R2H2I`6o4~h}CV+vyYG}HQ^@tZAoUg#O57!$LDjt=yb z)B8jJDh4DAGQ(D%IwDMT#xM!b8ua+kY3l}|s|93VV9*OauuMr4La%ATo#;8_4kwB} zb!bn{csHExqpZ}H)caT=9~*L=C+og0%2+HJU6A(S7LgKW`W97AdmG2&f&kWTZI-bJ zzW)f5rve#j=@dF5ERU$@r~i~inNe3}<+v{T8e%MxFNL}}-AXgfOaMYSx6plHzI6x;b?>QoYh+tgYYlW>1d} zL=--ElyCVVospj^-Z03zUhZN?T8!LY)u6k|VXS_JPAI&GEf6s3*H$rg`V>aEHh|nW z2wJiQMUIL`4eJ7>ov7e`=-{3`7#0F6X9PjTvvrs(+D8Ha^NSv-M+##)mUNnsINsJTk z8F>mGKzmL2!F{;R$gx!K{Y?lm%k8~uDNjv@!n77(lMK_fHX+49c(~0MU9dDrMx!Em z1}_5*)ol!I1I}&X_!TGvswBys1j2<31&sP;r3lJ(Q(YuxYLNb2gw*E;(YKdj#G(kc z2J+LOK|yj#q+~zu4DvITCAm@|sAR|Idg^=+q`#yD@x~*tw7vqy=pw1vr_27}XXAl* zxZBDtqfOU>KP5gi3+y~^0*_=b1B)@Yj6(skKz@~pZ}^`udf>Hxu^)(etfL}&=&K%* zbWuuln@A)9g8y=s2yFS_U@}iW$b9Vb%KB2zw6~}@hpLK{jJ$YAtW?XwXH8HCqq9V$ zBLv(q&zDOEpPiFf`~#Gw+OL!uaywmZMSb3g88gOiH)T^KqakdjVSVp>KZ6w;bSj9> zR4BiF#zR#zZ@)Y)H5bbkJ6`KEy+3NVit1l$D{WJki|3*F4c(%}vOwqMNmNN<@>(O+ z<9Si5YHra>UJ?88x1`=N9z;vtJmTJ~&?t9D46jCYHH1jQ>NZHN?x(zbxYx)M_p#Md zRC|tK*n#ke?VyZM$)&5KaRZYuhk^PZ`Qeh-60s3yAu8CTXVa-lXARroAmZn{HqCzi z3OtVk`A!nuMKsTs7t`rkO&U4xXM*>pD_6o#rXBOk(y(>{WbFp^m}1?+X9;pFv0qVY z3qAeZQ<|fKsT?SQdcoS*g5ZdeltcR1?S2@lf?Hl0oyex3nvcusnZ+&bdGB>6tQVK# zsoFPnx`;J^k$lO%n}n8=Q3e7NTfH-lXm*NhsP=owuL_>)g z^3A9)SUkH zTE}Tv7|Pli!QBFO-QsuL*xFneY(nAIa1t+*?ya%xL?1vI7WMGYYDGMGx&Of6+;(sr(>0jCXk4@pZEmKDl~ z*c3{xbIO5-Zp%*FWe2%ZAP=Q^VGN^dfFX9zOVmp6$+-Vx6z%@MP za@j3vl`S;O)LPBgNzn=aEUvzvd&lRQch&xc4!FHE@Ds=6vsRGUmK8~UY32{HnLP;^ zFdT_8p)oi!b}wvN^lM(`o49{9Kbyv!h<=$w=t3aA+Lm$qy{tX-Nu;Z1=iuczB>1=1 z`uI>Soq&Ws(RF&|^FxqRf>Nf~>0YX36P`mg#Lp8iv$pS$2JpXxdIDA4p_UEeIzUA- zNni~euYAH05SS*wjbb(!WwM{HwCSYEKmLC}R`q{%HyVTK&_Ou-jY=2e%`ktP+K>Pt zhks_%uw-MQE3p~B!E&;F>4474339zoLy^rzG2#$@H+*d4mmsuGwa@%%Fyi8JQ`^W# z@Zh?mQwzvhHd@o#fjVqKT&;c zAD6ku1~t*x#xhQEJi3r~U?I{1SIsx0@?q-6*MKSm8r-)vp>Um1$@py~YYIk?R>Qfw zHb}a*MU93Ri+6d@Q;hQnPii&=_HN3@dI~K`qNmW5FM@$d{Okh+V}hU)g-gKk$hQ+X zc9Wz46k<zHRGYIPV5DjZ% zZWckn7}(&KsH?@;syetnX|kO+WC7GXemH@jU6XU#dmZRD*_jU{sjrjv=_*@d>P|KJ zcGwHBjKr!Vcbj^ywrEzNy}1V_@s!^2#MeVcfhCA!sYzQ>n3Y)(yxazK{2q!K+3^Cm zDkeJy1sw0%7mYGRsl7(HZqp%_AK93h@X@hyJy)m~37KxwuW{nx9+?xTpxZq>hal@c zhiHW_wc1aPqfJUql}hX%i`+K&naw{%6|BEF7|tGxsvd3 z(7iV4Mb~6Z%)`%{_yIO{9lmRkzVE6fni?}Q^2Ov0S=wbkSF1I0RHfa!^-S`nUyK;A zz})Sjg_J5E5UI%5eROERUrJf0Qi~Dt(Mk>{f{)AfUNU-~tzBjrJoRR6Y{c{IyX-ORK3=2W z#Du67>JWf)EKZoV$r9tWc1Rm5rN3Q#e9aM!q+NIr)?FU%*QSSh?Qna#xEXkGJ^BQ1 zJDxhth(-_01+#f?C-TMhz3fm5eTx?M6gQnLz4i?-U(`_WUKV1=WE|gSiPM|xGe+QV zre-zwn4Y|RbyyW>p1*A0#E zcxch(28$9csRMEDcLSc6%!aa{I>_h@(FmFzs@%x1oTkDNRNoP3U@1P_+bw|H-l|bI zJK_0g*nL*(U#<+3WoVI$2py~-;Y->Kcm%%E8B~0dCPbDYhCKmY;@^T(tVuQ4Q;c^cuZi_2^IQ61FXZrlzmY=_%pqZ0w#mJz)^ z9qQ)QHeRPi%KGbt+woxQ&Ck@Lht~`z5x9)AYb@w8= zWy zD~Mz^gP*nZX(qo29TqYAuPGGMbbjrs&P^8z$`in4%79M$TPAI#-d|pM)>E0`Fl@p` z#&4xyf`=oKL4KDzkY3b7i-ok zo(#}{%qA|UC2g0nu46uXr4!=SJ<)khe4t94ZZ($jX`2zkjZHzpg0m)%sDR=8$kTnz z5~1)?BQqRRZ6Ug5C4q*fICtq+RUCUhRY{u-;kUEsT?1VR&UFp6r0qj^A|W+NRQ6-K zwMrgw`{r&h>JMBmehCV$)r`~NK{eZricQW{ehfUjl&Z8{^F``Ly3h*o2wTpN+ZBH3 zd4=IQ5#>%z>+j@DZ2?UVl%=-7MZ;;8wo@o6zqp4ftdW(_jwfjByY|_g-@$;zTAbe^ z_x?)T){dl7SW70NL6_LR33_e8lEHf2(*4y4j5E;0C@5nR(#LE>hTB)x5?pvV4l+A%we(@-RDERfadg6rPV-Kas;a!T>gBpPtN|h-)|Z-c z(_xFtWMu+(i0341%;jS{^bUYhsy*d(OSb3%Qp*^{RiV>uOtEZ%+-t*?!BS{u$F>8f zq=Qwq()_2ay~2dgXo1@U9HH?fCat(yd7;9wANu!^V1bS;r5sf;8nc(1tq-ItOsw|F z>TJt2=rwXAoIsCsD0AM6w)h~r61|q5+69#*N%d+mrF~NMAFil1IbkDG;KWfGwx=w$ z6c2hR47Y2PTTWrd6G74D-C1Y=w-`2OXlFu$NGPeY^H1;D>#wP*ym(F#6CBL`cYogsAvKtRUD5YiS?0q_ znSc!gD5348Ftg9`+j@k=xC=H>fe5+9V3ZygDDJ4Q^+^LFR-=5et4h&FX5mPS(WoVM zCqXq{mvnzH;^X8@0;xhx(ibdEKR%V8!EcfUazHt$U~2wu%>v)e;%Ps?Lf5wWRes_r zpy8~vP5(P5Nakq`>h3C)4w*PE4BVX_@w1qLNa_A6=O69fUv;Cv$SyB|fHuYlbLev(X zx7GX=>X;ESBZKFu=RhTpw(%yqA1nP&PSfr$ol|s({td`^kaVYak^pmy2dV0)j?+`* z!t!gRuW-yne?-im=N7AaGJXmy))FT9jk*WF1sfU(N(|s=eyK#DAvR6;Z%{6i-DUlc zN@KO(DYt9FYT0hnz&<#0u?2b1Cgg#h?;;xL!cjB<6CLBb0QpzoHj#$;A$G?Xd-3u4 z$2+(y5X5J?>la=cB(7;fPD#Nf`TXj3o=_yezCJI32?x)QEJlftrZ*5l$sCh(6no8T zbYJrBOV&fyBxjToQ^q=IEk+hGzS+*@Ax^pP5ihNNFHp_N@=P;h zfUdqD>j-yrFH=bq1THS4i9lutWcHui&EemuwQpliQvdAMSF%dCDw&T}lP z$s8XBA15Aea8nwc9U?OkIKY~6zdAtuwS*-+m&VPF9v!8k}a%S$dYE(GrH%s*2f670tB%`$Jvg0L!rR{1_#Jawf zbe3VKm0N~oyhAo(A9wO%Jlk9FNKFRg?nMoK=h8aGdL-h8J7>r+0>nzmn#6?vPYjH(PX$)m}n^#xeb$<;#C+H6IYwH-+XlOlbKPeYUHn<>|{)zg3E3JxORp z(ok{EoLaT0f7+qVdj=?ti_<7wSK@p}oe$&*tM1t{#Wv_$?cMC%=y4#y0+?B7Jl}Mh z)Yb*Vc@_^dMU5x&DeNjp8naE-Dd`#4V|z4GZ)OyBYYCT}B=xg`r4Sy+20FRqLHRHx zK(Z;y(nI9d$sBYIh5C~=icb2so&@gDEj(~?!o{h}XV4V)#(F*UT`Vuhy}ij&TnP9) zh?)LXm9V>5IFB>9h}&0a>Nz|e(P#iW#d0;}>AZoHGM{t){e&&`11 ziIiF}vQN9jSP0O*kO@EC&Uqj+woapXvbzF8HlEfXl(cM7IGzF4!6j?xGN;T?KfUxWptn4!E!snD&Roxx zrm3r}i<_CEaJ03X8#|lNoYey3YS122TWOBq&80UheFMJce#T?Ud+tfjE(|F7N(K5x z<}vu-^z4(mBRMXtO|BtLlcrS?Wg7tB*^Mj>I-W+28Ymth<}1Xl z&vMSxVp3_0Fxp%MEn&m9Y-dVta(H~1T7etshW`xvZyll{>PLtwQgO4TB&JjnTpoje zSit}mxM!^jJP?v0#bzK6h2oCuLIB1VpVp6sdMgXNg?IFkO~cdRMsdrIIEku<)@?sS zOliTP-%V8a_&pG#?(Amo=ld0WJ>Bk0p(HL5(o4&7s#KawNnr=u+bpB%{Iz!Q_3{|j zphqN|T;3b3o{n}Zvc!D74dcB!#&S*(^B%9|#l?{@0W0&eXNqn~ls0A@;9T_@C zRj%X?Cay9gGt?T8DJHRBrO~@PxdkDZ*CYejXCW_?&eKfND_#9-nD$$=lq=qQ!57&O zaI!hC5wHw|eZ7D7xElNty~MBK(|}lhaxu=|s8au#%a1$DmE;Hu0gO9Jk~eh`h=x?x ze^LhBt87L+O{hekq6v%=y@~&n+T7_dmwZL!{uppn7&1k5KpO;;gS4xAICIC}C{VCh z?2-aCZ5^P`J6B;uZ@Ve!n%m2gua9{8B5xMn=D5?x(Cp->dTq(uMK_HZ&;RW4)x2dw zs`m5RahSsB<9V_wqv$s1intcuos*t?b9$oLr-}@g-6YsnjVz-@PpS)ZImK*XauXN% zx9>l|poRh*Py_Qs#X&fR2#}gMme7D|V&c=hg*I##7*2vmvkdMzk|&2Zr`x#S`y=#B76eJwFRO+6cewVF1 zCq&+!c8pWYQ5w31&o;5$CNi>S+Kru_o@)*GMNd6%KmoZDgfD?<_;3Csug5)G=FoAF zMhT0|j(`(oK0IDat@;O{ ziDwOPVT3U=PFuSNGbk~;gOR7b%?0rhvfos0iOKBCrJu(0rjZ<<&UVb%5|V~cdiAum zd2=98A;<8H_@VQP_Qm4IeOve^6~aZdt`Bq1mC71AlH1pM%4bxYtb;VDjFIW@c8eT@ zLP)NqO&H3Ma;4WtsMN32PVZgsEh3mfB6$FYtsxLKQ~#_d2`zcPNY+zQoCLom)M5);aooSDB)ptk zQ`wQJ>_bfSr#vPnswT4k#w^I{Yelp&Dyd7_EWno4cugepo)J932FMC0br{{zOMPB54lU=!7Gq^mBCHHVqUBZ=oS3q+tXMb-z*zb{u zgxPjMoc-Jm-!bkpmtd)KIH_^r?#lj5Y=s}GWL!-cEVv+RdJvk#g)|1A-Q$>#lu=FX z25H5@nzgWuZ=xNa8w9EVM6yC+dc z4XJBV8VN*GXP&C}zy@l}vz%im{-#`QTa*^3wGkCYUvi8fCEM(lWR*${#E8;Q2g2hg zYeZ(D1>?1YaIqyN;{M}dl(vjo!wl)0%hA#=aGN)fC>0kRALtL{br*&yl zROkhy?=2=eM?V-ek}0fxpGLmRNo6*Wp7wyd z%-f)X9w0XWU9U0*&rrI2|>CK1I{hYcE8IQa2%Mn@9by(D2wll*sD77nt8pU z6>^;GDr3l@aoub|1Ql;qd)e(5oZ9=RwF=$pyRO#eo?L5i!7R+UGPJtkt@1HvnQS>{ zmk0I-7ren$(TL{D*Vl+%RkiXb!85n!A!ZZY=sU%~h3;)^L0LqU(cWBin{77`RYn?r zK}efM`f}r~s6Y8~LuQCT=H}49;w@7=4}L-92|{L6^+QGONa|lLwwUHC6^fv8k!fXt zZbUNG0f-FNv!Y6F)-tix?u$z;2HR%K2s2T9JSN9a;6k@`=7ZqJ0YPg*OWIt zJ#2)14!MM~815^GtU`Z5G##x1M{kiC@B@N#snO&NIZGNHay_jhp0fVA)X3g-d`dtX zr~3FHxT#}`;GLeTjJ+kSJF-{c)j-UeLrZ4B@IS8w(hKf#|Jl+!#4<6nX=_K0C9i;!rrJA5zsz>?wX*yB-mM-0f|11JGa1 z7Ar-3Kvmw6JEB4)7N>$EEE`tm{ykb9T`DqgfL*dA=g%Jompc=<$F&sZ9L(m$h5zcj zxp*qm0OgxPh6um+a}NMy?eHnXZlT;f)u5~!pJMdhIb7)v0dct8dT&l>nZ3XXp(5NU zMWs6hzeLTZ)=}6C*n%2*Lo+JbGr9gnstY9dO{b9Uz3_mK1w$Q4fB19GyCK3soD+L1 z5*_Y^M_)L=b1UHY?{rz+JfQUc*4ph>Vefjwj<4|*FFW2xL(xdF0tjS{EMazs_?M?2 zMX)w|?UvBST1A45QtA6{mlm#G_uJ)!FxHROSoW@HRWZel`)m7f(Ag7)HW3TG8aQw3 zE3H_-m8M^`N_;WBeTQ4;BPcIelNT?W5#b5{Hma4K9l=>hagEB(Q2#2+LKCa zG;EuMXVUex!r_zM5iYBtX7nWG?d1i5D8-xcqW80M6{bHyfQ4&IEDG%}P|#XPkcu zoHvJ&43;~`fYdc>;fe*Vm+CtJF8gGQc~_lX4)@dY+P~*N-+rtHNVZ6gzlmrt2u(7} zys-l{6;ga^i_RwCPVSiVi1ITv2J@*`i+GJD8-4^@{{;vY86l(i6pn7gm?u;iuTecYaqrZmWauBxE*5C5KeXi7v6s%C?H2pHSNKR$32)a*15b1+qL)jn z_Un`nx;`aDqzg`o;*WjG_;bPxoi?jgy#-@ulv+B%SeM9oV==)H9WPvjuSCINZgU+( zyN|U!q1=BH2(*^cZw*W6DnNaL{=v+ruUf6c&@o%29QpZ(S!NZ!JochhJ~zd9sxsUc zz)L>53_Kwh6w*{tEci>{^cKDAaDKPtNT2D=gt{93*mf{W&hH@$Q~XkgMDR32A@C>Hp&G07yqdL3;{0Fcj< zp=b>|q48_;QjqxGSDfXrX+>ojt^6^Eq27jT|+P?p6S#l?Wy^Q*#C4#yTLp}yt@wE2I zz}J#}0xz&u;25B#zFOilUb;nnC#gWm*N+S_=8v2eBgnZ~=y{0k9n50FiP-3H?#lOWYU5RXuEVw)0t6kd6t6U7{tk$-i= zia|?&=uWINY;bt^CBa~QPD9~(ZgYnxMYS#|3IepZ!d&T9oZ&ahHZ${}HVYO{SwCbu z7kkWyhZ;mJ&d_e0o%1At5)TrCZd|WC3;DOhEPokYjU2)FrN>RniqTMRIOgh2&QM8DqR|!N8sKTwyRnr`85GYN=6w~@hg%Y|C5$Ch90y@_idIDBdJwS=rtCCn0V1+uC z-pIeq+0p*`3=c=wgsaZ?;ou2W6+#IYeq+9)Rr9yl)*a=ot50Iiz@h_^=&AHHzUM!B zQbl~bb2VVyJv|tQ7kucGw>ROJE_)LmT*u|z5gyXzgi;rY2AVsB$VIZMdzW0%&JQaWl%!uE0pg3izjdzPTt$D)#-_NbsrN=J?KRP3xQMO1`_ z96`0vpyZDzM^wh3{i$ke3>ZnB=YLPN7+o{kgibvAnkl9stg6n&Ng7^?aOIG;Tv`>s zt!3WGGjE$2-Dn&-+xN`tLnCmkyJDKR2v2y1%i^2+5h;o^4|Kzh{_Pck`UG=yv;eTU zyjDuTdD~r!TDW!Oz5N}Uvp`9sBC)w0+ixLyVju{`#S6w=65R$weUxpzt6nDxqNtxG zEoLSYYEt=$im!3~s0c&e+r!ZxdxG$`5hr%?QnGN|#X`mNz(q78 zQfG(iM+u^vsStqJ=Xd;6^6?nQdCW-=>@g)M_p>p6e?Ws{c15<%XloVg6;SB8Yr{@5 z>g%$HA8B}dv@b3pfp>Z0@>A-V)&w*Q_(k8tM@Pl>diXW-_1YjTMm|D+C_()Eb7_Xg4-^niRK)Ue9GWUmnvCimf_USt&*|766_(=fH zDTCsw!)@>aK?$)m_3jfpI2CYg`VH7vYOpCYK3H?uD9g)MT9PmN|M&6}{!5HElbc}$ zi0x(>=gyP$Wq;GYYA=u~Us10kOhS!F5-0e;fWC5q93ZDa z%&uYV27$KT^4Slqcc|KtP<5&bQ3tw@G6d^5+!rM&U^U6?e4ReU-on`366HJP#3Pdm z5o7KI?rnIScb@Q);_lfCEPDfy+Q=G0!7ZSi^oSoViH0Usuls9U$w;yHe+bDyAWS0^(QJ#pQ2HT$G$9`m z74l_9@f+91dqszQf`9`11p&o2$v+2xhpvQTrQ{{%f2j&{J)k>>$*KS9}$*dg~`i!Km z^>ljk*hKx#cbQa55!Ov_5&BLK(EhvmsoKZkIOduTfGoc-5J0O5=jm8wcY2R)>ZTPM zp*vyMM52BBO7tO|r6rL_RwZ5BheVjo0k$RddMKAoSXTL6v$e-p14*ozt_o&a;b)Ut zUb^(C0FI{0EdS&KuYZi zy?O&)l;|#G;GASRxv;h1H|Kw8c$|hf=gFQj)TWV%AX#y+XBv z&Q5A|8Yva=BtPQ5VW1+=GK(Gzhr5?S=KtNWrOqyE*E3K;4;$02{o3X zac{?w7JU~Wd6Hw)f5l*PYfh++9qC~XP9R_KM+5v)y5tob&P(4Ovvjn4#ytHCp@pP! z4sSjU>0$LmsHfx|bffDgb#vGWTibp$P30cX@A@!W54mHDZd290y zgcCS1V3T&w5i#P@tU%>4jh^zlm5FQ_BOIEW`_G*}7xF-9h!U{=+R7l0O#6~-yqqKe z+wf!1wbRH0P{)>TZrl_cp8_ky!tJs$`A@$#J0Yf1gnwT@O>)`m*9jw1;u0ge{ine( zszc$|xg(?aN=FN4|EGMbH8QEU3OX`sxd6Hxew9_hGl00v(z0zK4a$5#*V2?CzNXmG zF(lcWngbL-0=uH6*um;!ELQzZNYU$F;0;=7(U}BQp4pc++znA;;ri2#Mfy_YNask{jlP@!#kYQS zWzwW3KkJKoJ1uHEiF+R6DlSKeKpMjWa^?1N5`k!D|sRouBTXbT6X;oqOLVW6X+ax*YwYH|xB?_xxPcA_ z@Rf3qx}~Qf5^NfoJB$z=ZMj(lk+^T!)mIYx}vb zt3B}fgyN8XHdyzQ%v&h;8j7x2Ra9S6prXT?QETlvvLvi}Hl`5bdk*@E#4ehh#MN_@ z-bbVN0-XI0eD%Yv*|5w5?m2-RcSDhwtCDNK?or-kJFeLt@Aw|H5!SXYCUANqnEK`W z&JjnIRq1(@Crl8wsJidnS6<>S#wD;gXj@1l>|J_Bm0rh-*^!uFNk`)0@^Qhnrk7ZBX&b{N3xf9v#KAK!6J1YuXggq5y=EUyaWWfKlke2^e-|l$Cg#UIL(C zVAAjp`5Aons$DYOGu3}<-!%a}3LxW_MpCugp;MV0fm-^MUp@-8Oo&y58=sLk{K>$; zJ?@sgB+DJeBfIB$vhhAfE}V9h-=Wv}T@jimJarCA(rlp0Zokr?Uz!lADRNjdMkHF7NOw^5L#;bdC ziS7HpRD$H7A>x{vK16F8;EC^nBaNM;Gl1tI`|EFtP35htRe|`;Teq!>Mn}77qG^pCL{#L<)vg#5AcdSI>-^LTGY~HtI}s9xPXTy3K4}g6 zsf%02n;>ty_$SLiKZzsMWb!sDSsRb<=kA)GnqSpbvuERv4Iv!+0I)7gz6rGn7mksB z)YHoAT_lC6UHfQ%!DVNpu!vo-&Q-^%F<`9g&JnMNGKXT~cWj~}OH#h!|4YS^A_>2! zzOZ*EhTF5Zcz!u;8$l$3zpn8R61Bi1W>7{JXKiy|8>VQ-(V z&q}M18-I#v@a5(-a<@h?iXG*awy_>$(73e~p^(R+$1mg%Jvrou8RO8)b{1REf)!4> zuYO$;F)&;aOtbyA#qt_aRrI`F`UR=$9gwC%5H}-K%-&-9FLQj?-jj-qN%>triT|uk zH&*;+=x__?y13fExG*ngyxUXJmAPUx{xLZm1sicRsv;2Qp#Aq?&VCf zJ(lJD5L*dCoAs)Vux6@6R`TBH420M3~S_>zFKY)qDO8YI8g<2_khz1@5AeE zNx7rZo+RZUBT*4f9rHg`8c17S_)xR^RtKRUv-Xvovyf}o}P`rct-xU^*^oW zqNoy*zB@BR!}26%D&f7246{pbC|e#gQ^V7wfypbS{Hg&|l1r8`Aze?HI?s1D+uvR@ zfUyRj3@Y@GqTb`R5s9PP2o@2#3N3a{EOBfZZwp_^JN*({MelfvROg)`p4xZhJXw@j zP9DdX+I9prxM3SvY)KcTz{^?F;W}O(mL}xa&8Fk;C5$X4M!a%&RI7+A1A}hEyisnF z&OkWDjORSvnO`(s1CB0HbeM1U1iiQ6m1RL3w%(KyKrbjg6o9}LL77Ou1Y|Lks&XeY z-)NfWJk8tTC_H1YtLvLHf<7HgX&?qOm-(Fb!cPz>oOMzb9C6|xUP(F=Vny;KuY%+> zl!Ca`2xz3)IGTz%Dbhy2h-CPn1CX~z)GDcMIKFxq{tfBBU*ahl`f9;4&kzqM@Kf+%~Er(X?d*500J=WOLZ6v{VrjZWmtcx z!pHi3w+~Q)jEa9aCGla^clRcd z?;OM_IIxZQ7e$+YFFtRSyKiv{BOVl49y#^G^`Wpt^v&7y{gt#LY%x4eSt$Hb_jS0K zYlWHct@b(LPa7PiYhjqkyHoE*+YoO&{yLbb(^0K2jYNq{xi#I`Dc#XOCVvauDSEvJ zQLxH(RovwH4~DN>fx9Zl6sr9V)jZ-&IEyQ!!%UCQVRgSH6c*)oipGML@Qt<{wbw2H zW!`&LSH*HQ&Kp$iZu(1EyjgV4s(DE-YOgVPnzJCRL59*vI8*uI{j{wP8yd}9YRm|tIgh2eTR?#R7S_L3*k-umi6ugzIR z8k)m!sXil#(4y?NPdv~(Cx{m*4Rk_2zq{yz_?B4pRMo>__vK=3*NmF=|DeyGyDLq-f+X7276Pq;Dzk-j5|fPHJM$BF`v^$}bI*Q3SNM4Kb+&A55Ke>4I`Pt6a0$D#@?9XorkMh0iL1@(fBC zx?`p%d(1BcZ&K+8Jwm`B6r z66>dK4tv!m*nYVD?XIrR+^*|5W%Y9NSDk8SEeA}`U39FY*-FZB|B^nTU?K>(0r zL)qb`S9R76<27hPUy!pl4ChQc`wW&4Cv@asy?fphqwMjlxZfb-HdLsl!f4nc-T-Q^ zD7++4cbl_jxMt0y_EZRXU~_Ab_79|gCB%4T7wgzr+Rc*#kQHV-uxr)vI{P@r>1F-; zE1mHe3rH~t4DD62WzoviFA}E?mDRUW&_Y9_DqmJ4g}Ts>_AjCQRb!|Pyl5(BV}Q2n z^KZbkyz23YJQ2&AJ1*KH-O(DYgF_x2x}eoW{0$X4OtAXEp{Yn{e=>tvnNwV1SlbJxh?Lb8V5?ZuDY6nnNCKE ziDQ~MvV0)rV69Ixj+xfriJUe?)j)7bKEG*ONUR;LrSG53!7{H>>r?Xbam9N+0Z7@1 zFD|F>7_Ea9K~vqP^+0jkbn9ARJ51qKzxhmgJ=(Nhu^c8M*?Ju0BD4t-sWzQ1u50PF zTBvh<-J^j>xkVy$EfBfKoK4&lM7uIsca<(^>=tI{%F&(g0a4z^XsFq^>kDGReJU;y z)=WP1NI@X-4Tl%Gu|LfXvptrwJjaXzcf5?|ev4vVxh5Pu8Ra%1R^{U0Es2R# z3{J{b1?J z6afPX>QHdk)8V>W{8@;h64%6TEK7*}2CrPx=M=&{rUpn&vnn-h=s;aH?wmvWaH~lZ;O# zMF!bcJYny|?-0KAmmnpdlV06+)G{QPYZzmbr^*PVs_h&G|NKi!XhCy*%Qu9>`kY(Y zV%;(fr@~Qmv^fT^12$b>vFBLhtxk`3?lR-W1OBvYHWzazE_$H z$j|b7bQ5=S2hPLoJwxY^gyCVNni<&(g?m1{Z=aNeD_%~8G};81SfWE6XC;VP;(z43 z$Y;8DPC+EqFT9$lXP|n|in{H6zQp{oOrAzyjaARh-SR0u{3tWL}+epC1DY`r* z#uNj^UTS^-RpIr6Z>$ljtlhxfuw^t)Oupy^=*@ynmS8U>zzIf@5+2BDaH*=LEctZw zKG?gy!O{axFt5}PzkO?&7XpKQ=;ua{;?9e(ot#^ZZ~YmNYR-&s3&h}{E^vy1bX0F` z&cnR!)Ka6U=PXh=)JB74l)+clfl%F_k)Ic77LhP7%{XcGG}~YUElga8Uz2Q|E1_W* zJHrhfxooI~w~Am96~^~oAC@z;DgXxc^|kry;7Ko39cbF_FLPn?)6@6Plf3+q zD6t`^^oywu z$*gN;y&6rbP;Y)eRVZQcxF_2>pFVQgM=h5jK>>m}Hp%AF9=!q(v2h0^H}vyP-y|@<5O)K1?IOQT);!*ogDa==00hVmzuy{fB0pY@W>Gd zA$C8u&9x^{JGy`p*!Ae1WS~=x@Ve?gTW6S=oX%>9XkbyYU|-)3CVCJr9+kAZ7j_wW z+WW>E(5E$fyosf%%vH6LaP>;+74X;xfX8ii|IEl^y|{kFpX)$}rheJ*{=WNU$-1eo zHfZuUP1_(-I0^(FH%hHn>W&0xJ;@cK3@wI^PXkn}-dI|0l+8N{=x^Frm}5NSehtNS zc844Tr{hjCvwnb9$2FyZ{B2W%L=tk zywvsv*LLJQ(_U(R)&3<(h(~lnxYVu)=PVoLskl93UkOwppJ72?#O?v0f)_EqO=6m%dM1b zGCl_LEm0d9S8OIbMi`AL9;vglI+W~7&>byxfFU<8)8L8APV*;);2QN2(_RORvRI2> z+p}seM>>}1dc(I?pruD-p2V*ln{q)*$RCad`wq%O16W$p51?^`S;^<=w5oT@Z5GzQ z2**C>rI)dw(|5j)!ElA2x+{Nk^?9Az^hy-Kx+-~jvdh)t{}foc5Y9)nB%_=|e?BIE zk)J-7V&7@ljX+ug;Sf?TX$ihaC2(?6+T=v+I25+k-fsa_EOuJmyX5g#dM{Lo<(YR~ zRZKZ@TC$dmDuWmirNjU+K+eD2{{-CvhH#mgV;_ygmMnAn6ttw1fp`PQFJ}$3gO6mD zcv81=^|JQVF%3+@i8Qqw#a$$=OP8mQp&^|G^98ow%pkradXGER>S14dMa5Ng)Zhcr zk&QISjF+?Sa`C6-V}j1Yteq!gYUvM~^(^VX?NQXa*d>J&4cSrzk(e+))kAW3w6m+V zNp&$CyR!J4uZZSn)AzYt?|KL9gz~jqEC&{i8HhjZTC#E4=Hvdb6(`1heM8pm^Eki9 zV2CzGbMognq}4B_U+HNXKxYWE6Si9DnLKuvrRwaLJsPsAQ73Mhxv~Y}3Hyx~jHl+< z`Q!pm8kF$6k4_%x2H4upVq3<?S#O|UZYnXxnqfi& zVNhc%r!dV<%7%<0YUlITSj^Wb1!>#4E74$L_JUZ7ak=&oz+f=Hu~3943-3!L3uWP- zO6tI{+bGv!mg2C2quE@%5C6i+Cx3sdP)e852KfbA(v0K3-@oTZmbC2Q-0f4CY-l}gW!ig?uiCe8`E6o@OH z^t;N?3{dBA^s}NW$UI#-eP=(Vw!D+keFEb_^k5b)9-6zL>LKO`)<^Vgkd!3z1nMH! zP;VxW?Pju}l?jSQF?vFm4230sE=2DGDGQCayl)q2Ogp6G-tl)y`#QE7dWItyv>x}A zD>K@;pUuh!BF6$Je6%Jwq`u2%R-Beby_z0T#`~Olx=Ta>elM*z4A(y#6Dk+^Wi?C0Ouh9fg zE(Utt=V-D#C|sEJ>kT*^*9dEoNliL2D&{AMr(kTv!?SGe!f^g(n&`SsFS9L;gpBibH|S{ zhzUeOSltuEJ{zT%K;i?1e09*N&lao8Qgi3d(IngO4=5x91CW>D za4QlqX=FoM5&zRUu`ATc_Av3BT(Vs#qSW?M`T-f5F9F0zSrbp2JVc7rR$;~;?(LN0 zW0k{^!ah5dxn~pE4<-&Q=G|uUQ4uNmPCs-KbAT; zc2eU4Dr3D>hyfoAE@TLAlWm4weeNp852>hp{UVSSV3U$rn&h&n(p-fP2Hn zwi4O=1+L?QxB&Ch!#KW@h^O}=h&z!5j|JwDwaT`lAiG4|6zp#PquJwoj|bA}ZC%QH z!lSMkeI16{U%B-vlA_VvKICVonZkF-*`eELU5Mjt4ezEBy1_vV&_70k)?bw-mW6Wt zud42D=e8XRoV-oo$tpWi^z_&JlNf`JAFCGotdl-PEHB?@&38u@+v^EisDGfm;64(i zj57&YmGej+8sjpxevtI7MKw<$;iTfC%Ql8wrahK86vz3By_eL;^Bjna);yVDqFKEr zL5efq=FdJ68Z{vcIKEq^MKuyETUj`eJInz<5N9ZOo( zXQV1}Rs9$#y*t2?i1$vx^|xq|E4rNdDHP|+LMeR2v=&HvNP0wt*OL#aM8x{1q@SW} zoWRiM99v+6dH#^$Cw+`AK&{A6sU;*0w*xC<=8Q19C%0+WOSMpunG1|;wSau@vCmGDqmioheyJMDd`%hq(b2K@b0p+qxMHW*Q1k@<| z8>1)bOpXSo%9>N3FPqI-m0=?Gsp$>wU2Gh0n){F@)QQCUb-^%LMekQwHr5&dB*!PTj9+XRIy_dmde$zoft|w1%S399fhqgf_RT@ z0{!!2()DAl)5=U+qrC_XYJYZl*A@6S|2_G48e!QLUb0b>?nsYoW6uVd;@@G*?j%)a|#Jx`A!;vve1aJykbkmdAl#7aa z3L@)!vy1waJrQHLc&0#NqmAb*@|>E0NvKlBYX8|d83wqKj^9!=UgXMkpLX^;T-n?) zouV#2*9OE6w|l^5SUGwVVMvQuZFgM;CPqQ8io#LO8nz@83#a&*ojz|hIrZ&>S??2j zOmdHM_a=5k+@5x&JiuAIO$qh`ylY#k8JEkl*D;S!5Kt3$WYt|DCfX7=)p~BI=`o=S z41*%9JY|nHge6L9Y#qm|O&!-^E(>+S`I_I45oKIk$!m|qd4DDHAqrGc`t&vwC;{fR zE9)E_U4RU<#j9m;ze5>-DTG;_7rYwAn4Ty(lsjPIS8EoPpAT8e;Sv3=e3`i zs=$vJgkgh`*o=r^l=5fom2zJ%YEa}-T1aLIu=FZ=U!*S^vZCq2#HIKmqZCB zKS|m3u2<{!>s{5hDT5Ik`w)TySiY_^q0a2XHvSOPwQl*>2?=m#!ys>Vd-rD1mWPzyNZSYXRnBR%L#?OrnMyp4+ zBR`|rUgCRbKG^q6Rh;$Sg=&mjn_?lxdLj*W4Tq=sfc504na-vvIMXUvvf=l&aeXaP|A7OhjTFpVD0iZC^`>##NK#P@T~<|_CHCRp=Cyxg*cvn z2Wyt{RDys#K$V>}4_+T(pYV*&zFV_Oo^}VDkC)R{3y8%UO6$82IASw1*lM)+iPF=w2e@<t>CKf%3o;N-n&YNbrSo1FfKK{8jzoOMPS_dshRT=crn?|+< zu^zKRvI&XX)MVS|G37ztO%_&`Y*t6()+@K1OaUwGdx5@Fk2lW|{rwYHwifiNh< zmj9GXb-ix=lt>>fEfq7}Y}_uTd8EBbGjnn(Tz=sB)I0#-VRk)cqZk-gM3|0;)_1SA z%pLxS?V_vQ7-ZD0WD*>vx&TJ<%o%O^!pm0e71{(e=+fh*AJ?SbVU&qeYB-RBboSMc-OMk?EPJ zT%afwWWDwW!ZmmYqp^x%GOqmrc3x0*kjNX-(h5WGMpp|nmmcjX*D?cY1G}VcF=qcL zS}vVuT-s_qroy__BEzg~GYX=mSF3w6fQXmK!Y?=1!Kw>B9y|8`LX(D!*T;fqw$Rii zki!P5QUtfec|AnUaxWOn-ec*wAaz! z+9F;bb0RDuD;-EOyiq0rbVnbYJH0c;ouy<`dkcVKG~94}1isjIc^txSWa5-5K4!4rAd%h%4JAwPcN(xF|b$F8u+x zI4-xd;UN%q_!nC=@-t9N9WLbK0(m~wLm7DrY`fNl;1vMbVSoQF0Xk#H@gd@*4y-Pr z+eu)vYt#?mdb$Na1HP8J8GPl3U}U{FGBR}&a2&Md-@10h5hqFNWI5ymX-!(9u)z^@ z-LRTD_9>NwRh|3I2{VJu;`bDrF=^QuCQ4e2fMDLvYO0 zu6cWOCkvoX-8M#hj@av-;P9m1ms2(U9QT}RKqz^@Gnh>KqDJm9fiJ{2%UyXbOqxf6 z^Q;Uc?LSPQjSfTIybLS|eecw)5l9&5WQIbC?+b=jT^SHk8Ipj$uAVPn9bIQ=O>*M^W%H3O0#}OH7AVBAzo1y&-LW@E$`V-VM`P($u_|uk zo^L_6+Z0jK486B>e51tp*Jc?`6+BRBStC!dka>x=hk7OnAEn*fsm+JaMsp98GV|X- zd0j27+Wc96pT%Jqb8h)P#?T1loDTLm@nckit(7id$XGH^lK74srOst*`lUDlqzHLn}01mnJzmU?J6EZ zX`aoniA_Am@=m?P@!$CfnJNhQm8_s26*OTd$#rjPE#Za74(AZ>Fv)R8=s~B;YSn)`CB84dCWL+#uP)*)LBA~ zQ;+`bF;%JEC9x_OH^shL*Xr8L{xv*>_5{{&o>2cTjgQTx^-q?5XNgpso7Qcg5C@<- zTcwC4pX%?*A2OdYpb+`5U7jDYH&0{qqF4OzcL0_!s@+o}7 zMvgu_FoD%@aJH|w(;B9U?z@vQ=5f+ynoq%uq`F)q!~TJG4#ufw@Iq6qE4ru_zg4Dc zudE9f;0Yn|0A2K=(m5ZLF=f*{xx$O{g1CPQWzaj!U@~OV-!ZCdBLtTfLW5rpZ8ecE zQvFznVB+}vo}`rR$Qn^$ZN(6tIvvfUcwlIfU}Kxj`mQ{IL*S+}gI{wJ-r z!tu8QoA|IrZ5vCv4o!T9;D0%jtc9ppqLwh92w~xVH~jO;WbIvDc@1*pQhAD~sMP~0 zih^Eqd5CPx5KNiJ48;**{c|svkbPM^;_Z;r+8HfJ%5`d@{=NevBP)2)Me#?^b2qer z*oK@qE0xV!x(b2bGXV$ek~-Q=jZuexs~u7ML3mq+lV{M=r9%kRdK165ah*&FOpw$5 zdc;m6QVNV8u{^34?Sl#BmpZUs5yYz8Iz}}HeLxOK6_L`|Fe1AmeI?mZ2Y7`8!HSAe z&#(M^h#zM-IWhOZ32p`(o0N@#5SJGKM4VxKTWqDny;7u(CC0q*$`4Ti^PIk}Td2j?}u?i$v-?8kaP-uMkS-tDI_8hCeflCU6sp%eO};z;H|Nk(1FexI3py z`iJ#o5(50njz- zP1)&!t-!Ewro_BC8!P3QYn#3Q7>-xS`Z+t=XBfwm`G<(pTLGO4{!m=YBZc2>F}Eedwtj1tQvU5qir}R)jKmZs9LC*VS-p z)u9h)Vo6raU9YtUxr;5wmv`j(6t-yZb6fGt1R=7V1ym{ITiW}9LHmU&CM^hKAEc7( z*D4yKIUwT3sJyAFJ)OgI`jf??H+j&G^wsAR=MXMEMMR&tMAP1@>*`Yzad?i{++%v> zPmAv_-#7Ft0L7Z}K|g=H;EAjnQhiTHV^0r}e*p&O{>Z-zoc%Weaa!Ly+KC`2c!C1E z)W%P;0i<^L3G+Z%yvlhCs5i|z{_W(X`b3Wa&1^n}*#hzFBEKNxH9QA6M~|K)mQP0( z%2IZ~L(^;AUKNA~l-Z>N_zrhjTtK$}@JJlZNTmG)NgEk&K_m{Q$&x@Y26P3~+k670 zXt4_=O)KaB#*&%AK)^aj(7t+hLI4+VNpvtMLx_%Euv|1fag1rg=$w=2v#Kib{Q}J! z&J2Ryz1TW?Ta1%-@mMVD3;^5XH7FD9wnHF3=a0j5Pvj(hnUh(A=3=KEUjR0W7J%(E z(YQt6rr6H$jvluPuHhShOP@xTw-we*L2NGTN-KQlK!K`r6jOeMa7u*rqE6%Taki8? z&HB6_nQnsJODgx&0(~|7)TgUZ$B^3ZE>IlqwITblBch_8)Im6WP}BY2bPXJQG4*Na zgO+H2mTj(rYM66w4{X7`3rRd4r{|^-`QKPnDfAX^2+8|OLZT;Rg( z*m~Rk8RB2wOmx}whw=K7jhf6z*a3Kwv(Q5&)pOY(4QVY^iwofY6HK}dK^Hm&Zs&h^K1MN&QF0lNvy?@l?9!YymI)3(nk2yH(N z-9Lw_O2DKc)uF!9kY+}n$a-4Spn1`)T}OyM_9DQIuUaJc561q=zTmwXj=58)@qTVk<`0b6Q(F+mX6VMPt4ts>Lq~ za@_dk{-0PS-?Zi~p_#onmF=<}EiU_UaSsLh+x57LJqT$?t7{4CttLk{fqBTC0L@(# zc>MUc%;@lOoFSsk!J{^rTRJ8SxKLNAgfrpwLUQoL5bDGCF5taHf@4zP;^$l}k@Th!Mv}4p zAW~=-EW=x!lm!{NoNET8yJ?%@_}87Ti$Gzgf~R*L&{Oj|*_M;aV$~KF?F4^0zVWgSO5>3kO}3ATfTy`- zp#K-5q9uMTVvk8xoqZCaeSy2w2C~D>)J6yGrF@KZ9H1`pnocKLVW5VFqp>*ZATq_5 zDn1Ky0?|UM_KYkurc_U#b|>S}ooHj-wMA_|<=YC1c)!XognqL}MNGUSkbq(o9X-** zkZK1W`Npwq0c2Fs+R!X>5tWvYW*V_JhO0P>0wmpPS=fHq7Mxzv0rTCf8QyQ)ig6&~ zuUT=t6-^YxG2i6KEUBn&c2hT-6Ax+h3;p$?_R9Ss(gX;}OAt~Sv=_d2BO(BZ^s=0$ z->_04k5(i%PYJChGGI8QFTi@KZKGLEte{(qphpj>c*wB}5d8zb)Bv3$%?0s-E1x-} ze;IJcpA0QRqyd+11li}*ka>FTBawwrKY(Xhpk5bHivMw@zzn1J`L{T9^Xx%2@^ zE!6RSG)GD&mSr?i%f-{*#peRe9*eKe-tD2+zb%si9hub}eo%oy&!MQM;-dvsHfE~j z6}hAMtAOBTN&ow%`9>x*1UC?}4iKA*sf=(}09$D4eZP<`mSN~GZdSwbBXENdxe<+FY?d1xA3l@mc zg_!Q0lBku(6B#{~_TITgL_AF^CM^J8a9>X-R-O2~U!Fx`+X9+&I|5qPD|%7F)kSjU zWxw_hK2H|5!2$eHV!+t#-tMV1aFNO8xdU?juUYY`IhtEPU}Kx(Z^Lkf(0h6s#KdO8 zgEG>d8X%W>@}G4(xUXStZ`{D>6~L>A4XRYL=Iu(oqQ8kTRL+7%j%^=O#)%}qC~d*%B0S=ZLVs8luHAB5IA^e_*SnvsQg5up&cX*yW?}9dpyAw;c8Fmu zku;1a__-YV!$79!&D2I1mtQ1E=i0!f(Rqop7X}>quG-0~MHg ztvX3SfivVH^yvpP=FWQ(1f{{+NB`wI*uN($Ro zici2AIv})V(f^>BlBIK+o}#nQNi85r1RWo3@Y0qjzLjbRDhy1jK?GijeCRVkJMVt1 zBsyYF&l4G)Jvz4eK0Id$?=Dzgg$6<>q{MuAs{@I|LE73x(`Sb z%xesf##&wDNW8U5&HJD7R?xf1x3vhA8Zp97h5~7uP-6DsdW#$UrRsXX9ZCP&=q~7l z;{8J8OL~g;sVDIy~O}wJw$pX)?f>6=jnfqEf#Yu6jJe08kz6L&O&mA7vbc-44-R%Pf z*Vd5RuxtU876BMR=TW;yP+CB-Zsphg02WQIC(Inojy30|M>L8zg124$rp2ZVvpg|o z)x^yQjQ(j9f4#(7V!rbzN@AfILE8k4Ro>Sy32Li9Ctf9biM-3fMXEvrggkzca2nhA zaG$!(+H%hg<~7U_9{{NH64H2px23yQK<(mSO-FXzTSQbe3=cc6*kuLp*3!z1bsG}C z6o0cSrDOWD&Fay1TgOqoX@P%nLowqh(w0d!rWR-u`j*~UKdJ}MRQ6~9U4Gi&ym?R+ z=8k~1I>?n_Df9WslsPimMB7z5hTlLLvT;a^Fd%hk&am(~km&i-j2YB;| z^EQ(9LR5-Dv*pqFAi%SNviaQcjSu=AlsauSZ})1waI}p~MIy20ZH`~qGF~vXY``oF zbEUgNk~u^;4rC5|CVkG!8qmYDssPft;yL-cX0NCp0RoUBm>Z|{b_L1vVbJn*hTZO~ z^YRipLAc6uf||AMHcUd>%nY--q%$2|p}EN&pj#mVCR5#x()tORJ-|4n z7mZ#Yi88p#)#b~<#K{y_0CH_&MnfiLy3wphd7$5KtBTvwc=-__CPZPw*$39#< zyJ(|D7Q86kaH0T+Njvhf9slBmHg#IRG zps;4y1g*QdHV1n^Uri=CafV?%(VBgmBe-GcsZ6jD)Em-`olr}72;#*- zXQ>vh(S4tmJx6GoxQtd~-d7dk41^da&!p#mj=S-xN1n_%K4vd%ZnR%A2T5eUv4JDn zBxM2=x<)MZFXihpzukPFokereLc#0VMk@=?dEume=Yp0_#96?j16R#VL4J6jn8HSZ z0ia-m)O?c7d;u@2_yTZ&nryb3zxClv_zX~IN;f5Yn$O;Lh3Iix=arR?7+Bd48T04( z<t6T<)muq^hftWKTwjWVTaQp5-e-I(x_t$0{>tudAae0t zi!RA^o~*k&AI(waWK-r02?pydD3@!K9|v>ssvECszkKKMm=|mX(sK?{{xHpe82fn- zyaj2G3Fixpe`rIK(F^OXnSgZBU~Xy1xp4A`bhF0Mv;bfCIHuz+rH0A*yUfmxC_jD==Yc#{ zX=VRPCm>#_;nIZra9#bz9TL{>8}xK=gH7ZkH$#=WLZReFiKu)MDD~4xC(f~WSJu&WKLML-KNVcqof=DI%Qz}XSgC=<^QEl3#vVfF%-)9z)KB}QoBJva-nV|6X1dRL6(52kd}n`Vu)!la znO8?4{uJte3^y&>{sQW>S9lM0+uLrKJ&`^tQA3u&rKYRx%d8e^^VO+}WM>JKNvLCq zEOQgYVy5U3tq^p3-r%7#j6on1AmL+1xUrLzi9yy3bZUIHm#ah$nD3)(0KQ9A-#iO+ zt1*t(bh?=}Pkb=Tu!QiX>vRm1D`$_RP*>3FUdpz0Ab@=b2LXi7Lx*p4XKM?q!Cxh- z)^h!#yco_mXFa#{i9`PG;GVhBj-0?3sMWeEf(h@v@vh5EuXAlJte(`a} zc-21NlA!7wO;KkE)~g1$#9S(tBL zg`m`QYa+(0=Ko?C&@%_Zunfou;;=5ubbPMGQ{_r7?GT+5JCXf%+vh%sD!irxXjMnYCYq3gY)qA5k3=sP6 zaOlDzKcOg7R|@4fJrMv?37DDMtMq3%3?W~ZU1$+^g`1!SwlXHB6zc=f(9$$I$!o_Q z2!yN!f}Hl^W47+HEK}WB6Pp}$enOh&?^*MSoPuq42Eq^IMX<>G$%>{4%?XV}fGbv! zj_ugO1yr4cb?F0KTXBB!Rt%PTw^qS~M5E}x*?T*^ut=UoZp_5AW!~7vw>#8IEd#Ol zoqEPi{Si)gjT0|B4jf5iD!c4v|2lq}|7L?a-cZqa`PFnH3C?Pcx1P^qjCC6ogcG3? zbF_%okcBu`gM5&)3}W;Y7VNPMs+nXWGLe_$+iwqC@h?ZI9qrG9#dEslTRVK*gCpI* z6IeqYr==kw-z%Td?7+W$Tzi=n=eRONkO+){?`N9RVG2r&4~&?c8x+dEo_~xaXC|zR zft$B>`X>X8@KO!tso~LOLD7V;LR_p7GTI{rN6x^Q-`yz@lN=rWO3bm~RpcahWrP$i z&+VNlo5z}s%L_x5Nbc6VwPpHMO~GU&Y6w!o#-+wpjy{88 z21N8+n&r*yqGX|o5J92I}i^b*z9Ge@p<=*nZw+K5Mj?J62T{^#DtD%8Aw@c3}Z z-(3f5kslmNA&l_ArhFcAd@LcGaTm49S$>w9Ex@}|*TG;f*JsisymB-L^FuE%`T9xT>$TVP@Bm-$gb+ac{tn6nWh~7bjFgQp3ERw;GBn2 zAABsFJXBs(eE%=5%Niv;6Hv*}-Wk7bKx$FF1S99U^-aSa)>1i%v_nK|=cJTqa}pDj z4K9^28l4)sNhvRR2;rK372@Y<8d`Lgf|;J zKllY2S$Pu_%@YbcAH|Hz+^yB#@mN0gL`L81y5TgR>FGislBQ^TxgN8r0nwxyb;Esf zc`OU2z@J`Y`Do=w5TaPaiLSBS$KBouHw#o#8+*o*fx)OK#W}r!Ph~)srjqQDDH9FN zHX_}a`M$+TPwD@Z>klQ4mbgbf?EMhDEEZF_TMi{_Lf?A-APmB018YJqsG$k`VutGcXe{5AEZbOY;}R6m)j4m{RM$IP`H)g9Sa;q8YsPr)0O!UV z_g>0_XaeN^@b^;Dy?FjU%uSsDM2Cirx3T-tcAy@0yee99Rd1FkCM~c7(EA&_lSlwh zk!9?7Si%#s;Q((@0Q&`aMQZ=KOZm9l8qafiUKin=7RemNs#=e|$xL3X#?%IAjj|H9 zg*fgn3SF`8opsAw2D|7lDG3qCoR5w^7x|3yq1(nCSTL zSd+vD9}b<6Fr6P%G3iRntM>k329Ss5yahP4T!TL$D-o}^wT_@N@M=!S^{+0(>YZfD z{cb~1g(qqguAl6kCIdOp*uFqVOx<%wFy4Sl+;eYG_qis>_(&f8z)j{Nt+mYk)m9T>AApd=pc&|Gm|k7>pvJMj7y$s zPUX$3jmbRg&vu~1vB9cOTtw(qkEX`aaUz^irRx1b!#wr`W<+=9qB0LJKz1tygFJ$S zRc!TD!J)C(qO3hN-^QjuX1=33;d=8)7TVl8U!BK-DILw)K1h6hX}s!2O(S#`|6p zEuz%;wtHLiRa24iAoRarYG+d4D}%h2F%kCf$JbIZ zAwGb^&VLj$<7LVF+xr{>Uw&&YQC($d+tJ#l9KRRdnZ#7EL9kzU3WCF=ay_VRePVv=#9pVL4N=oS`RorBZndr!+HpKQD7p zvMyP+n|RriUJ?ZS6|%-DM^zP{1%JIHVFp1eH6l&OqA51*(IQ;9unvvjK;rh~c~X5(OsT5{BvwKxog^*v}S&yNeOEeOS5Po@{wpP|Ypj;jZ; z{pC$V=--^d2uWEKqU)N3TCKh;=QyvRVdm^&#AhW}XH}EG=;e9AR?|sLq)AYR_-D|j zcdelf$FF5^SfUf&=Jt+YdJ0SYMf~U-Al~zvTvNc2-ed%1oziS{oxm@ivwIMUrk_U& z7o#ebSy+^`vT{Ed;lJ}cN6`oW5d9p4+i&N*qsxG;r{Y&OrGL$fTrp+v_LT_E*Y_^P z`%aRy>CXx-urL6tWJRYfQ+XXi`_g5+MOnqpb!F_E?l1JZp|*)HDqrGdQ1AEZN zuUE;P!A$d)e+rW7E%2DK)Uq^K9t1+4q}S8o2coOR)Y{`KY>&;y;%7a4`D=D2j2Q(V{pN=AUgbF(BEMz_S%-Fi>X{b!Nv$NlKMd4~Q=o_Pn zQz1gB1wdFwaIuw8)7mz9rxU5?eU!X$7Tkwxtc~By`EFDhqE%g|DWXd-Y3pO6P5d7) z`9NR4CcHOYG^_;^n*TdnC{v~hx?;(f>X|U@Py$$Q3QYQTv7|p2qeo|#l*Gy-y~qS} zr`}&L=349zY?jV(Pcft9@u4{unstPQf9z&Jsao~y4+bUHw!BH10I6mLO?XOKy3;w$K|H!!7**bC-!P2lrJ5TgUV|wUhsOgf+@|Vuj&A;T5&@%#!u>MEftV?j>0a zVGe(x6xT{JqT82`KE+L{sM&E}f<%s}h__n7NLo(d+mRok^T<%K($I zU&@+^KZEIJ+^y%pS=d#~IDr2dlsa3J-bw2k;T9G0Z-*F56TRK;rKiGBpYd4)ejmDaUzUFEBC&IUm+riw;!+xYm^eGvSLUE7aL>bF( z?M^9SokMkR-X#r+5)^UZ`ry340PU_kBrPQN(#5`^r{74Fk#&6$*KN#Hn^PcUo}2On z6da54XbgQ4=2Cb9Ak2T4ToT@+`0@p^JKwTyWnDiaWfkO_11q6^PiiIPY( zMkmIL9E?e<1FEv+Q}r>GRyJSf8G<9!@)}BB^Eh`{{aJz05M-T`xbIU!6wK6@sL-AN zr*SYUNAA`{g%N3j_%cgw_x$z|X{ImG3lh-lTZt~&4g4GG_}sI-V8#(91{n2i_>XR9 z=#y5PJHr|c>MY)uIUk4m>@Ndo7KBk2G~m-FtS@60gzCOy+N02gF=-A6B66koD44{1 zV73zPY9}zcL)U^&bwNSTHP9%p7i(GOLxD0N*iOZ~gHJ&+vzOrZH{l*mMkD(yXA4f$ zLj*{@#@31eR+gqXHZ4uja#3^hSK=TgWMsGVb{3^OAKiiO8Km%NXR8G0`%F?O5H+o_ z(#+KeboxBn6`@mU>Yk@@=$~%YUF2(_uEsCe8VmvOR-h<>3-Vy~nLoyifKI(=7SR(o znpIahnekSj+;JuF{Pbg&Fy3M~$Nzhtp&i@fc=@mr{<=XflYz`93*fx59I@O7!P1Nc z{>SiWK(&&1au@S`5~RWLo5qzDW?Hy@lU#OEsB1*?9j9=Z)BrfH?Jv{nX;!j#snIc{ z+CXU&5g+&D#DtM3L59KOYo3_0mTzT6Rc!vjb5^#>g%y!@3*`>? z0CsR@f8cDX_oRl?BDp!q{J^1$*4o(4q%gb)59WYk=5pUGfC(3qAKR{@n8$hT-{N=f zBBciXY~(8(sOK(ldehFMhDj|U_qirRLy6ZP7AxclCg!4lVUAx-)Cov^Do7)5)D;29 z+F+i6RyD?Ai<(|idxw%>hAwKr-x@vjcs0f;K&|Bg9-C90V(~5xNuoH;zCNR2xwxgj&HDKdq?K4C zo5^~0c32a3XRUcXA3j8zU-lOK<0o-JlMGpYGo$gZO$#r*oKcQSVHwv@blfBcWJ`)& zn>>x2hjZs#N-%J>7HHr5b1k4(iI2n$X19k&-uv%p{OBd0C`-X-+hLSk0Kef>+Tue? zIPP<5vxzN7?u-V-N|?>T{8Q}&mQPacSKLbOpKS+9c(699z4CtRwDFV{F5sSxn!53c z!iIE=$CYN;6R*Qm;I(>)d?=X!V8&oo<}OZh!>hot^dZ#l{f%p~CG@S)l5($bUYR2o z1h1vQ(1N?a2w${_F3ZS^qLvE~>yJIxv1@-jV!vzeCbYqvdY}ev4?*p)pslx63ObVK zu;mF7eb?WZQiqmmIUiF8g79{9>?hs%;$Cs<_(m(n*oA;oy_5!iSb?RLv;MfvMij1q zCxGTQkoW*|16FF8RwZ7)K`@vh7iIt$*RRZAR(5)wcv)R*c;{p?M^hnPau>1B6+P)h zFqLNmKMB@m=z?&A*U>CUWw>8jGGXojj_*RY&TNzB%Io5Tg>{>hnmQdO(p7H|8Fs=u zow1``b1m!7{gw7@*?m_m)XbTVb)%HR)e|zebJ}NpSSlJcTM5vE0FkRHmMRQ3voU^KThmuWe0UJ73$|P7fOc6 z)&lP0iryRjLqgjHn==!oeSEe5C5XAp`x_JJN~&E6x-k@7qEUN9XnRyaadYyf&r1Yb zx3kjRgbd;6pb%umOyG59^NS`D`@FB|HC=sUtg$o3#(Tmt~#Jd{h=?-}P7QDIR6%2~F@%I~?wP#i~(hrJ#-Z}wa2i`~nFM{4ZvyL=a z>r0gwzh&(B1&uvqrUzu%lq36RxewB0WF&};eRxn?95^}ph{FAd91I{y&?qqyNMvOQ zl0e9;r?zdZ7sTDTiJI~X!eQb14h&B%N3j(M=ZDEBcqmkHh8`?PS%yqA`^<+Qi4b}< zw#mY+57NrMHR>9r_X^46;m1``05w3$zW~vYhIuUUlL4$w8aE0sRE_?y@8${*q*dgU z#FIYhQEmCR@7H@8eBJycp>myo8f*y3AAE6r>2ma{H+|{BzHWjFA}GU*V$x{^q5uP@ zb(z3dDxwueF~-G4M~)O|odQ<9lT%?*rK; zv+iCX(KAy82Dn;a%Y2QUL3Cv*bc~tVBv^zKyGTSAA=CZZ6!XZQO)%edC8pK%93~u# zcg$qCcl%>yYLR(%p6+t#E2d&|I=;5CuEX>ok#GHUb?t6oX`YRj>=6GuZ9C!A|S0@Lomzmf(5E`xa z2wpgg&svgU%hQ{BbjSUBc&tIh3woux5`Ndc*PmloW=B6X^vX~KUX3BhdH&Awx1V-O zetx$5O*W;EM=etZBKr0hv=;)>D>w>6>>p5Nd{8epdCA=aEMlw@ry2j$B0u|eT`wR}I zHCM~?_7_AL#&BaWyD^s%E*0vc@cvl+m#%Ddxj{ZC-TmLJV5QhB|MgfT`VbNI`9!Ro z^A$=AkOnE1acN9sU#N}1B4BTXSGa=OgOK_4_>g52fvXC zsDB_3llBN1VB|j4DL1aN?3P)gM3Hpu)iv#c(d7t4GU(=J%wmW3^KYv`^IfI&%PgXst@#_8eB{j_w^vz54wP?a|udLHzuu@$q zzrZ%ndv~6ne=>^%=;Z{AuHXN(6{Q&R382Z1Zc@ppfbdKv+_U9#B5JnxaG4Vc?$mg_ zoooh2K)4S+ssaq2S^4o!J}s?&~+Do}94Z%_X5h-$edb2hpf3)_)YOI3Zdq&c6L z!|HCK4_Y@}b+A7Pd@&`_qRgU!Yag3F4T&;Qy^)=qyx!DZvc46+!TMgy_8g|y*sYR( zOi7E{1Cao70?7_Ms{)9$1#K+P+BN}u@+!h!q3oiJw$_?Wm_9(H>C%4HX!E?aIX_2& zu|$W6@0~$k#eHaLxqTwi!TfNR>tvOTO^MAV4V>6|rh@Fzd%-#jFB`%B5J$v6z1;Cv z5a;nxY*BOP=QlvI>(0QzAt4Wote`2ms9_Ala+qC!t1;YI_n$O!#fq%#&p*J+FOC951Q{qn2@b^^W0xCyP+N5IuJa{@S*xipV*P~zO9<)>Cl=& zad_X^OotxUj^IGyN(2W!tgXok6f@?k?a?#-iDwOAnETPvb1L4SgTF+jeOnqHHM&}1 z(0LS)55C|6+V!~Bu&)3oBfUo#%+PIomme?af4Ssqs$gX%)(J32lz3+e6cEpL;?KP5 z0;G){lq07g4&xm&d*|nznVi96!gV;8vp&7NUbj4mGbknb-E+Veba}{JHeBMY(H=P( z0}aKfFq;z8A}6K1sZl1n!~xPxIRCq(86@+vfSk)A3$!Na@HRr)l5sFql@sy7snbJ= z+=W<7G<~A}v~S^ZoeQV3SCTKjT5VFavFyiQ>5d67EjuHJ(ou|e=$h75N&>qkpAzjB zjrYQARvPwQa%$U&2i4?)9Dg8F>Y%6EWUVZsetX$b)l3tOF>8qnO07#Nd&_pNc*4(3eO?HA| zcDuPTK%uH41$H=oGxT7;s4}hyIN?tD0O=2ItNVJ zuh&MnN0NjNZ#j2)#xt(Rg!5}CeV;*|0~fgI(m2!0M{_0=z@FrXqbxMnr~ONW7x~-k}%iL*nVwM_YBi3 zHOc-R!Z8q1@bWfo>A*w~XUl!^a9weX_MQ41kb@(olqwtnJxZ!2IZv4oqdSmp7rUbbW8 z)G&>%5xw~4WzXX7%3hi?ym*`~Iu8xbJbL;ff_*@=FT2Zw4F89}c}n{i!}dVF{Qw)S z4w{NDxJhO)@wvC*Ky6_2%Xa42?cW_FvbjvGfF9retQ@nS3yjkqgjCIm9R7t`#T7bFp9RNS}e_+Iasr2pWH+%*?efroyP{Ry(CiQ=pK`uL?Iy#be^YBH; zbp^8KE_*b+%mp-%YFYq`z-I8;;)lGYXmwASjZ*zjw16P~J;DzEyw7yg94y_crog{& zk=l{F&*YP=)WU>EYsz`~m?&bKc*trrLTAF=HU|XxL_F*iJ^8Lu3b&$%57@D5;p7Ly zXA2Io5;1yCGk3V;4o*C%at;C1!VX=_WD>p{`KpqnMiE71*{awTBf69ETv~4t=+K1CVao zazas+ZCb+N+@y#rinwD)f5(9_#ss!aCSgaJRvjY+aJ=k-Mq7Q9(A zm}A6yf^vj-xZ}$85XHLormVF||6~^i<^maXc=e|7bu6t=(EiD`p!%}RO>!65F9QXIu@798>(y4ed9yRVEI(r z?N3oOuFkK-6?568S)>}F z@A)}k6M-{23SNT_Wm$y6VL)oqq&3>OBh{cRH9xu97K70pJzwtWKzHLZF4ws<+ z`c+rc%6Nti`X$1)8kWrh4WJYLzPSc=Q7}a%m@*g$kEuvzqyFZB{fN4S8{`KqN_*V5I0;f_zT}ZEey#BrTzxaW-s0T6tBQq1U=(>-oGE>03vw%M+9U)yA^0) z>l;`&$Q>z${ZZQA#db%}QKFFMSQM397DGG!HWAPt8qVHu)wsi!NUQX^ zs3rrfb^u|zD@$RS0NNOmbenjo@lbcGDPRT#B4LGy^4V(Hu>TbGhUgTbp? zXk)jZ$RoCJy7e9!;bvHhtr7D{o-*41Kg&`wzQY$B@MHpwl2mDS$`1KRw|@x%mt!ms zft(pzyUBmt5}=H1bO#QwPJ+=7v!zHDZ54|& zuv#u}H8?qS+iA27WQM{K2CKBAHn1wf^C)1MG{SHvpq~{{+g&Mn3gspovcrYTW@lLL zGCcBJI4(c7br}OK*&c9}Y`S?GD)C2$-UeABi8ohw=Ul3_@b5gmbqT&Ya(6Z5nd2Pc zOLHafOr#XnC){oaO;;*Ec5TDL3DTY|86hV$hC+aBcV%6aYIkbO;EEDL$ZKWDnXpi2 zZ@$o91E|w>llt5l(zSVkR3@~0vd-#a5f)L|qZ<{?%C0ddOtg;g;-RL^#5R^L5|p5f z{NS1nwbfQI1}U>BINlJ-N=jY3@`O5$QMU1M}6B*@+^-S^9t*lU@)2zw5VU|PAso^?^&^uS z62Z4(dR((c2)Nb-{s^5~uMPY8e8}v8?U1Nu_Nxc{1VklSt8{fmdyrUA-hrO4cl3@< z6(CaVfiUh_t5+}q9m)p6))7)v5G>;A3_`5RbT%78zR)==+Vqv{phsj zpq;+c0+tlH)1aHQLiqw9?D+SO#t=`vRhXS7?_i0PdQvHK)ERwypRvh&U5ofFfuk6N z<8Ew@iMz9#iyFcmt)uFzrZ3hje6h|`@&2D>4~_J+>hs9w_obaqO`++oo(Uvf2mppE zEhB{$L>65Fo0c?To`jAX!{S@6?*rHos=|AIOO~txb=dTJqqqtBQW;^v%yR@$@=wYf zxD_*~Z9~-{lBt?+1dIaupCchsM4X8ooR*OJ$kgjzU!5aULI1V+L_ez4OLGJ78V+l7aN zC7Q9mof9dG(XuR3IIsa^;r{&ap^n`B+VArG+)68s@g;`4Rf2Et5aPStQ5bRZTOiy= zCb9_)5sO(FK6u73M)?^Prf?1t5X3%C~4* zzI==oe;yS#!2Mg&ux~40p6T_T%Tj%^JLH=%BXGVbp9s!n!dmNY_{Bu+k!eW zl9kA0Q{%ohfrLceyO^U`H5EA>ek0>MgmL^9Ovr3vJJ+V!q3aBHCKHtII>0RY36=da zh!e)y4&|u?w7YIoKp`tB=!3_?KaekA>(9#D*3BtHQnRWz3K-@jb(k&Dt-feu9Qd92 zdnI5wq$5=oS&!k@JV*_UMz%rja+8`a<^_|tsMqmFkvx1ZhNm-u!fv_OS3=-CD4j>5 zEDfdB#&QgbN!NTBv;Lp)l20l}1!Xp_3X^S*afKtUahMlO8?cuveBDowixk|Qw_!$* zC6`^@8taYLE1p-(u#_34Y6htf)hN%J{85%*IQXu#jYh^LGzXwYs%2JgP}n7gJwL8Wd;CuPUG~ zlGBT;<%Sl*!2IiHceH-lCZZy_p!ebAcNLe9~Haofi2lKNhke5)jgMy zL?m!G*WU_^dVy#4(q)x+&FS+oPq~R}WKMH=$m#7!L6yOw^&y0WYXz1VHO0nbKyB4= zgcYlCeDvIGz*{F4NQ^J+fMg)^gXgSB;z0cVpJnvEG@j|$U$ugbh0rYtqJ^qx%~%WO zRGr0;9mXsPqLzkk?|uKk(PZyxMK6KNLHHzHv z5WNvMtvFi#&iMt}orM9XR@V)9la@YU$t}lF1`a}7+se_WYomd-3%9#XK9QnE-gH7}3qv%E#}8zlyQAHKyLKbresD}mB`36j+AsK3AvV!jlBW=js| zwG%0V&3yp*inkoLz`WAIgp>Oub(tX<>L2IIfCXw%Tb{oe^(2V|C<#7<>GiyZ%!H*@ z#W-;mJU8IuYN&w&T(1m~l0XI7( z(?do}-1`N!7U8f*{)UC!P7X1ScV`K^)ofWgI=*t$($2Ke)MCq_kj%ytT5;@j@2;d- z=ba+EZ;0C!o;#5$gPI%+PB%}E2BzVoVyZZ`fiME~la9%sECT0So6Yw@h3YB!vS$So zZi7c)G8tAA2=&3L3UG*_O)G9Eexf@A zRI0IZt%o)pW5N6ms9(Ekxc5r6)3_H{_y#uh<1Xz>)t^f^wvAd}CiEM})lJ^hb@@2K zxvWW2>`DI_U_5=>4Zdgyiki<%a@th%_;yNW-7m_rIBi79g`98%HtET0#4(6*0U+U~ zj;InKupMmPmLS~;NZW~W<;shoVUUN+>xn#w(v$|mdhQ9&tP0FE7&$v1@G6`FDN)s( zGZ$-D%<;W*_Jh$eBENV& zAs;J*?Gol{Uy9GQreB^Q(U7JK((UF4Cy&;3l!0EIl5)iM-3ot|kWq!zV%vip^qoztr7 zu+Pi=Q$E>X2M^mQdWWSZb9Sd&5_Mp$|D|=9+y;!9YS4jqHq-}id|5l4<`)*MQO{ z1@E7aEy*+bJ%uDO=1U3~0EF(>yCQrZw z>YILy?Igy>eHcVF-NZoGnjMF}XR<2*847qUqw1q%(|4x#Znv05s2e2(D&b$g0ZHBLI|Z21Qp4};A% z&UOz(D!sI&l;pYhfW0IAqfDEPY^N;g{{!kML!=dJUx(#v#Hmf%;lUnqssORfPbKf}$}Meln$k0B41ublE0Sc|?6 zteh%<2;Rc}UPSFi+K8;kw^%A}y3Bykw%QGxr0*uw<*a-#CIq>jBqECntYUeJ2khFpo|>*=^|SLle`N*n48-j-%Zf9ktT z-eN@`vk0<~2bINZwrjkvvINgF@^b5ZY7r}`*(YDrxsuXD$7^EdX(s`O)+-G9Ir6j` zSz=@lHalZ3Oq>RnO{H4Ixt9sQMJXpK;+}H?s~AUev*M5%OF9G9sM`G(Qo|>8wUHD* zMs2U#YoAWDSwB96j~D@1*Ko7pl1UhP-MEmLPdY72mn*R%oapx%|5sF2#I+}_ej7L$BJHW|QX6rFn?ChSGcLaU>5$jMv9a5;N?lSeF zCi`y{P8c=D7w^0V?1(RZDAQs4Wu~Fre>x6&d+G|Bs$Y8WD6GIuUP?PQ2KWC;qaiq) zj4OE2KLZbrefI0Q@My1UlBDZ}SU{mGE(Awq_Uz1){bv@h+kvMeo zE^E9#V*l35cUh5`h`RaY%S}$CW*V>mou}k5@LKV4Olo0J!FP*|PkB59#}pX-a@>xf zM5ZL~jAf>lm(5`-UeZUq@%L#EeF^n)o;Dk_puZmBB)myczlq5fTluEI;`p5ZwT=nY zX&EYK^xP`KsVN6!w5o;YMDVc9AAWhMV~W-S6O^%*^}+&%-$$^yT-DT#|CCwAk0SdD zsm%@VmL*8$`=R^D#hfqOKTf9%u%Ca>R7$pyYNO9&Pj+7AqWGvKf@#`;=ii~(yHPk$ zAaJqN=;f;*kw?(oV2>+)E_A@jkX*+g#U*54UqcE!Y71cgCh0d={@e2-m}id8h9#C#$MZ!R(Tz^OVw5{faF$0Ensl?~Blb55uW| z(>lTIiB&6kVpJr z_6=BH`$oRPdW!iKTn8j<37j|o=}1Iz=!KWkn`|M)*hpYRz7L(ux&;TB@e~&CBJZu? z-*i3Iwud~%ZgfFIH=6ybSzIcOd(SVJGY{^c2LI+%5rG6kG#XqTu5rNllnYh&oU_TUHc}s|#d(jpa#KUo7 z+#BV?{&89~@T@XyV1SFM42cfX6NC76w-*kQi7+7)!Ci11{xbqcLaOA+NCkb&Q*M-+ z7r&aK;Qs3dcnCv(UFrjL@PhBG5b4w#49@ciN%xYW(N2?rIV&*8Pba+AD=d>H?Klm9 zJ<$eBjq1JUYH^@iT)!=jqAnI{bEBQn1MFpB_^W8?H8M6$X??O$bt)41D( zLs1Z-Q|0$>MCWGH_U2NS(dmxQ)Dg-4Vu_VIy)xFpB_}Q@Q^`xSY|hgL^=9=pUbmt5 z3GNQsE%0C#`5Fdt5}d~_XsQ<)~hd5xG1jHAmKf~Ti1|-_-Vu-HNqClyNA9XoZ#F!{vD6Kkcsa?TBihcbj%P2B8hVCil zk#93_Aq?dDS$vWsQA{c}T1?YYeZF_bJln2dQg0xZ+(SIu^4$wWkF0J___R5Ze^V^3 zhR(Gw2EX4_!d-^uYb#rZ02rcQJevJpFh;_bTcFSwxUyuKw&1N1)h9-{jl7w)HK>@r zBli)f0RuWS)>05b(Ee}-DL4|ffJ$ToP+H7kjGH@8%Y+mA`?WLtJ6H`xmMm=qYVj?! ziscv{?_#6h#2U&k$;)%_!%Gm1M{Wx9n0Y?WL^{3&h{wL7T@tlc`%Tie67jgW^#EM* z(rwhRU=kV_+U;GW3TBNd8=3n>Pr#l}o zao4_s(iPRSF{>rNFL$Apalz8B`k2m7DExK(XZlsxBs4RpuMj5iq?+W%;0#? z*IgTl!c#5C>c&=;l*D@rP0}vL&OT3SXJE>+q(WA~tS4DN{ z0?jEyP-j4-60~)8yC7p#l7k&eun20;bZuH|%_vT|>IZP2L15A_l29~@mxLbYeycn} zJg=S-87+dANW(AlD0Ce_)D$H;Yc-v8A-LcHOULk_5(&QHJduovj_p8hm2lF5Aaw4& zcVILW%{Xal0|>hRiHlC=A(k9&f2J~JjxskL`s{~9Df?FWBOa+v zY2H331@v)tYD<-xQ;SM8Z(2SLthlH$jzGCXPG zYUL~cy6Z&Gfp@!A9$yE$oljb2(35~_7K1I$spyKS{vN?F0(sW)w^OE)XizRj-urSQ zcyXx{q_w?reP)X`WEE#>;&yPg^4(tSqeb$_c5G(4wjMk4Q*?t5T`)g(@!W#rHHj3O!(ym`LInI~4RWrX`?` zo3JE^8QCOrm^%2oH*o59drbXP96fJhEgb$yGZp8aa4{vYp=s-ki!0*zG8hV_tT_a*3B#bVo>Q&yv#1bg zVtFj-?x?3=osOySe_RAYYCA=|NYyyfYUI>31-=Zc@dQdwv`duV2MdVqXEwAH=@?!n zhOeVY)uGdUa|$3!vBKxb+s3hpSLy8cryC)iOOtr0~0F-u-R1ZU~Rwtq3jM z^p}S*bP$e|g!EYv3uMcW+NCS1VF|7;442EWl zZ>#8^JtgJhh7*r!m`NXSgd?G1Id+~8yqz{)k^m%y@^E8$DOd5?9;}g2^UQBQA+=Jp zL$2phM$nwl=yo*vN7`?J|JO?1rw^2poT~dZBRz}ZhN8hivLRAF*m>g!YG{ge?v1>L zM^M(+``oxSynLky;awlL*1l-CVJbI>{GJCv_-1!C_+O!54>_HT!HZgLQZacHTN+nt zdl6JLXs_jbA=}4uGLJzokk7=@)fE;s&-olFl=XN57a? zLh0#>@Dzr>(}DDbF}dU*U86=!1^xm4rh-k zpCL&>ne%7R~l3f-6Y= zn;!^ycb=4H$%=~dVQje85!=g;cDKLLE|{Gl9Mjt;C7et7aqeTOh}NtQ@1rMvCJzJ2 zM*?!yJ-zM1hL{aa_%>~|oPCN8 zhm@EAW+v+iZ6M63{d{p1A`!8PL7n(Isib6OCbT_3|`v4v~s2pH~B#&t-Mu8 zW-SR3v)Q-1JCiJHhCv=Jwl zL5W6{jZSrT`*cqR@wgBgJu%6woB<)%VfyWnJN~r^vpIq>g;cRXuEZszPZ2iQ{Ls05 z00F@mJ_ae=6$kNx!878!)S{rP$qQDn9I`Ls1C7VsqyAN2o&H;F@c6!Ig>T`ER6`3K z6}H4C4L%Y9;!oLt9(e={q(~Q#ri$;4W1qpp#yuSsR$|U@ltJ10%wZt(6FWK9I8T9C z!p6vPkc1b%(~HP)Rd5W!RHH(c(l%WpVkrFfIF3!Jm{hQwB?!C>4XISynDwXkZHqHqP&jVyNAmqhNz5sMw$vN0vD zn@U{{fYLD=@iI}av<=*4_z@nnrske{V{fz<7S@V34&K+CnxEKPZU@woES3=+^R^|Q z(Oz1IkIgR6pxRV5k(@EP3m%{4Ts5Ao^HJgFvb#3XM^PFVI>#pyKdd;X%2hWuOaB1o(6EvNuWQ`L$|y^{hoTRBoZtGz zs39e8z@Ymt;Vp5BcfmR;Fu?(L4cT8YoWnw7qSKO4;vHAsM&nU(t=wAX^a{7_zHq<1 zHv!cY0Y#uM6e=UV6lGuVhx}zH!&+V0lAj32&v||Y8 zrtEPj&LrOWt9#uHacE*I0M5V>0wnixY&FC8FA!UhppG(HP`r+;H?%b9%TH{3v^J6$ z(kUm1H*o1C>G#hdt9y5HmnTWm8j69Bs=Y&)4Rd{ic>QxW0Lf>75CS`aR;x%C?cCs4#J(R==oP=cwV0WM4W=pKm$Usv&>l8?$;+a)hVrJ|6ip z4zx*R215ImhFfJF?IsAY=iezRpk zyFA;5*h>DtxpqE0$6*%58=q&j*Wz~^Ycq8R_js$9#m)3$7-!FfO+3n&6f--CA zvOcNPZJDMNy)KjnD{#o0-KxV*np+x@zkAfXB<^jtxJXXsw#aah6;`Mdt@44RN?fBg$NmC$9ts*rMqYAOfMrLze=8_njj#es0WgcOfa1aD z94(tfJj4rZ%j_)#YcOF?uh6Dm*gr9Z$ z9ojGYOZ0_JVmV?LqeZ9Pksnb0?-zLH5%OPe*>`kZxwg};t7nZFn_4-;cun+;fMJv2 zBBR4zii?tx&EdEj-zSL)zycrHR4hcOg* zz5!cTv#kxh>Eh1OyEhd~uLVPgmG2UQd~Js=ztL+%gxA9fu&C8r^zd+3YDGVJD_ zo{CsND1Eg47siXZhje^(L!4|KzMHP50;3=iQ(P!8-8W9|?XgLTK}xl)54+CLZ;m)Y zhUNs6oAiIX#_+j`c?cPIWV``*DXYcE9l=Z=PhGKSu<|#zW}?6Ej3+Ru%#J#2x@QLr z5p~_1+V|1&yF3ztUW;(mSPRuTjs09$p8&-ZD?oc>#V3dZB^!!s?jr(N7vLYcf;TfE zSDOD$*3xv_v@lwz=#sm*6xuYOi!jbH^2NKh00zzTb-O&w-|@RZjOqRDZ2aaiLdUe57I0m*TW{w-|NGTtz2MOIH&XHOOij6#e zBZcWMWCY|>`+n{2O#t%0tQm@R@C+(U@UiHiq)bcdQji>lu$mPJv${h!wzY8>Q(*;< zkiY|9Za2@OYwO_itXN>e=+vYO+1mp6bAKq(YM{hr@;U+m$>Fohi-dafD&<2w6_@g~ z!e;cX<#50yA~t}^YU|P(gY2QwduglP6Uz7o_qXPN|EnWsR}zO`f%GQEDjqEN%m!JO zM-Fb{;9wc`!NNx=NNlCHlHmu zj(czdX+L$QE^+grF6Aw|fv&jRB2b zMnW0c1F`!`Rt9LRAFT$j0!*(nN(BP`muz%Dfx_dTv}4GmT`X{3*wCQ*<+nSBf#l0RoUGkEt$x~e7ol#IF4j5F>}uk77|mrHmR)G(Wq@KMl}dsAFL0Yv2Wuw&3q<(@^% z^^VM9zpJe@4ptf70-x0G_7P?DVGMPPF34l6jvF~E+2=2M2zsKqdnsZ|kb~|ocUk6X z9)rQk>`LZ$O#G=yC&Jv$*+xj{g@B;&$nTa#NJfX#W~M|GqP##Cxqj7xO_cL0O9GFi z5T`<6yky~fM~vO8Vq}=bvSqE#;1fVku}{4CRyq;kWsYZpK=Ddf7QM6`o_F03Q2?fo z^?X&6L}BStD-U&e5)3=#`O5ER!;I?Gl&(nP!KiTf zMRExyNL3X#e~64K`8jJ^QmzjXdpag`F2=-ncGCQTvjtvsF#WWlV)tL>-Z$L|FbXnU zdZP(#T<(yVDBcu1B1%Ko8$mh6{Canv?;%5L0>EYwVF%I_8W|qtw?5Fv|2%5hxufd=l|L$}CSNYayf zz`TS2x&WV)T}hUjH##aOS|UJoSb~w!tJ8}qVc6Y<1?jhR`-MAzT;#`qcYemiw-;Ry zKhT79DdsxD?+}cs+)SZPx-ihs6h)!cJv9d6DwUmZCbMDQG-$BHc5Ge}T~gUdI_$9;~FN>L@uD>z5{UDP*dm>{C0uqBGl-pDD1 z6K1=fDGY6#n7SJ~?Fb%6i!w#f5lJ9eF?EMBS?7m~;d1A^Vn+Mr_^Ik{ev9 zOhrY1S_vMCUK8wRd?WB2ioo0Lxk7hQxPxCi!58?_0m(Z2v5VsS#_2NsSBa#|M>pkf zY+`EFe4lAJ+Ql)%h()E(GHq8t^Qs;)qJ?5zIe|1hZ*Fg0A&oPVOih1vdBgv4$piOR zvJA?F#NE1ZT@LSGxgt)T?^XmmP(86fbhkcq_Z(Z!PScFzhF8pv>~E0&_1kwTH&s=# zRCC1d!88QTea!n%VKmw6DrE*s5B&R_^z>20bl;CP2YgSjWFiO^hqfZB$y!}s(rtH4 zITIN0c$2Nc_ah_Ta7G$m`KmSa264;}okc^?3@8rc)@8!cAwe6Q z`c?77o2%Z*#r6T62=QSxN;1g~2?vxrw$z)Q`y4hJN|US_IpYLSSbUD6+Afgr?ODmG ze4`yf7*B~jvM<;bmUIfn8tSTQEUd_YFiRG59Q=4hChp6rA-Rr=q_4@{J!TT{=sJH=Q z)V+Sn&L~^T{WH{~Sc*{0TF0|Qc0Gf7tlQ-H@M1>pNR?!aWD(DX5EnD^%t^a1{J3=R zkhB8aSQoj}4>cJShQmEWE5T6CCfI&(vBZl3SkpCg>?UCx=GD&$<=OJcEPC;UXfFY{ z%aK@)nd+tVK%3{7BRl%Pv|lBlkOCLyBCs)&7};>bY}V4+r?vpfJ> z`d-mr-{&G1X&qcGjJEtK#uuCUN*Ul7WGL{tbG3@s_ZhyuQNE6Di|8Xj4sEJTwGS8O zE;)Yv!e20gZ8VfX0RYbm!w_a-PWmQR4v!vfo>oD}%%~JD7WTJ|Q22{ETe!JHIwpx z?|G!gcu0)!R`Ldz(V-(pgv1;30YuTsm?Q+I9 zlR2=Cr-;yZHwbvqqMmTVcJVR7rx-UsQ!~*j|EhwQu+f+yr~vQ#&Z*rSDL=Jl@yC|x;B9IfbUEVlkW^f$Or0K= zI65;=#wunAg(49}{v%PWD3ace{K6 zV(f2G8r<+N1Y0S`FdB!WmbwEgMH(BSqB*Ai6$;E3(YIdg5$!3>y?X&+ zc>|a=i6o;^wkRkL#xtC;2;n){fm+P6c47wUT8|Q_%2i!5#`4~5mT1VGcX?CnaWPgD zgjR`X6C`OP9Bpu$^O?k*6v8yfyJjl%%5Tf%~7bb8F~kc0o8hZ z{LAH|SGROLquJqWelyD~7dG{N*P!!rr3d%Qvtbz(+&sO?+Jd^Re`OOOd}q^~WG{wj z>2^rG$w|#rg>5fq9-W}$-<{E!*o3oE?LzqwVSm9<-gG%H_7qm!JPJu8Hj&ZEpR0pE zJPC2j39RT*nR!xq{{!jA>|MgtxQ!!&8?lzuoDhUIIl6(l|RAKDTMck8WUa%c*A{M24Qjz&tc z#qRv80i+e<2Y%Lz?bseHx+p*8#@9Tf35N;qK&i7)1fUXHVxAB?x%z%=OyhDO-zhdp zAmTA_OYGz9a>NAyhYvDi)ya@tP{!A`3M@3i!m~cD;|himpk@6==Aj`6xPH!bH5kE~ zjS>gC@2H#2p}J(flYMR`G$--1o2a7YjNQyJgR4c#lfP}NN8zV&b-Fwd&l$eN}s=p_C@d?zPG0U?phc(^H~voo5N`Y`_YS&t(<$@ zTd!cG8whxxZRv<`yo=vUE)$X+{a7o{KI+#_hB=Q0toZ%tgB5KlPD2?l)^f(73ApOC z20a@4vrNPU=pWPn=W&Y>P77v`{i(V~2WWJ3#gWLY$k|^ak?`kk!v@UZObI%|-B({E zxw!EsnTQ0B{R3xA>&j1+?Rrw19R@MoQ1~l_jqn7FqUqAKuxksA*fRvV#*(cD=vpMe zt=O^7e&Mu0I_&_*(pGS?fjEfa5YW?TaP66&M9Z98nY-LG%`w1$b@`#}tv7W^W?Nr$ z9q)xr4#kow7aF=D%Ec>4P8!9u9!D!|oR`R^U5>J$HVw71~gA_l_*V{5ypp^08Hj-L21r5eJ}P z!HEPEiYyASEKc?Iv{qt-Z^?GBeP#xP%6CLk86*HTK+3;q1srr6rynjK)b8)!lgqeD z@ow@aCNH3~`|6j>n-d(Q45Sx12%#o~De7@7sxIm?ySKq0&gwEu<;Y^b@*sqMP;QU2Ax^_mI{)$$jq`tuif78V;b4#wcg5&rhP^q6#} zN4rhF9g)tm-|uV))`*TklN#Rp#hMG10*M9?E0ZC5$c$$<(i7%1;kK-X^TaDmmZ9u1FIKK)g3(GXQrKteVQRUiWj>zXTl3jrl_md}mTnq#gmAvB%csFjF7N#3Kq z&&P{lTRLVG1vCvM%ZgmrA}>pV5KpBw@+d_9=S%iYuPfspd`X-nWB3~Mnqk%+16egR z*hEbIGXylC_FY^5R(?yIPLGv~uES?$USooc|2&1AaT_(|EH9z#(5H=d#y9^(>7RY^mPMLZHjP|123*G8~=EO`6EX|);#Ab7C%s)>;$a&3p1{9 zt2oDY|KWQ>%#J7aJJrUxi^+;Vd!Ak>7!H?yRrxRYC9K!NXP%=RAvZMl*5rYw$9y~w zjmuhSpo#WTm@4*o{rR+>aM?@eLybJG5)NWFq#FEbTvQ30UViUi(z%ZxpEYW8Yevp*iRJi2|cYwJDdLEMwupV90JqSM@M|s^e`)TE+AG+ z7%>d%lmZkDIt}hOt)ugu6t7(;$NISLyFi#`Kpy*sb@H=uJfE!3Mf#H({_Bl?tm6G7ev)wzmw-qcd zr+xxvM(?Uz$OEa;L_|_QmPCZYnXn+<+Jj&i5k&EF-h6(lFHkB-y{d(4@ynPX{Q+&@xdz81o z(uUs7tSH)AVqKd_!QIbG!&`x=xaEM<({$qM;9~s?LZC~xkhZZkP#MY;ky1D)4a)qQ zwIFe^=t0gPoi3|j1^xP5hk4#Hdlx|6h8a&xUCaL@?Xse+;C|~h;S!XGSo=)u2di1a zs!c|g(HvOJp?e+=5oBY=p-q$$<#LTWNByj9bF|&tG8VJ^^YXG|CY_QWeBRCW*NZH|DZGuS z@1hm)THV-ygqPExeuLg%_$g?3lkreE)FpObe)*> zXr|4^1xTAyj6ixE17ZN-w*ulvm#?T>Bt8Uf3MzK|~M>5YUm)S?ROnLB$|Nlu;fcG_!aEdgkIWdHF>NLyonCo$mMVmwHBl@mn!C` zY;T5fR|tV?rIHwe$a%9}fBb)$fSlDrkDW=g+kAJUo@>L`_l@-U=!4maTfU*ki^~gr z6_TAYgG=(|Zv36t;Sy~PU^R^FX4*=z5K5>9zVpkF^{Ma+*vvY5Mkk*pis~xIH_R?j zK~e1^pD^E7*(vOnccx%Zc_OqKa@P)x5`$Q<|lK?1zDg{Cu(i7*1uW7-sn z|G6CrhRMX6aFth3rXm!Qu-uvW7)$V*^Ur~gLD*ygJ-U#DPCc%$wKNyEQ_4|MDpL*W zKIov%@;G^}JU^?%iUL0vXToN|I8fLcuh&d;hiA2FO&m?i+$D^n!aWtpi~Nt&55X9w z{==MQ68%9L+Q5z&RNOKf6hwPXO4V>SsQ zWyhtyJ3H9UEjY;EN1~ebsMK{3VWcN88_r_m-=X>wJ5ClYF zKEbY4ioO-CVn|H*9G=Cgo@;9XfzR){c{Z>A3+K{7Mtah@HiR%Xc+3BgdsrD~itFQb zS(OnMm7h_aK#f>EY>;BZ0Y{QWAZt8;8+rEp>ATdaT~! zDY8S!euX@eQ>;izdZJ*8clqVqBDzWw`-n9R-tEUkrT&S?Pfo>8zQuFosw2tU-G73U zp6^j7HqN*Ar50mlm$`N1)%*8Q5XtBKlKs4p5RE1nxBZ@njzf@7N1?56 zzQge;Zk2j&D1e^1V@XK|VtmG3OMZ>M&c;qJZ^C;KJuC8f>^D)iB=}oE^HDt5&0fFWYUx>E zN?P@AL%ycOpXS_ro~;B*BmcmazxL|-*xypl?})a_M>3P2Rva}MgvRX$ z)+F^tWWPWz{fqVV2bHcoU3&yyAB;i!#B|+_*c- zJWgMtU-5qVo|I*er$Q1NO3qEW3}gLIj~wIlH~qomzu0VG+MfYWT&R%IpC3U1@cdMHQ3=|(REOgQQGbP|rg0Ep%aK5`ph5U`9xqV?sww2+9?QNYAU93~j zKLuJ|dR>aU;Dg(lO$_*4gbJ^sEFWA6O;g}@)$Kgq&C`a_(K_xI6N#oEt1J5?BoqPS zKmwoGT{=6trDg?@z1@@a%`>HZt&acHk(CwZB5wp3|JAvTX_}rq4@t5DWU~-6vAAip zOFUgBihutGKnh7IgDMzf?OuR0j>cm~MWLa`NTkeS>I|Nt zw^WDy(>4gkhHke`)w1|6U<|&5PrZ)&x%gb6_*lE+X4s&hK>SvVPm4!tl~pdIE7GF( z#!LUoaY6AYa=UXLy0HugakHzgk3fkk@@^$4rDze!Bh<3_&o7PL;ul7P&(a4$8rXpZ z(8aUJVPMJmtQ!Y-c9psi`Uq$;Ft)Q5b>bWu<@@~LCH3MOg7R??lwXxem~=cN)RlL- z)`dl&Uz3X4U~U``Cs2amYdcKmuorc?Ru`kO+IL=VIU1?Ei{L1D&_8+q=wyH__?S`j zVpW9`Hrh1M?3&CoBO)smPj?y!fVtuX;cQc|=D~Vuy3=b=NXYazJq9b1%;FZb1n`ke_PPI|U= z1@x2|bR+k`t(6=t*Rhp(k0*hdfKp&Q?QpOLI-U6N%V623K*rHf;{pyk4gUa6Ou%%8-|hG(y_^&mXPwcB#@*J>UbHtTrj0 z#V6I#<0~!^;okdB0kNbj6~Iq<7;Bqvh9Mc;Sx?Bt-ABQc2Co?#Kj$(NwJ?wc|;=`8?vtouZYjR0ro zB@@HzheYaiK=(_^b%dd_UvcP9lL0!g*cI;` zS{2D@_sszGEBa4XIvzfIseb(7SiRw^>DBV=TM-j2J5xrUIOy|+vkw`DP>B#oZ%U#! zhd;ja`Rp00Dr4lyZTZ<3AfVhzCybKCMwz{4q)2a?zKV0Iio2_1Gr@ZYWm7-i--zY_ zPjvHehlI~n4X8UpQ^xCaoNkg#554&;KmaJo#|DF>wz;8{55}>zYLrDNP3_(?I)w zL)j+^d%CKE@k-@x%scXCy`rCpZ#ax>FGQ?Dy6K$Yj*E8j)RHvZ zvWxaHeUF+pt;VxvGW0GdkWf`uAm)jII)L_PBT0W!i;>Tp`TWXc`qz*T>=slXv~S?% z{sXFJfvH+A$nasp0jWqVOW%9L^@@BGy#kuPuaJ$>gS$8FLUrBCxT>31jyem2L2-MTkzd;&Px*@F5fzAj)03IQDw2}steq^dbOZtH+CdKtfw29_v&p1w~~Zp`Pz+! zzqFSGPbNa_Uo-HjEo{saYdYt6Pd7%6Pg9a9*+G&19+*lMVC1VBn-n&i74GrAZC;=I zM3F1mK8|3#GCs2O!~N1N9RIRGG02Dh{^5=B92yVaf$_$b66$pL@&DbIzmy#cu1c07 zqKZJLr5dh?iAGy)irC?7)wikHP?JW6K4+Q;LSY?l`Pt+<;4!(^qF!1ci3$ z6ZsnuO?-zZH9F-%W&BTvuQrn|zw?=`KMFnNnh7Q!pdt-v69?|F1Jkop&DxJQSy~WH z{6FIo?zB8~(|Y{f{~yU<*^~D1!^8+wCl150dz!|acg5hO6iR9|cz`BFDy03?yfl-` zgjEI!YfUVcT=tnLNw=s(6>3Xm*{b9 zEfsghWSxDF+EqTjRzC?{(dQnpTXL*b|TICX-lqyKJ7<@B>^q;PQGkP9ikYU zIRqJckcv(2+pHBue&bLL<8WOus4xsOI;Wl=LHl&#z%O;qt+-~^H^RXBukN7CFAM<7 z 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%0DPMi8h#2 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�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

VIjy8)+Q7Q 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

ChZbr4()~ zY>qT{#FBh@L1~h46h~*hOH?8Er@Gz|_c?R4hBO?;I_VtsJj<2cJzN*O8~y~<3%QWW zgRpvm=226J(uq9k`}3#px=@LTrY1*E0roSDeQp}d=B`&XI_zB7)05McUx=E?yX^BC zwK0q9-Y+Lx1E8bg@2B`m>VOr-v}_|5>?yRxL(f<&yb?JSWNfdBVXeMohru(N<@zaH zPfU&t?oJ^k@x(ludgTim$HfQF^h!J1 zCHIwxdeEOlGA#4CS8uUW#P?}%x$y#{QkfTIMg=mbZsWT)rR>ykt3nP^rEr|v`O!KP z|1Nwy|Bfboagf<`1zSdgdeKZ{Oh0#i9u`DLuNjJhA9|S5gF{qqT8NkhTytWEo`A%R zIgqT9gw?^p$v7ulSO%&;wCHs9D>KdCKeg^gPByLPxG=Vrc+}F(C2D6UJWCAl8LS=& zyl74O8bzm)krOUXV z=J8nEJ;PDG<>rWl?NR5138@ahnp1nK(Uor(2$rSrEBVE1DauIAUuxAsd3kYdKRwHL z5tAOLSh{M`8yptZIkw3%hIM|Gm;0{)tQUbt=em+i@UV0bKW58esDS|tj_3ob_^5WV zvu|8GMOy1Ma+C<8=04RRGKJo08$`#Yzk))gg&{5LH?OBEBd5RO;+kAZwV3F~QMdJB z3(r;%MAKK#scJ6S%QcIwyG91JrQOAtmc^6dK1p3U60q9~TR4TG*>X*-yW$w9qr6Tq z!8-?YWhA!=&<={G-mE6TD!jSLWst~^hPc;WLr*rJOExTDdV7{4mFOW_-1mcLzs5vA zZwWTZF_%St6;jJf%Gt&bwTx9APPp3i+7=Lq3`NT3E!8t<-!SVTP9p9`-RZh1mLB>X zj*V#zgQk?gBrn(t?^%#>@%;ki|+al@1j&3*;m zg)Z?8L_?xeJ#L8?qt=6~{suy8eDuKib8%0oB;@s*<^AtRKEFt0$me;>IXa~)+S2|E zL%ML|M6y8>>BAGJH=7vYaaEc$;|G|2fmqPFiPJRMCosZ(cwLi`>RH2-~a@$Hz^8toEb zDp+kU)!)D#+F3BK#oVh|`n=Gn1Z{Nbh*3+_yVJKbBFLGc3)g z5AHRjz&cBH9VSIN;zP&q0na!xiZ0CLVcAEfpeDg`nL2LGV=qdCe#rPU5hy8HfMiff z+%cUv^DBLxIds&-1#n|7@1ch%csL7RH`x1>tnYHvP&DD?+}UApAG17U;2_CIS;?Cn zL-@w__}_8TfZ((w2A_XmnefEKNqO0jL_ien3*khLLdhOtWZ*OK>9GgPDOGoQM;2wm z+<7I_q>HE%JQvNhw`J;u&6hRQP~aC z?be92=KsO<_lpgb;CjtsyP%g`6sW_!*hZS52%Tq;uS>d>bFA#g>}s=XBnCt`FW

    l&wUWTe^U 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%0AtKs3B88pB#pu)P)f>v7&*BAeOlz&{WVc-8 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%0AV(L_@&&nSyGH|ssW|}gJsos_ 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

    H1(3ApgIuplxd5Zm-{svScsZB&{vGAOc?AYdDmC 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

    3`xl4H1EKlnjU^UaFX{YY=G~_m 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


    4L-?7yM@+?!luhDYu`x3;P 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
    J$i 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

    Dt(3BFk3q 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�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%0DnIjI`iU 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 a>%3a& z!|DU}44PLQCjqaGb>v;u&ezvH@Eum?+-hKJXdJ)SbNCixn;W5P;QKP)j$Zs#%IOfs zU(aQMc&ZhpXx{vb4QY`vAUv5#G#pqx@KWTBvu}m9wk~*;6#bY@44~ONjAn!1udeBC z^~R)8tC|H)UM>^2c9Nbc5L02QFp`X$o=?;-s>1p7ZF2RG39>>hB1R+3ZQ1Nym!CR_j4SaA5x2CtABq)g^aEb0jpr6w|GhRtA# z-0Ca|Mo%~=m*DL7|1^8sjO6&BzXVZPKDf6emlD`e-L)!%#KOrJ!f?V3a8m3}RYQoB zAV3B1b1EOg7S;=y?kGi^x+Q-J8Aww~*weQ~qxD0A?JBBU*0fyw+o1t+2}51AYg=48 zQQ+I*Lb1IpRwu4K(UpfkdQnOO3*98P@h-t){JH-^k`BxOC+fzZhtaAgL&Wrzvu*R( z_F5wEL*?jVL&wTKi$R_Uq7bKDB^M^J;0i-ho^!)`TIc#K-S?OMe$$VeM7OU>3pRQs zmg=Veky0`$zcQoosujm7&BnJ2zb@;oy|)poeLvy;cM=A`#0}!Qqoc}Mr`Cd~_D)c+ zrP_$jT7<))cv?F;(*&Dbv*pV7pEC$V18xf*D63IO{Xt8>FE$M(SCgp#C*7UmFmfgs4`=k7aT z>_eFnl+?Ezqr5WGXBCiLezujQUvQ)Ac7-(Pvw`!Rt3d%uxNoBx_6{QK6NukaT8rwQ zk&u8U1F(4BM%S!`NWn(x$gf8K=*nAY*BYD?;+b>WlTgQ}!= z&%qX$ghkekb|oHg83^@qW;D3kILO=#9gY+>)&WA$ErDnG2p z{3-)^Nyn1%8|(cQ;5xKePpliKUcq~M7fewAWJz?{hhQAcj3~p?U-$}Z@#XKRcGnfNXRgcn~-$Dy@M$!zOH<_eXK^$~%cNeitV`q{*}CF;#-B&rsNe zAX^aCfhRYcr+lxW$gQhVht`V8m1sUkFqxQZC3Z|SY}o5LO9m{%h)r?53e77>z z`fhfn{;b0HrCJD-ve3Ez4%8rn38xK)VX>Q%v)`CuW`{6{S(vFTa0V$gx1|kDfV@9N zdoiLX4$TSKjFfW3<-@BxJFs}etUp!?-}OCo63Km^bxrhK37lo$Q^P(>bCM_%^rSBH z#2UagFoTkf{!!mPk>efCQAATxsNKUd_%Xjq{>E3^htEkD|4+O0ZK-NGsTGVare?Pb zNI)9dA=+%1>soE#=%hQg%3rC}C{55JviWlOMZJ7Qo9si%fxeWPiLg}{I{9tLi-SHJ z<=$!xDF1GIlxDn;x=|^`Ikv z!Z^8N6jryjmO@I$rZZcab~8-`K^vjJa*z%u8I0>hfX~->8`f2pCw43ODO!J$O&MNe>7X;VG?HNE8ptAnLo!eQlnOh?OWoRFLu@2+~v zpF9tf+%1ck9r5U6WL*%gzBS@;f}+F~U7>3ehb5$YZ3#6ASSiCd#=){a&Uw)<Ua(@hVEQ@Nxb=z&a z={a6#A{nx=i7slY;2rbmxayln2Pr*bmTxbDX8q=P>P=s@tGwBrpRQ~7e=SmQA?686 zxP`yp=p7{Sr#1OCuJpG*5Nrrs-)CV2$W-phcZm^65{}#yW|bx%~6}4D6w8V zWN78^r8a%1onV=Xz=&}ME;ZyYvrJ9U7LVs@M1)OCs}d2sdyw=#iKNH$(C106W?%=n zV@2C4h0o9|%Q=kcVC$Nw&fN>plZyAx94n(pV*EGq^;l*l8;q11#&ip)o@-l0*O)@u z&kY|6qFfl@U%X**qyPZjtSAhM4)>NB z0ctDLqUalXV356Hf^Eo%t~s7lZ+aMkbZJYeZ};L@Em9_I_Q4H?@o)WRp?& zs^`$BFF6rXGVJ6~Otp!`3o+Ib2Lz1TT21P}V(v`QgoV~2EEw~F-X$g0i3#M0db%J> z=_`koUBHR|yP~ykRs-I6&2xR~iCDo^$ukE&#Vi{f2C1E-Yq0U*%3<8XLOc_@eI>6; zcR5d=F`W`ILhe2G7O4{Y#5LuJX(n%IjQ}&Hj28Nep^6A!-0^(OWk1)OgrAEZyzu2s zERrH|@2kU!KU@DUE_U^@AN(gmv3dhlOJ=P8=%<*us z-wI-n9)Ox_(v_S>XK2{-3jAu??NqV(g%b~pB_9OFjlrxO7M$qCOY@Rd1tqZ;+JuP- zqf~-8VQ(jYlbLbiznFG!vXG`5`hrHm=briyV`^n=tQ-5T-I7@&XNSr)2yDq?bE&y`Xq3rRhMB}r=TmShcXorjM5n7r_1q<0UEH2 z`SjrB5SkH7Ru1?m6516gW&mvxSu2U1mlXkCE1&CsAl%GkssJP5Z%<9WN&`Fy#ed)S za2rB)QApGnz2-unI)zxuo685G6u$=?BTcw^*hhAoc-yBE3>9~7fb{~v4Y-mf1lPw^ zwe#JqkHNd&<0t+ChM|r_oX=fVWtgY}Gku)Haty=MxUWfy=f@oato0I;8tNv=F$|>5 ziK8uWc{E-@Wx+A0*ZO*t<_d~rw{_Mcn$D|szqAXuaf3WgW~gw<3X6+r2wB;yD^k++ zjd9xE&Rxma<)eVK-G;%8!gh2;nQoxoL8aY zX}66$V$3Pd;hD^OKX9;xRYPRQD>O4W$3gLDU(vo@Anj zS%wkCBuY_PpBu)plC7tEk^uiCP)SCKuh-{o0Xh^Ehn=d&r?YIwg2liq%qTK=DYC$U}XG05jGaMSvG)O$tc<(@>9ab zIk)P6etfs-&Uh0$j(ZAgU_9T?+Z#F?97Wr!{NhXh`oM(yee8+M>G|Wko|_nwu!j+_ z34hIlQ%)Sib?OmS2a=50{4W7#1jKdJcw+CqV3%{W%${s?W@Lg%C&PKvO4Qn4)QA7;AdH;TD<000Z; zV=st_4P|Q#r|FtFDrb&WKRBUs4vNm8Qkb17hUU_Lz;dzwd;V|1@07wyFuN86)|Et3 zW**!OVQo*ahR{}`&S06yxr&(+dOA-Ip@kw zI`#EaV8Oy*XdHy8CS+afp!I4?F8?QDbZRIQk(_YC)1>`>_D!}{e_UUQaT7-tkdq+E z#1t2OkKW7hrB=@-obmOt_84wT_f8W)Wao$wFv@!``15?vuQ2|X$#7`Ehflr^r1!V= z<3x`x9^1v0!Vu3%Jc~C+BI3#=JLvseDMFu(CWh9ME)hZ)+mVXFfP&{R7sBUPd+~rr z&du=Jj!3^VV%wS5H?nhZKGRhL+ID*+rt-x;DU}uS$Kz^IRuJKnWI;PP*pD(miKW$4 zO5b?#>YgYa7+s@xa$lz2azne>u}c)G$NI^9`xqHQu}U>5;{pnl+Hcfcs#=T(uA6EV zAf{lS5L;tnsW%ZE3pXMZ#c~{gY0{J#w>C%e7F5W()QDoobqPp2$H)bQpyXJ2_V?dFTSql(eruLkZda7>j2)33MMD# zHI<%ph zK0KErO<&(Ec40Zu+7ZCS#+fvU7nrhn&qX@vXK97q`Xnkn<1SuJEpQ`VF*kOs;=w3Y zV17`mIoZzzaAMw^$bA?Zs02LL(Xw3+km)>-NRHMm`Bw6uJ7;mcW=)%k;ko%#b1SsC z%_Q~0FL>}{<_Y7HOx|Ew|84AW^2efNkC`JL45dg6s30>i3gM>KhR>xT@*~qefg4ia zbH(if%o}~~NM(nbz%N*-s?_uS08svXp3*sb8Qqo81q!nY^`+{an(^vEFfe z0iUloMoW&c9W`-@q`CJ(k{7Y1-{)l^t4CyO|MiPCTp2z3HmL$s$kC}kXHFyd2#&^+ zk;sZn`>o9i34pgVr)}Vx+Re{nUq2r2G5MApGA^vz=L>{p#)SC0i;7jZi8c~sr|@cM zmF0SpIfoMm{*~gE#}$`7bLSkBq=&oqKbpe;BH5J$F-t)Tj%@e8{wwMt4y&ld0mBI7 z5eY27d5G=Lw+dxN3L~yP{NN%>qM^4_sda@@$%Pfs;{32(K{oC&Ra}1oa`U~`CV*iQ z6MHUOI>=gtIMWGp43DkR8U9X8!05;~TMWPE;OfBKmI`|I=y?RHr7j5S`BBQZ zuRCA({j`m8Y@$wH4WL{hf(vli1)3TT2gR$D6_wVw%{}njhJVZ9^Q!wkpybH^Qn`i^ zf)K(>vdUBWNHS=7!~w-^!4VzZ3YY}Xuz~{*_fG=l0Asn{D}~h`7DmEy`?oZ_*0mm4 z{o0-mEMI}JMd%~kb$SN^oSEKXP4fx_cGcQY!~tj}gTnY9^&J&f@_$3%Bz8p(`X3nU zGqJ4U(_>)=`3pEkD2y0jrfzCCpqB|-M$Q{4L#ySquahyD&?0ajGc7s_fW0nOBTh>t zMfKjAcQd7#r$K*52}@aLqE&rVW6T0wb$spUhgqKMSh09}Y3Qs}WugHh4N|nc016|B z%+6eT?pm=n!$qc@0=y!tn^f(uHHei6l-tJ5wvW*r?sd+Y^%J|y!vD)qBk(}pQ|39O zi;s`Cx~K;{cQ*F6-~8fVe8m$PwV1X=-rg$OB!#ymxcMSjc+_Qb8Z&m`RZlX?-po^{ zwjuklD+>Ih6$&u0=b5sJ<(yXC*53{Oca_RDnBEwjU?Md-*>l7p=e2Q&^)u~P5J2LL zz6p6&ovdY*HJ=?Jh2pRU&ySNi_bDwL5Bizu6L_v%Okzms;%vd56tkz^t!h zovR3|K`IaXH$|w+Fh5hGUS623e=(x2TE|%8o)lhiNK4xf_YDM{(l376tA_miowcIb zxj_LbZhjo-V1NguB(gg;dB@m+*`QD^V;XPEr^}Q?8-H^O5*!upM20PfHsR`#^zGag zpBq`!F^ykh@v6CR23)e5Vc{jR>8%5?zpU;I!}dBY&hw8K*IDR4({~P3>hr-|SX5(3 zg%eICKe;7nEtfnQO?~;v77RTCDmxrA%?Dm8FlX01l2P_?Cl%6LeBGmHpbPY{>>w8h zy*I9*>EeR4?8InH{H;@%4}4pR@jg=~O(euXbO5dzd&uTFNs+U z{XQ8krF=aHS-kVPZQ>(E5jXl}Wby1-X5L&7jRx(tfSh5VF_#F~m%?z)sG=w}>gT1e zUsP@}<9v@MAOb>oc*VrKt9UTJGE|gIHU15~s1Wc+NAs+Uw%~M4;MlAi(j_?~D{J(U zWU@)cDj^sy0dMk3{0CjcaxC;EadceHHjkvU6wTc&qj%b5fZUo-mK3RKweK(aBx6Ys zA#_NFe1DeA*vO6*WOn7BY0XOmbZx3L!ouTj!{tqkj*&5gD8CoNKfdLoF}8zHYM#t7xl$VjJa|* zT$tgBbV$#qSvRohGYF>u0=?!r`KpGXs6-{hQDIc zOEp)Ee;*dk3fX7^`SGZEk>R>+>!p=?{VmQSNSyqs`l(pMn0*?5EMLDay=7Uj4UX>K zxa9Q%j(7Ls=FfZp#_Du|SGiC^1QQAh>hS6ovEVhX=JJ#k2-B0bCs0)%r}RUKw$;lp zfF$ssV#UZAy1qWIRI0;bp_T0AD;oravOqm3#ThD;k-e+VPRMZJJt5SvlzP$`uCJ@?+ed8`>p@4-iZ8 z#MrHvI=aa$Kkx7ODmyuF2|>{hEV}qaDjvN=x^QmAl0#}2b&@qDj1F2Z>$!%4QMbFr z>cj{ntN2-(MT8AeArobfao2!5p(cM4bU;A+a%%q+dxuFJ4#bdcqG5)1(hMOqwC=bM zi4SQ?^2mLJ4jcY`Q(D(3#<#wTj1~CNeKENf{FmE#QoZ&<&O&h}|JnIzHovA`%AF-2 z^`GGF=7DKbzYbf zTgJI$RDn+Lc&W5a4bh6Rsl!`&Q;cxjE;!t>FxN^F<0Ll?(wFwWd2bJeJ?QZKRs~*2 zp6KZgr%H2)A{M=E$h3oD9BYZL`F`fUwc^PMLh>>90FP*^F^F4KEY#B3jh*x4qgnmiKNnT)@~9hj_L# zQm#nc!vORXxhqI|VJ~iTmRYrtKM2dOoZ>M>+j-l5@7HB-eA_}JQXxVdV6WvI0mk)x zx>I_)8VMcqRuYFIVQkI_xNI@ORTXC(`1tDNXTTDePeUUJSk!GbgXH=#x#}Q@W@s4yOTa3g9W_yDgy#rZTC> zTuKP1dr3UjBMfJGfJ2kO#hT{E=PduHK$y5s=4mny?;V5_fWN*j;w|Zpa4>;-u%K$O z)nbAz9wzt}ccVNsRy)|WZ1noE*bnoG%}t2gm_Pe0q7J~|&T)GEm-@>X%AoOZG$a>S zStc`?x$q}5;a0Zjms%QRzU*hPJ--Q1GDEXglOBdE z6Ji|I{w>r~ZT1*snOTCJ%hp8JoC&k)c8`Updoe6uo92*YCv7!b80)mZdJ3~2v{<5M z8>33XKSXHw1E$j|r`RkA`p7nw&7)B1NW@thz_n~NTxRZDd%nA@c=V7J0C8dLo6;u* zd-yUBkBU|KxNU|e16?c2>t>|c$0`uFdY$4__+7!AWpsSCID9Gd;_wXZC&ysNhMAbk zUq@LK1tuyCU&5~mJ8haVwnR@>(C{(@{eT6b4CZ#8@=z6)KHdA!Z}JWR^lJcH;qV-B zi#|>uEnBmRNj4Hn^`Li>y~iO(ZdZf-UKVYj$d5lzf)ulP87gxkLg{{uzv%6oMU7YI zQr^gQ@>D2VE1YymXK979VDKNuuUMA2uDjY ji6hAvcBCn%ogR{aoM}8M=7pqGHqC<=XA^5Y>l;!;q*sfTG5@^L-bIjdT zOa?)F7g141tZFBj(_i+Du`wl!4Z^~GZ4KRU@0^FRihMfzcg02V7R)HRZ*1eq{D%_< z;A->DZTW}3|15Mnw18veJJp^v6r4_ELiKpJ`))f*Ybu4BZh6t4qKi8oOF|!`j!|S& zIXy=XJ0nV~pw@cs=?A9tvNg(7oI6oa0SS z5F>v>WzDfV3!B$ZF;am-0G#u3;jFFa!_U%i2C>cb8l)euFM*Q!Kn z$N9XH&XdC6_EvvD3U`n3^Rsp#o%~z1q%o7%BdTt~|H0Lyq?b@!EcZy(m8lHRdb(uS zgy`{i>G!^HvCroIPJQBSBfNIrET~2OE0gbTBg0n{otRYAwdFQ2Xj??Sh29HUNcp&`(FfxPra;P*wk^&T~Y2t%) zd)=c2;$CpG&F=g@$ZmQ|;@RE}+0uaD`g&z)AckZt1N{qTykc!ahMdDQGf8dvHV@XQL1G|Pa9i!rp%}?{fTpXnb7bP~$?XNqW?*|vwgJ=^FWogasgof$ z;2-uV`v0N*D^D{xN+APw!4kcK`No$DQS zQKT3=M&r`cDCB7oxYNr!d+s`aPn9@Q+R3wLO>8D$#-k+xg9DT;tg1!2_!3Geudo36 zORh4_U%CSB4_VUr(6ckwLqc26NBi2sF|cAcp_4+oXr<|5gcB(OYEM})K5RU?Wg~~c z+)aJyrdRbOlRav4!v6s#$??Rcpy)Cf3LKuMR%>Y?mDX6MPm*NZ)qrMVbqfswvODy) z-$vrILXU9J_9UA9GJaDFZ;?@s!`1DMVwn$t$nI3uh+Ajg%t1T2EnpJj@niN?eTKEF zclmyU@%>Ss?0MO2^gIk22wR2d&Msu;D@nd(mgi+k z?QG?7q?kQF9Sy=XECK+j$?t;s_X1!5FAUbfd<@CtY5SdNMxQfq2`h7!-+D~nmB{)Q zbkbmlMjMHB`{Lr{G(7Ec{r!Fd?!|pHxPa-H5t=Ns7lqiBSlNSOlsFp0D)1RHR54HinwfwF&=Gz zX)bapGHZIglU_J^sn*5UWQS<^BW}YY49`-b`8g$163?BlIgCt$&S$@w0fTOmQy9_&C)w<*jS4mmpL&xC%$(ME zLS!7sULAH^b!87gqDNXsu0jCr?M?;XUpWjP>tUtDGHr2jA9z!`@$Yl&On>6Bc{8&4 ztiBXmw6vsXn6{31WD?+eTC|1bb(yN-=?oa6%mR#V#q}IVRNAUlt>|-n%+Gd_7E9Lh z$LL9+zfY_weh(b;PX`z2NvtYEXan;=4=2}gz~bcy-}zJ`9}k8?wN`pgr!YePUs;yc z4-uGD&AkrU?|088N0qK@Ai(g)6Zn;C_8q92sFIx+yt2VDEHs)g5w^PA?8mTWcD1U# z+O2?QO>p^UNOZ!A`dS~oA7sezS0EV&UVD0=N(F&cfEFvSLJN>?Pb-T|U>0BR)qUGe z*5eR+J;zMF4LuXv5S4JJiJ*67lbYVfyXNMI&001^P7H6r(wyl(){H`)JU6F5D1Nz~ zb2SyS#PaIfN{X{^9`N{qvSkXWJWvfP#8QH`UUaS*%D<*IE=MCUNJ}(PE**|PO*yE1 zJ8Cmvo3sUnbknUEiW1ulW@K}1gpF&Sco-v_$zhogs?Scr->yS&SId~_4-?Bx>Gx}z z$OVyAkiiTNii7}pfz+=Cm&6{8ZeMUZ{4(YbJdjZ%fAJKiU6*+?&_E3FjqNi<#hSAg zPD=s?o6Xk=7b}{dCsT556wRwaHb6{*zEh-^!qu5pcbo4mi5(C`Swmq%H`qqT^ie$9cj!mxLiM~{ydYnP9>q|_Q^G}{H%5DzQvN?NBM5JGlrW|+R`@FS@4AJTN-|4)4u=kMnM3C4?ij)Gff7Db0!TVp(CYk(G^19ZBN>2-& z!n$^4M>v7h9NF@A!uBcMFTr7CLZ&6IqEO0cd@tDHD3U)&!p0>+^#~RpVw4=Z=c0gO zyHBnLQ6YQTr!OWOuOiX4ajjX0PGF$E9URqG$+R6!76 zj{OM5*dBDFpyNbAQNM;BNry>$1I&%Ew(mN}c@Sy}MJy6Vq1GwqKrvq(-_{9(oL6e# z7qh^+Y%F+C+puu2=oWHh`roi6OJz#fqOf)kt3mYK>AL5YZXUIA~D}`b4B3j@ZOa| zp6G{Jj-0i_zqQIl2)Y#Iw$olIv>)a5HeQxn>u13=#guB$xA^N-7)4!~nLawE`(Z^v_e zmgeD4bgXxt2|ZX@r=lWJ3GY&(h(`dJ27kk8L!c1+1F)yat)U&zO|?n`+g9K39`C9!x`JGFtdmCiyHOSxBO7dS{l}N^V7!VFjhID8>u{7ENy&`;Dim9p6 z)=78vBfU=DYAifpb!A{muiNgIvY;8b`mPpcW_KjMULeukQZ8#*zf1oq;1`z9>3KWU zOHF6IS43DRw^?a8^>s1g!eOl-U|bt0fOKYhRhNO~LZy5{VzDGn%m32>I|&@%AZi zWK&*a79w?nRLTMXhW1*wea^Kg2Kc3#jV1G78?$c#gnJV7lJ)H}oB; zGw79#8{xbqaE`ZU88TmGlqc|*b|5ErqNTISaekEbMlLAK$lHnQu1|+XGSzzO>M+Ie z1@0@Rk5*_DZ5t3orjna8-?dn;E7hU(mN9Ev{{w|OrMDZPqZp-gZ#C3Kq<-H^L)!P$dx=7aqD=aD$CZ&n91df&O7+JHw=IK7>73H(>9Y2#@AMHvx009S_#W zhNv5p+7$cW1iy*mBNl7c#|9P5vXWhi%>QRK`u9FiYn08#Wzfkt}0RKWQQKS($lx7gW*T&rK98i(&>Vugx!?;M$h=muad1FK*+ zF__Jz+1fGhMB^feMI>P1Om3z@{xQeifu?*)&*a%%;-eK9;VWqa?6I97<2BAKfSEve zCc_ch_@O}ElJ=-Ng3`QAND9iu(iBFAG8h%Q5O2y9i5o=nb_BBOyUohye-8@=78iUE zXz{~)YVA~Dfa3d~y~wsDyS-pE<5Wz?Bg!%zP;3z+?oS+_)V*f>S%ZpLMLyD|vZZ8F zN1BdNLS$ohsDsDhps>}~bymwCU6b7C&JQ)A7b5x~*eag``im{y1C9BSC?B<1| zeV|YSKpqYF@Tu5_Q~1epg)lPHTomOTyEAQboN9O+x>pp5Kb(}c`2UKbX>1CBP~#~O z=QR1vBUu1e$=bOi_p;Iil%Ak@03NX?Zc7fIRsT=|!7wLu$M-gtOxGoJkz~MCLh)|h z_+5xkx)TRYR5-#(C6sD{FTN}-2O>7^z1zR2nemr0npmnigr_v&G$(VG6s9~6;>~I4 zamUU}7QXm;mkW?ejaDR+XS2Ya0+*nV;$Sw7jTh4}1!ux5w%N58IL-H}4~Wtjp4!9& zN8FWaT#NN!Krq~W^v+DTi)E%njN_=eSzS&h+ft(59w=5v0v6*lYt%xAo|ds0 zYq&u1j1G*~-=8h>!B1TWNp4<&iAlr-FEE}=v32I08+!2Uztl&0&CFl)`-Bqd(0YfM z>K(}dV15~p>O)0W#f2=<#|YSpc6Ojh&DPS+A7cXoG}VzRiMFqGbo`q$fEmr}#H$t? zM4BCEPt>Lfhh#0&`VZ$_g)1V83Dw3&1lk6>W~-|aYTcPsxhuhkbBYx_YE;*=RE#Dn z%7(56eC1!6vVb69JJTRKSc7)8lUMXINbLp4B8=Efy&&G8h8u7%*0 zLn!APk>_LNfKpV#!D6@Uw({E>0iqgi>R-BW7Uex$jV1N_jcx@2S9l|#Yjo03RikY! z_TcCo?M7>u$6wHzv}@B-AnZ%B{gt=&BZ5q&DU<+6#h=0FIlf37(wtr`9gK+SS$A^i zpv))Bj1;&M*hGW!Lr^`=b);ClaZP^EDq5xX2>=MOD9?NWFDjb2tf6C3VHhc}=x`cS zD+thj1u?CtZa{f0k|GqWba9Wc$r5rGvt0l2PdT^ulQl^W6mI~F7<9_5QtbM60@W2H zQm(!`=AN3?tc%H67B1n@3^ZwZ#~(5rX52hT)Zt2)VXUlK0`d#89z*!VJ_^X3hZTQ{ zm9r)qvi@h<<0gbB&6lrVt~Xwkh(b+WA{Uf$31th?;}ygzpm9%~t;+G1N&APapY?TE zR1Ef0Vvx_GL{&PS=5%~@6@@==`vm`-)lyUB&9yw>N&ZRmdH2oo`|(2U#cDW-i&p^4 z*q-3RBBH{66DBY-3dS?OpBDVRx-PXQJI|A;eT+>&=*kPs%l+~;p&oS2q}<1xxB8nz zJ_0PW;_yg&H2mixjJ|(z7rjglVM9&i*9ZI2O-RM67`kl9X{Wti@N(?LOuD2v;@ui* z#Abq%p?H#HUbfUMG4-tbM#veI?hflmkJw>UD4J7r8npdRN(G>mV2Z}fimUIg;`CiuccWAvPJ;&R`XJ&0mlST6 zfdx1^;9Fd7yXbk%A~`uY+{7PLZ>keBG_G4u8kCYQ@Ofl!Ed zbD6eusmn$(3Qrs-AE8a@b{efMeBBH{14c7&_COSrC6AX>08?b79P@G88LfX6eQO(E zriiHc;r2!aAp!*A6bjf=V)hFT zJ6Sv&JV{zFj6jo@K{M?pvZWEQe_aoZZ@4C>ya~{4Y;dRJXHdjNYLjlQQNDhGE_S{t zNj$x(+j)z6%*KP#tH`E*xtq#~FL>Pw%;YE(camgY4a`CpZHzaqM+kP_`puXJZ@dJ)b*!_o&LEpUUMhSF z__NQD!Ee8mLfRk!X5`9Bg?K#2>pZP(n}+mBdgQ*%b+?A^!`UyGe6PiYp&v=zC1^)a zjQ{klT0A#Oi2e}{+;l3AoAR;+&@<6B;unzSz)44*gg<}G!MAc_aTA9p2Ce!Y7@#o6 zus&vv^*U_x{Ws^=C=LeO*Zp#Nq*@IjqyRTS$iF&za6T!|7UFFvbVTQK`NMo(dNpi` zU?dzKA&24l+UjVsTKrMk6}i% zdMsFt*0fBHYbWS;W|148hpAUqvBSp1^?}$pIj!yKZ~c@|F^udp#C{(&35fr@F7{6* zD{(akEf(c)N8+nL7N96JQ?4UEsB*gk!yRU(zl86g|rZrD5D{ zA}pQeP3yN)={rYmIl4CIqS@XweT)2%49(Bt%g@)Jy^#m$6d zeyM3CNE|EYRN+(ZZ@It%n^2Be?rQYP61x)l2TMzz!>JyCqkJ@7nby(0tOQ{e3+rkD z%-J~n_&6^O`&G0-tUn^AVzV@Rr(!{uhfbQdG%$js*YIqBOv8`Tiq(;=RYcV9olpHg zbY#zxS5PQ3vrXA(adXA!$uq&M1ABPua~v^4`$C?3);AiNjHq~(O)QN#(N_}}^!dqR z>TQ161i@b@*9LSS$%PB$qGiWrlhGIPUhapnk*BR?RRjlX1By9mve>XVn zS%3dX)wBuD`IDXGn=J&DP64&6ot;p(d3{nq$6(ughxcc=Cpt%-!<@CLYzhz$<^nv8 zJ=c;+d!`#Pe2F9TRjd|%;~O5gYV2$l;p+@B>j!Y*_d){)46 zYKAJu>KO%e4E;6>C1qS;qSMH7nISTp^RSuoLe0%IFhT#>j`hPlVKeK~S5fC5Fc$9E zV;;xB!W;fvf~jw1vCkwA((AORF&v=3=*2i%7TBf#1OkF!%4xGyAMsi|apuS*Io`r9 z=BN|BHq`E+eIz#2P-9>WzA?mSxl?S9W5k$0mwDYP6<+WhU)h91``FY!@H%Y*N#O2> zY&Iq6riaKIw|5Cqo&vqKJXZzJHrIX=3-rtD3#xr3GYWG7WU=o{GSh!}=E2=TkU4~#sFzXxp8{Q*gV5xS;*fj&| z8MUzBuzWLqKKM?9lboRl7*yU!_wflMWTYOWsL)`$d=<+dbdCSmPadB4AMcT%!-LI* z%Ro2NK5DK(B zS>6!9)J;321UTa*LdA;S_ND!}b7!N|w`P@V__7t^Kr9PRxe~()HZ&dV6XHdcANQc8 z8(xA@$WRCW+&LvrwMxxzj9q$YLyR&-1k^+9^_uO@w8lwW5q`ZPls6boEDcX;P6g73 zUmo5LA!gKK`$O0y=BUw($oS~a5PIl2bov82)1Qj3=^to`*6YXoRxK?&!B|JUbqnUgE^`wn9BcGCRp=2 z$l;A34#CXggO4o(i0c?VAdWm_8dHtXi?I-e0nBbgA;K-Rffc^|;t%ty;u|0hNGFMH zIec2ZsfZ}x{f1IED#OWvwwmj1X4=@fiJ2NJae=`;va-)WWr7wBXK}KuY@4eV?s4gF ziIg~)J1!0kHO@`0eGgGq=pBDAR_(ozm4mJ;RDp8C< z7~w97+Xa#VML4iR?29dtzcpf08FR)dHqQ$?c2A@Hu`qnl5fYq0;y%@kk~5O>ZMN_S zJ&A^zpz+qJvH2gxSeBz;*Dg|oEKwEbcg_MUzkn_&E$3jEvxIT6|Bqr?m&e;_! z>?FB+4H5(RRvZwt&h|$*@Qj*-e6Z>S6(2T@o^!}>WhdsFUX!vVQ`-rO>Hv*wT%>_{ zMtJ^xHb(@Axb~}|JOK!2>xmxQYI2^g^sNCcD+om~yGQ^3(ua;O<;dZfH0iy7(QiCA zUx&&~pHhiL)*B{fCS9+g1$W-AK-7uWdLI9Xo8c19GXV{hD%IlX@vv*L04eTC%=Wr} zFnXB~k`n=!gj1SOb$Wda)NN3NX;|oH>HdV8EWtei=Ex-{=IaZu4DBoDVP6q^Qf9;UbRgzL>*I;Mt+H96^M z>h0*#YT4l{70EHgVYRiIle$Kf4`@q6{mh!+Cwov1vBjWlG5hw;WHsX%w_B}o{(E>0 z5OfCCB3YH&K2XWAFszyy*UqSE^t{6LxQdaNREW`yv_ln)_>0MOic-2!E5P`JRkj0I z>sH6DDL_ft$%%9XJ!qEZ@gADCjh4hu{4kPq#wT8s9l(JYBnfM> zJj~_V3NQ>3cxRB+8;Vf_$q2nL8~+hXKG-o~yD)U1MYf_?^e(xMf#ISxBv}xN=P^U> z<@fh(9Aw+rSKK~mh1k>eUa_ux|f_dHLrL)Nc zpCITa%-`M3)R@04{#kD$!!Mi*-vR?m_&ja6CzL}r)G-{2dW@XGS^VQ;G%~||Ki^^OZt0V~VDfeo#`axs#z!6DjZ}Ho4 zi2%4J_>RXy0K*$9lL_rF>wUWn)#qJxrFXMPCXYjV4Fxy4hHjdr+u(Ix@HVzcGCg)9 ztn4mUM7c>#kg;m@sIYp1ENPIiC=;Kvf7Qk$x(=gsS4RU<#SPzj&v>Id3BV9!Mwee_ zB@wg0>5G4q)lLYe!o#C;QNrwL=C5{DJhMc|&FZR`Rew-}WNH6r@7O;~eg7@t(+AIZ z=j{bE@^Tr%E2jLCIqaiWO`vD~hr-Vu3TD{JV%2aHs69#R5`$D`B*)#De znlKo->@Ib8QHD_~`E{c?)Ki;vB);J!>nsA%*;@kWa(hL@sKgGRqk^lW=m8tBx_j2fqOvsd~K@vo;FIN9B? zKaKFRg}C@ch)}&eiz?#s2S5EhbCd+Ny~mwXfGPJpQ{|;qj*LNzx$i&BFH*LxxCcU# z_%WdFs|v2%s*c@6fNAGC;gKHF>on_iNmS7`l%^7$s8)zZ@xxb3c=Bfa9!#X=CV|Z2 zlD!G?+vd@8vhNuQe42Onq@zLmxyPK@3YapT$?W1x-=<(UQ_EBXrHimSI7vfMMR8OG zUkU6=a0P^B1{97D1qvzQXcQE^DsLEE$FZ@Ec9(ULaZ;I3H^$#!*7$K}VuH)!$L)eH z+Tvk4r{_N!@HVV_zay8nPppYofV?nztGsg6A?Lmt6)QRDU~kz5^B}v>D80?-g#H0o z*l*T^#C7V&`C0~cCGUueWo6`9tAKR82hM8yqlWJ%lq8Kewn8R81yA^l<1uLFbMhzN zN#+_-blQU-_sbZU1tI@zbZ3HN*68ztG&ZE09tS-Pn^pa>0TyzUu_USR~x%bVWp_$xsnh$J<^7_3wONt0}ktYJ#v6$>d*bU$}$#oCQlv znnrCc(WcV{jrnAoDZ}j4zAX+_UW@zr)+N@|wQz%n9X1Fe;mlnG zpFYGxZ-b8hlHA(YSM5JF)F7ASOuep&G%IYq8r`!psSe=u)H>l5c8G&X^?1~PBS>r< zH@sGquv2};5@;K41(3{5NA>5~RtSI!ibP;&9+ChF8AV~1HFl{VfMs&Ki<*_C$#DrO zl!2#&87u!a1_2s=3*TzAhX~anl8{p#6`*z(ZXk~D zE>^KY<3{I0y)ZV{{Dll+dWATDrk-uEz`&n!SY<-{O8tvR$}|Ma*FUpc9>UAp(#jE0L?}D-x*B)Jld8hPq3NXw3L zN&vpAqag!Bq21wQ5Qehq`n36SH6ZGn>QeP(xLb1Wc(Fkz^UUozMtq_J0|fag%a9j& zv%l=JKS)Fxt`g4m#8Rng#dGmb3`)>~`pyZK@2WA@6k`ht9l!0=dH(}ZK=;MM&aTd{A!uz9yc1~cVA+x%q>#qhx%74+oTnMV{^w1WE#0f+ zJ4`UGoRTWXw8$QYN7ul>6D|)QGP3?s!GJNBP3~MkWj!_77wYAAyweTL%HHR?d9QI+ zmuCaPjZ_M4XgZRdE3SpZ%sbfrx(q+tBA3F!e~}B*w}u)RztiSy(a-@Oorwz^yp)L= zwJ4hxh2`@I{*Uon^ZM0GeF$3@18Q~M5aq$0Jf+dkTJHyt%p0TYgu^(Bwz`@3{w6pS z58#bW7s48vY?R5iTj)2vk@L4lD@L&CJ(~HsdP#%pwLI@G^J>2!Ys3NhW-Sg@gxtP$ zO+P3{nRUA;9QpS7M+ zxLe_h>njjy0HA2a_}@1VHR4~apL<{gCW5Fj#L_LSqTS4r5k-QKGCg4&=;XPz%?MfB zW-iokZeRN{-4iTLY9Ev`9g67dg-}K=KR8L1P^V}I{(mU!WxmC%8W$?o4{Idj?H!{G|6;W0)GH!9T`4nU1m1j0 zf+bl7IV|5s=VCZS>=7sn#ynPW1nZSo96krh4qj`x5x>1)v`fxNj%T{Q?r{~(*aTV< z$py!CbRb&KuAi=^CcjJKGK<2a+pw`~Z(K_(V>?1si-mNW=i!o;X$Tky+UA`DSrvH97rx%k*hsK;kjEh7L4Cg?VFB3G3`*#Fm#qMRn zH5%0kSJIi(@0nCDY;%9=Y#AuqQco7`C`Q;Q>6A+vr+`HEe)?*>@C`=e9-k~Y z3x@=&*(GzfyFN4W8oxT7fXbE8_9L|XqP1buWAko@y4wh){<`0MbN+@WzTOB)fu z7mz4XyjWDrIhjps9_!I^%sw~10r`~>&=6edN&)jceDvty|4(h4fYB?tIv~{N*Mq!R zRyJi5xZ5G?Fb9|3RNOCo__~(W=1b@_R9(xrjh^2qG?IL)8$94_rj3%=?R$wRu|Ni; z=5*7Am{d9Y|3Fyn+j)tJsJA8(b&19j!K31;@i(!3AMT6>WJrqE&JWlsT z0R5~!fXGsA>rBLI=oQGAcJgyH^gI=4!q|VM zvN;$&N`}6)Fxw!+BwxcA4$}V;POua>AUjuh-`XBL6y#?L53^l|=q(2v!`?5$s} zZgCX@9_$Ruvakco^0f(p9~{tW9?A~&k0H#%-{RIy&fqiTnTwz{kv?shya9P&9y)}6 zs_Avnjx?uYr(TW01_@DgaUdoUaIHMi(Vmb+O1e2NG;m%ukV5NMcOJ{V^#~t28wi{U zxE83LR$3^m$*yVA&Q`MB&@8gY5W7;Qp5E>t;`DE*e-8f2nT9jF(qNa(H0wr4O`ESjTYF4T;`x*L_yWQvOb1LfVNhYeT;kB%=u zb6U){URo|ED|%nzT#CYaB=rBTR3ozUmS`aB7E|~!a%(o!N<=9xA?DcgKlG81Om|Fd zk_a@#qqDUGtXajUiJRBViRPzF9FsIALc&dzIUli&$H_pN@YHIAe;eLS~HG!#= zjIMLqr6QV++DxFaG&&$?F?VeK$xLRiTv53&@S%+&t_{%EPN$K0=h3y5Ewqvqm95$S zIQS|}ZR}%Kw;Wqt1Pk>C?3$F?Ys6_h-8Ix#Ed&SyeoNf$XZD(>-DZHoEkW5H$`iXW z3fY|!6c6&&Q&83(V`=fF1G$cif+NBl3WP0f@*&uv6PAT~TfZT&#AHY=fp81^8&G0} z3Ub5=k7vUw2+@I4R8B?VWIWgCV?P`UODuf_^shLP$05;>l)oi*{9pq437-N=FgScr zPgax+dN|18qK{R|HQ+P%%$7tGW6RYgqszhG%3wSc-_AN{k{0%JfUV6a6iD!1Wg(i> z-z4v0d0g}krR1^(Xn->?XcEjOJ#TPCl?Pvq?|?ty;-*Hptnog1Sddzp4fFC{ro3v~^qCHYsK@MYjfzlg4_wO7`~ z3V`@Umz`c;Tu8!<=>Rt6y!jT0(;eNTs1|%gf)K`VS{D*Sn8o1Z+Pj)%ei^$4aBsUj z>-JxFQI2c(iy>P|e`$O%B6+euo#aA|F)!48?r;S*uw#;`yj8&qD$-N zJsHVNc_-Rpp|`?t!iFTG#qIk3SgP+8hZ1+LQ%lDbzw3_)n!j-V@vWaV(ixK9+SAYU_*FP_Irtg}Jb;~qW3>(j*z%!*Ae(Tn9q_x+sd z#gRT*bHW)nn*|^)M12TCgI^W^VABxq-McH<36C-SHkI&Ii)J+302Z--?QDH+zhuPJ zJZn7Z3q@=%JuC}$#~W1xY4h>>=`Ew2ILW`BzOh-BY@n7n&pilsvENXU*1%X*%Bwq8 zG}mW$X(gT#vby||-^4rmI=vRXM1qh53cmnM5?ylBeHdwY7i9LpyRL^ysaR+>8BH=m z+jod9Ycrt8#~`L5yx)HLry){<57Q2_k>>bnwJ2i{96msQU1eBw-}vI4-B#_6$N|OT z>UC`N{iiYsvx3BhVlzCRDVckreUe*@UJN(=8cc)9YZJc3MEpp_gz?O}4czCF;7BHw z=n*+yO>*-c8(qnk7;!N}>+3;AniM&)RVsC;rwiF@Ww9#m0z!A;yC$R-ZX z7mGrJX^L8EU99`E1jM)CJshrZh7O08rY)wfjW13q)k#_5Te=CY#Vj0}a|zeSH)tc}-n3{>!@ynSXD%R->pXQ)iH<6DxztCYi*^xshlVOMbPJ$?hCWZhclY}K z;|#{NS6qmAMm}4DILv4eOaqH)~&yhLbcy5X}KPwVhQgPR!T{`q1) z!@s>XJ2}F6_2lmNgxv{{6So);*&Y-8xzXj@K(U+SGA!-c7nV9NTo!JLHl3SXrYeM<#-w)WMYo> z9o-$au|%=Nb>K}QUMCH+=)C6iAv}FFyiMgCKPM>Y=&~IZZ3upQKSCHN6KPDav$E3l zg}2+L@@TfIoDRm+PsF0Fw}ZL~DeLe^%y8WT#dh(-C`PpC;$PjA{hxTXCAU{;jn_G?gqQ_6iCCQQHEfYsf z9=)E{SP%~Tr)he5*5wOzC1XKv67;!Okb|ybhM)}*`Fls#UcOFpj@ZM};3t>~g!mJ= zj>x!`zYxUP_HnFUzidiyM@FrJ4}MdArcF7nX$r!#4=kSnYHHt5sG*;R^?Vt}H><=5 zDbAuz+DUxiQSh4Bc6tN+)LUH%7fPK4b*o@{6SzG9lR~01W)^Q;XHo3flfQ}zy8HEu z6a>`X9LZS4%oso7*!Nlq+kdpnkRse0$eISz9bM4t5~rIkQcjaYOV=cN40Dgvc&`eh zJ3Ci2G;i@H`|O<}RUaYiYvzHn>EPr<*kP5pEGI~Q^nOEkEf+2|sM*`gf1=%wjt^-8 zIEHUbz$x#&ZEDaxVosf@$%O83#_5ZD*Q^mMv#Z`#45a~v?sl1G->gj=zcqPCx&S*= z3N`w$)XjMp;ktNfP~&%rV-<_9^i>PmcsvS;uUA5nShl<>7cAT;qr}wt9-d-+`#})S z#s5NePH^hd)Ud2=4F(-3L_Hj(^pxBB2}O<|oB$3kGC7E`c@-wV)A<|-$U<7#Tljch zInG+@sa$3OXx7-KZwDsLt67W5=>L#uke7>jT9=4%8~@BjnrgcR#)Mmq)4(ORM`0w5 z+fi1V#j4ps#{(Ol^nXV8s#OD188)g6(?=27<6>_%{J{ zG$P~-Ka%ulPgW-(+-(Q1;UOH0=y@z$x(FV_plD`87$_F-4R@T~8k z!seB>??gwTZLd6)3)qx2`PkS2 zq&Q`Gvc9W@1AY@by#hcbkVOob*>+#Hu6HcTr%Ri&H8PeuMlGm7$f$I7khCsJDv$jN z%idkB9~4aHV?iIkzk*UUe%Q>q34D<<2iJPdo1ilAr56>2q=`v7JaGh_MqS`>H)Y%L zA4`hh%OtVeJSXddRr`Zvo*EwMm;1eg$-!*3f1AGa+DXC$2U zh+uA*mGrxr(u*7+eZS!stOy`BIREJ9Xvm?c77DM}4o=WW>riOdb8NugH2LzxWCorYTcaGvgNuVeSI4D{9R-!za$MS4N zhl1dB*CANtUIAO7WlK{C_WMLQDe;f?1UQMU^Mk)fz9T$1hHK{UQ!2VUY09hy$UU}9 z{_h7avBrI{Nl#78L#sD#RRI#nS*P?W%YQH1Hsk~BP1W_#Yq{r4-xtAPzw<#aPPB-iynGUOdVT7Tle$8 zm#tp$70{1pZS~yr_us>U`esy%mepGq_Oq3?rr+vpV6Y{mk}CK}YcmAa*(@H+Jt&zn zm)DSNCMPLD7W__e=ZJFv%PF^V10)ZGt^4>$W^q^DCEUbtad*}9>vK5M332TO#xY`H z?Xo60Igb*JFh9c1^~-m2d-8%eTJ2oEe%{&hho>4rsKmdT`tPq+ds*tCk5Xa|@Md^Q z*;)oQJ=US(pg(OwmV+YF7Fa%s@R|h>3AxChLpEP5%>!;`co+HuPtf}{XcVvS*wipG zu`JzBC%)HB{ctF{beRABV*%>d^-Z4vfKqD1_u zBm;LY!%7@0L^Fbb-rJP%9fVt~E9d@+KDU{K9_p#y07_xi&%MPej^N#Rpm_eC*hi=2-JyBM=mKLnZAD z+L}`@9)>L_T)T03m0^D<8%U)>>a~ygjXKO2x_%4Z0sKm>(HcYcAZ*?dJ!c?LWw9Ws zx=NHN_Y?e1O8a#x@{$J??f~4 zaeGEy+J?-y#ep1MK2eb!sQ7#=y_5_sbZw>LXf3e@TUY)jmdPF8j}yD5wX%Mg-W{V? zFTLU!1IREL^A$qU^JfLUlbYiO0I7bT52WBO&ohFufNkdHk^J4~m6M-$=+V;{8S05# zdGdgqZqP;3gtkl6>W)*iK!gIoE_1^Iq)foLauX{YPbL^rUzfl#A|eGF^{F0xe1D(9 zKJ;-O1%69f1X};b3Ajk7m3qWlWT&4Z%j#rp!%~LZ6z%hta%h|8yQdCqY3#CMcKQ0V zUSqGQDJv1fM8FY0i}|i5urQIeH*$3eW_CDXv-lOL;*y*_0sVT8oF+t#P+s>^tcDQS zM+hTgVky#H-U8w>qu0%T9ec<*GsQY}PhiA&$UNLaMrYhqA(3r1Pz~9nv${SWWr!2x z5}$2?sLEEdnGLFM8xHNCw!ec%OJ$UvN$7%i?7oo$pTVy;IXMUlguQB{&R@Jouiu`& z6{lq&T06S)=qSAk4tAgIb;e$I@An3itta2F;Pog@DHG)jY^%>h`BKAM ziTcQEP*eIH=fZ_5M#jr8aJk#x6L&7)X$sUYlb4JK)I~4jQj#Wq?Lhx2(fmYZX4NsO zW>3WOF3m<0!v>F8zsWe|J6(7^-I!}j$ z#clNI`4?zwE1CT$`lF>Vv_#pkuZoHS2<_VPDg9PzJoTmYamugaYCB0dz2oz6t$ zzli8BkRMfhB+n(XG)xsHJe~C2H^&=%(=Ao6d{PT6gcBy=a#7HYn1j*cU&|wRP7_DDj`n%Fu^<$ z*fBRM8^2TG#IvU_VVAv2P?;m?%#`cR?6xndlelLxX0FKGYxzep0!8~+V;Ot^-AMZc zcD3S4=n4xiV&zRps%kmj>nQ3*1t@o^+bEtjm-ekqr83@_e3oN41vub~CuRu=XN{0B z(~3!;Dn!md_cJas(DT`&1=SCrLT}#fpDFlF$EG*xlgL>cJ?Vs&$tc>^r!>b zI?0X|=1I&Hi)y3ow1FZr=fGUV#o9l|;8-aT_!c*YM}s7T zQ3z@7tUF{6Sug>Bn<;2gAEaU*N0~gFwf3-e9N6ZI79_VId}?k|sg z-BTGt-Mye2XvTPKwCQl9d0kq$ zRl{S3mVgdW&Kcknfjs$848LQlM2dVo_fL+3A5KM!J(}cEHl3>4r&ey#&l)N78H*qU-#xFHWfpk58-cpxA*;Wc*M>6 z;q=zJXqYzO?9{>R!n#GEric&}P$J)Nx@_qL1ssoNnGdT=bE-L}bMRT|f2kp$O?lFA z?#Yya^ ztEw9m%U_E2x$f9|dRi#meq-7Y-~?61@dY9u+=ftVWH?UjIu3^yZBF+TlGo**IzF+H z!W8$HVELba{OmEfuOu8u&nzbGK{%nnnm`r=5qWMdI@>iUG_nB)b)IA@LN|jRlZqW6 z%x&{xX-Qyx|2j*aD+Y~_EQ&1{$B;|9Op^1r9}MK%3fQuorJ$MCvBn=*AP-Zo4m>@; z4giT>yu{u%_fOwHszX9S2u5iv`e7G&^)hY5D5dEMeRy(ty^3aRY};{!asFt_q0&%+ zul~%NmMl9Yt*~Dd6Nz|74=251;pu*4{C4)d&6y}4$2(DLX*Om0s1#Zd*pfK8=vvfiRWgiDzIVSU_38m+gnh>>Q_kc&eA@e9w&~KAH~}8FqgfV zsbVU@4LT!wLXd3QkQU|sv6dT344uMzb>rJc$yv>OxkTHet^|V;$km=G9?_!&YpZgI z0oZgvpZ7UjQ_4ea^Ts>!iaPV_dNV8is-WfkLCOx`jKN7NMx`Y>z-90;yx9tNqv-|A zZ~DfOM$KXjXAZIeA?{5kY!zwlatCKYCh{SzZEC0uI_0ox#PdH)l($F45roju@zJF~ zA0LFeyDI_}jR$Z7`>~qH(l-!oX@bu9+?-0b{JjaVd*D0~HyzMh{b-d0Vff{N9;$xR zsHU@(<;C6rj!1Yx>twPUMfkO6XgfY{1FzMgD;{cyVl{T)bgJOyd6zApzC_M~p|iNB zxZbfusOQ_R{*-?u9fRuYt8w_wlla56hR^bvZy1m3VVf8NOv3^uTdAhu-iBT-fk7U3 zzqeFypmG`0z!vF2`#+8*hm5J$kl_!4Td+*{o|i)ofymWI_2Y@H7!>JSt*&_QW3_@8 zT0kS(kfF7`cS_u%gQltcmwX+E^nTzW4dCO(A1KhrTEQe%S>@}ZPRkBn&OBu*Km)QG zGKcUk-)neO+>0-?zG`tV-H-1$+F0&G?J`$AQ{IVPrGgrGW3}YON>0wpV%3%;!!-zk zStNC1FKe2V-q>6R_8j)8g+I0mhf~#9R!-i!)Oy>}ge*T8xM=b0 z5J})3I2I~l+MS4GzH~VRbkd2tsS7iIANwj10lgW{yllRyBib6?aSSI(MmFI{3ZasK z2>5?xu|x61(R$peb5JmGRh=h#G8&HM%i1hB??dW7xYSY|07DtUzh--~wfDdOt*QlL`WX(+m1Thv z4(rAIyOn;GRwU^|i)jNfv7dUaw2-YgHp4ak9bo%Gtxp{>@;Lf<6@LLeCck>bEhK84 zEUElqy}b9G3;qyE>g_2<5Pz^57PvRc&I&|?LdolloOWI$!;Z_XjT!!412x`{x}~%Q zxB}WF3iWWmSYT$M*^^8DN*#GLdQ|5sf;Qj_FJN`6j~bNE^=p+QJ{wXItmAHqSww=V z8mJwiSsAOj_$PW{@Vr;}$s33m`J5#9U#-u5C2c? zU8%INJ_oN;w>JE0&YHL&@HG%}Th$Fv-eB0y_|g}%xXVUTW6o0{h!SygiD0QR?H`Tf z{j*mv)`g*t3j6dJdPB9Aia0=&(zf|*zmb(zPYH`BhGz-xX4-yt3Ithqmolb4SDZp9 zI;DnvA^aov9T3CRPka-hf|2WP!nN~x0cD`}luQUUt=e3rFKTJk1VE$N+nEGC?B?l5b~P0lHP!`Rg>~1W2}ZMb@m0x3)f6VaCroqP&;9Px77-AqsSV~OJ z;>@!+A(gp6DDckQZ0|@g=o5w{_s$TZ$fNle`uaU8tmI!#ue;yY9Nmgb#Sn9sS7M#1 zs1hd1tX;cVPmF$JxcZpm!D7AQPZglRfxx6;ztNms4K{frDQI%dP`cZ3A9Q4cqL{Ix zHm^M-w;tu%B<}AUYbIfx%n%Kl8peMrjXQn#_`wZ)*im;mey?FdBOt+X1ZS@S`H-tT@p|dqX+s#pF#Ph&lwF*<1`{R_Ma0m=1;krA+G6E zdW~O#jts1aPGZTDnH&trDN!c#qxiV9rW$v#n zJW(eQRmID34;Nalyir59LYL|I0wd9zqp=?nnF2lD?etmeG_!ezXe=WM<{XCBc9)+D zSC!C1Az#YkWA$^J02fTx)Z+Hg3wW?n=^2bNMv5aKn%{oauFg4@Mz_FlH?Fp`cX54; z3V-%WCQ_ry*&R!h z*8ikG1ffv%;k0|PcbM9om*3wGt7p|FTAAwI} z<$nYx@0<>4fsZTA_}x9|B&}Jg?iX_horLCSKJgexV4MB z$n7gbiDiYB!V$4ZqMNCiC_7QI9sy=g! z_Rb%hA$q{cH@p;gV!DO|{XC=71({$S3fBz%-w+>ujr>`C*nt7%4HIqjvN{YpLfVSP zs$6Y5;#kV%;FC{r{c6R5=!!X-Ie8aMw>6 zHZVvT3(t0XhTddqiXm6{PX7gXJ4mvzTK{X7D0wqj0P!gs-J6Fe$KSG|WWbGcc7`rC zPiUR*pUc?I>C1!b1H)}t#7L-Lv7~OB!h*`0wMfIg4%eS!yZpSyXhaEZs~{-lumcNU zm2`xZ(Y!PZ-R=!~+B0tCbaG(PJOOJaLaMEoU?nejhU*j|;V35DkOqw@BVx08-i%;Y z?SeNi3=VfiAgQAOQDw1+4l?ZE5GMTiS|fDa37=tlzkZ=Rlp>$|45`2R^_7%$%S-+Q zEam4_G9CI+u18BCA%qyq!awcOu&wfiWig8#Ts;Tf!2k9dqgFB1?Ws(B;G#9^nLCA4 z<6JAekZC-G+&RmGjItCKalwKR-p8cN$s%sy2?QrT`l9v${ywMtBE9i9eP8-k|aq|Xn|#SbC6UhGef)}rkA2-FZChw^y3eoIb4gyCNl(S2*_Q;}#w^I>u8gdx}cfMJB@bPO#oVL^E zbP$#g--sNt-=m?8JPbaf?uMbRz;-i?Qw-4t|HS&jKE;88z0`JwQH5yq`bT|Y$qJ~S{*W3ZEI>T%71hfqV=3z$i5EZjtm9A^3n|jax%$}0 zW!BEd)IVd0+3hpFqw(Mjh^a64Ci#n#SC&(7*y}`}oE$ZdXQB^<-Ky^93ZILh}dw z5Ie@>JzJwe^XXvAymXqFeA~V~p5>7|DlKQL{tJ01O8iC()ZKP&%@2wxh4x;%!MkPBl*NU#H3+9+2OgmpYqmc31<(|3? z;t66l6!Lr0@M>d&E;wk^^9>*>y{HzGdjm|Aoj?;vO#V=-rSl|{wt5TFSyYP4R%^Y@ zvhbv#aFZc>F@?F=qbK-NQj*66=CZt}uaT5`3hGBFW6Mjcq-I_$>mmxVf`3DuYKQuI zzFI08l!A208=Q2mq;h)|MIP00){Y;8mGg>9e_9%CmEWg+i(GB`NX=4Qso6HJ8zU+P zW4%1IMfx=lpds~(ec?xlb^(ArbY~>g#UvxAxzf&UZ-~`e(8{NPs)ch>ri5^Roztn^ zek<`JF!)x};t#*{mS$6qgtjTvR?#5< zNutDeOH8U3KKUhC>&T|E~XOY}93~yc(cM z?24vzS4d}VWAYcayQ9GN_C=QcmMMiO%~I0~ubCQ^3_EHy#!6J&bJP?~ZjItYnEnT> z=T>alLO`66+ZAr}7Qb=(?L9jvbqD{g$xrIVw(|$dCRT^RWp?={hwf@4(4Pi6+KBF! zy&WK~jAM)V1ikCq)9)pdK}_#NYWUgT;cak8;`k|lqyrJZ+MsGI=?BkvqBLarfjzwr z3Ex^vkX-&U3YU08LE2Gwpit#tyTRS%#=asTi8IvCzb$J9WkFE@UMYU9{q;o zXhmj1D=`0eB9ymX;mtM~rC6_+G?O0=4&*CI;IQA+6($3om6Fet&bi&?LA50Q@RB|u<7a2xNzx+&I5<$^ftMgM(tEe(cH>`4i;iDo3X%}`IR7%T1>OU zIMj{&l75|{?ZX+w$jmdVgAktwq}@DoI%H$r#MZA6T(nAWcYT+ocKIn11nJkd0r%XT zm1&6<;DS`81|g_ZD@vcpo8z!^1i%vx9;QoQEZuU+c1w*%!hVhKY@fAm+itdGexi|% z=VcFUC7e_YW_AejZcBe^bWQKWp(!c(x^ehQX|hu6g_~ywMc>jDJNRiuHChe@;O;Tb zG_qMf00296~SQg#0ZCE5eX)XV; zr(mam`G760bD(5CF^^Xz#JX$D%xBEm*EY*rKS)mqN|fqlw*X2&wZF2Z_o|^q0I3P~ z6P;@s<1GN5a`5KK{DG&++{@KmT9*|gc`p#e&YCFtkL8lLj5u?E=@XMX8H8sva=q7R&Da$8jn*Ajm_N7i zoPKy?DWa7?BO?eAV9P|aIpN8gCDUl#4Q9TY;7{~24mPCMkGzBa2YNr>kfH7UV4miQc;;Q0)HEC^N^`z>ouwrxF54Y9 z-9b4>+}hhR^A*7-S7|+e>)9r8Q3*e~GF^dRS9OP^!j;abZCE4JZ>7K!nmP2%DEeCN zUd58Q5NvhE{mG-QKKjea4fblQUCd4q#D;_RJtPs)tqnm(wF&F!4HAK|3u6jJ;`&4) zBkTge!7Xwz{s5zni|_URqWa`aUt52WkE8@kkYF$JpKn~q2E_aPxrS7qDv2$5_vmSN zqNz}kH0HZx$6((=;Fda1fdXK-Ls#2TNcurr$SgwSj#EN9*l^QIKZ~?*C;+&c02TDvAj3|e{otJlYQyVFQDMAYT!LfQvgjJSKyE@v~nh11uv2G zIayN6?kso_`W3V$dX^oVOZF%V*U4Q;anS60ie$IaQW0CE>MCoBIUh+wERlXfL8hIf z?YklI;gitBXk8t1EZIaeU)R?rS|YmrQBDe-A;(pa`J;3G zd9y1vo2F3jRf&o{QN%$`Y;`~y@g=v#SE{CSx}>E3U{A1r-!Xxr$Dq1n{n?(_qV|SK z9ks;V%ilxn3n6SqEkO(? z$UxkVg3R)AT*>+7f+oNK*28T$h8nFHd`yd@kipD*_PrtC6g~O@|7AHE-ea^(SyfHT zg{JUz@g%VDW$hiojS&MfO6T&MZ(xO<1Pt%_{cVF?jbDo%LmNn~&n#Y&*@FB((-z7n z58gN18G0X$l++pqY^t?`QDh}58!r_;N;_J9FX9q1KbI2rnbM@M&Iu~|vp-ft^p8Yd&iW~5AGY=`V}WrfxO2@ z;weNOT~+vD0RV=$2hA?vn#4YMwwzwn_ufS7d^6Ip(6lJej$tW=v!-O2yXT#Mxtn;A zYi0z=wn5siuFs!XI|3wXiPH^QdF~$RfuY`ssNYfXd8Ls~`k!=2y)XIu=2DDwSnyj> z@j!Xtp%?wtbeL~#%$rvjB|5e>)@7=GD2Zzd<@CEh3EG8;Wmmt zABDsKjJ@m?W<(YrY!(WK+O3Y|X8j)?BTDqJnk}m+7W5zY*L7CvI8ZrR04vU@pMIUQ zYfg%x_zC!U&!eN8ML22r_e-yR`oR2x6`jPLQLYarl`qfD+nSW`2oyNDHgspR#=P|G zP;A-0YtPCTu^l&hH5D=Afi=1TGBgJ_-HNx#Nvliy%y*G3T2<`m_CE^Asa*OP5<7;2 z&oiZpjuE^yi~jSqCaR4*4tiRDFgShKQhVK7MAiYW1qZ~|@im6{Y@APe6YEf62fO7H zkN|Q)PB}Uy3u{u5B(9T+AZsd%;@gG*19qijNvPziw3MOJMWX#0HO>ckO{fyVLUK%1 zf_#lc<@We7wO@S)b4_KaBGxp%Hg>T#kqj2N!oCDio;C`RG(uB!&~-Gc94O&tI)?Tx zmLd6n)5HFAn_#TU>%oh-aHG~=%&w0pMnI(V`FAzBso5yS}6v z_VetSy{&EYMXcs(&@HN%7_wB+^1V?SrSD7rIz~J^XNzr!BD<#fU0J2g15go+=$XdSI6 zA%7Du)Jgy%@*3Z!JewKcS09gAa)i%}M6yLEk7s$qh8py6kNeahD5BJj`O;B=7rM!2k=;kBcK~prZKZfqQ_* z$oPy1NK{mgZM$<*6qVf)<=(yH$UDS%m2W-e1P&0t=9lH9s{y)4+0A@Epu1m)D_F_= z!jh5FLU~}8<0^W1^oLY-OQRUj@DKGtCkM4yW(RKEZeTlPxIGmBie;7sXUs(I4d{}bh5Gb z2fJIf_Lo%tF9|(bTidl1ZIt7sfhnkg+nt{(KduV+n2(xDzOD#7rFE+S@PeCvzB!e~?s<8a4u@YH02;{z=Pybm|BW%tU^TH4tOS7V zb`VY1!s2rV{4oKaPJ*V5Ylff*7`Xh5e^|he$m#6%dRqnUDCf>XNp5S*(GD8^ey}OZ z+g(+t4IxConBv`RlNP5F9@?Y8^|Z&lI5!Y^NH$svx#9L@*qzhAbXfxfD-{UQM+p3| zDvZo%o!&8#LknOr=0`{q#)LG3Lc~xK@P8q|7h>037q}euNkFbu(=pxeX6K;oE_giA zIM$|YE9yl$g3(=E*Yjt=qe0V~4)^KRD)AQfbxG%=?GLR*b5F_ap)eV?(rYyWx(QCc zoUIO1TLkW3SGWBO*Ubqxw*umpAv{C9JdL8Br5!5Ebh7^$I>(09LS0QZrn4F-{ok-$ zf~=JIYwrP~8Tf_KA0K^~lB&HUVzdCau*j=}W48U*34*6#y;iwiX^0pRwtCAm)``$) zY!A>pcd)p`?`I(6rg0{1xGO5X`@(?gNkdYQ*ehnavfD0IjA1AMDM zS+iy!6!@^Ll9wn`BV%Bhn<`FhL>|#_b0lX1Ea_;8vrfS4z1Co*jJSE%?$^s#W>sOF zK|$4gn=7(dUw#Zzsum7skgXEbVhm=<*^3&IZ|#7gsgjW*@*|A+d>Y$BO)hrq`r-bJ zcFFf9fGjT42P;^nV`U>uIS!UKn_gE;n5YCiURVRZxo<;Qn>iExKd}pqfA+!S9sMT= zs*9G5QA3-z?P=bQ?6{~6T#M-uu*)Z}rKTy^M|}i2r)=23na8pAfl}W^eWlH!JZN}T z7@!r1KyMt%VCy7K#T#+cvwyAh-&3YmSa5(Kn zjWyv5)EW{n9jH3jGm~WUk|M#(C%_2M4kQZ6JM~=LXl%(-pMb|Q>{)&O z&DYT-HoSgq(a~i2heXigU{x-_;k{`Mc32|sBKo-ldj~8&n7l*iPUksM0T?Um=od;j zbqUl!t!qU?Zwh%Q4$kmwIt1e}`&QqMx3KedkuXFAd)t_AR?M=NeP(?2*iL}80|rUj zxHq$ZyM4k>J!^j$8xk*ziUuU={^Tekvkei4IHw7BEkgSJ&u^6=DNNDlh6%b~^OPWm z#U}ZFVP)rzzCORKxN7@`%Wk<8!7+Y{h*ACz)vop8OmwHWEMz`r&g`@HUM6fl|%2d5j?(8k9}M&4(^sjq_E=Q3SMk7{T5}6 zR$i|Sj)B28)S6V5zg^60YC{nR>Whp9TYth3erT$>`kizmH7$t0pVEC(KbDb7y@;6c zj>N&&HGj_Bze#b}?B1;0tj=Q7$pRSIH)Mw!bV;XQf!BnP<`Ok&aa#L3%|wtcH7e24 zj>W^XDz!|I_-`YanW6)E^o9WgYgW}?fzHe}oSUcODppOE4c5p9M1uJ9e^`B^OFfA~ z_uUPnPb*yEbP&S6R=fMU$T}c|7j&&FT9W03CvD&!a7!C&jk!CXqH9DCyXI7Y0%AV% zxVwl9={S!iS+gElM^4@g9!FE8U|hs91ZfR44^*!P*H-j-_#^s^#qKF(i~rGW3g}QF z!oK&ZbV(`ff?;EKBS;6&e&4OZuqr@1_h~zr*Ah7^k$dMuLB<(F`Bq-u$q{!SamgnF zjQCpXB&1-yj4WWqKIxRK045lGytpY_FH(@V>Yf-T0cb75{njlm-JUNdGB(`YxbSib z!8FO_k_T-)?MR3>{0nzfg2Sukkp602xJRP})nGgRv7uBIENxSiDoJ|MfQz}f z^5yl7xgy;;P)Egh{DM~lVN0pTwC608zoE&aSYT??F+-_gleL#;6Q7329A*)9f+ zcMc&#y14sKCJ(~Eg+ETWs~pFLox6Nf=ch^Waj3D%`y2p9c?}3F`cvZ4{^B&Rmo7X1 zn6(1~{OIxMpt$N`owz)XH1KDWm2noA{>$ooGUuU8; zQxC~%zN-NElBuwi2@W2@Y15VIMGi6tX~Sw|AS-SNG1Iz;S#y6Mm+{D|r+n7kvkFAb zf>V-*?|D1L@31lqP8q8nEs$@U5&b zc5pOjYj76MaiuFNq4sjJCHg%dCbsigB9Ly-$?#})G?O^uKGwD&kHRr4E2Sep93VD3 zlJdzb-mMs{T|@H_WA1%{)a~yIheQew@M*~==0Ff-zR=}r4!!*EtSFuC!OX+QJlRt= zeAV*Uy0gKCK6P7Yw=*cQWgmrU2f55oC zZhcUGIQs?&8FK^2sQP=~44D=nidusMS;0Zv{eh`Qc;Q(wz;*2)vKa|Y(9_c;L($Km zr;uosx=$PqH~c_@4+k_rO2)E*;n{%Z8Lv`w4Zh+_pamLqFobjvZzdtiJ;h8vS56z43=r;jeg5!l}lNo39s+6J>g@NZd*Cgr>R>}19ks9AS z1-lI%I{0);-~c95oJdfydj+C>ms8v!NbMf*di z;^69rza57MWc7HCmYZY%JtB48nLcR#_mQ;dwvyD_NBY;^i}v_4}rKCE7=zg4%Pdb4jhJa}Q|`=(;+**P}Ng zAsXU68bvlxnRNE4L{^n{ur*K{<3h)2d1L{E5Wx4q&ODO)6MH-kId$47*tRpIL#m@z zs;V&0n3lhb7?{)pk2#WY;>1@(mhzmTlURQ$i;>J+0)48O8CJIGGzcC90?b^rm8jpOte5A~uFO zu5cW&QBo6AelohnH ziYw-H3ukZXGlj4ZuS069f=r5>85MebCCC1yAmsY4Vmqx7Sb9TwIv!)lVeH~s0By2F zF-NzP^jlH=?u~*E9Of43f{bg#;Qfe_EtA*Qp1U7#f0@dvy9`fqZQ+KUorb=zrq0#5 zkulK!7kYj_bIAkBvawxpZ9%fwKA18M2lFt_s)W z9uo1-;TXrT`KO?~nuLkm=wdLWuy(x_TuZM5Nn8FFgLnJ??i<;;)M3-PsyCSP|s&|sQy+86lk2WrdC#J;gImjyWT%L2J$I_7Cf@{S<>%!2! z11C*-eUtr3=ZtKOha*zW?-VMd1}67Bnb1=8Khfw0d3fgpAa*TI%L%Y{wQZX!*EaakAg zi1I$BGZVIHZRgDfn85(O;Sq~5?eU&K?5_T3q$Hts$Bu(ltq*_mdYQji?sRlwAg(S4 zjK?Kqy+~{(oJ*b)D0PxN;J6(GLlFIQ*d}TYj#5bsEWN#~lvTSDuaS|7Jou0q7Wi#z z($scM2E>@A#R-aLZfm}w}Ckm^m8)&2lAfbzmuf_JG&4JaAh@L7=9Ok4Mo8;H_)XQkr8mOl!wHGNaz+cG?j zx#Kn0lp7T)T6}7{u;u;Vp3?6t*xkwt>QGh*8?Ob_=;%pHCS6HpKl~9)8qpA&WySJ* zDpKhT=u>~8L8L7o{KYu(Lsiix`RvQHR3 zZo!O>QRgKLB_gkwAJC(QLTM)aN>79~tiFCL3lb4jrxr_l8Ms6})+2JW+4T=0e3Ojl z>H4KbFk47dZQh9J!9r1=S*rptPYxcG>(i(rjMe$eszqwr_wN=6?mqE*EKFwe`}-96MZ*3wuq>Iv zK^Tl3oW;7TP5Lk|U_nnjili|432B{xz;>=Z4CjFy&y|D$saJrp1Y%q^uGJkMx7wGfkT?lA zXvxQGb_V!2s^g4T6!l3)hcF1->+r#~$y4XdQ#d-~*RQ?sBb;mC4SS&DGiQDsK2mvo zLrhoXwTEB?Lx|fq_Ph{!an9;LBE*mkg92WpfXlV~+(wo|iV6qX(J`JLNrtz$%d7*(UZqJ>&%!^Eo+FKjssx1tf0wE47N)CjWrT`4cx{H?UEF(;q zp@PyAJlpLDLYDK~XQ(NenT3Q)D2LMvmM^W%FFx%CZ>X*=r19grI{wUyq~);SeLL&~ zv`s-ShWbEwOlmuvsd#3V=orCPm?bm2z>RiiQZlG!mOjf!eg@#3CHqIFx#SJ^wwR&_CjY@ zUszn=<)xF56OSW~D_BIe&-~2%c4b0I`RmQHPgb;qt>-1hGThZ^1c|C2b2hLDxaF=% zv`DMli0oe%e?7*0N{r zlOwO&pgiyY9Y5qDbcU?Ya7_*ipCdL?1{|AAne@mI8^wc^l(Rr+)cqk-gMU}~9uRKC zkh}$sQO>&yz{v_voGV#yRqRcL^HyomdN{%9D)GHm2(@{eC|eWB(MPPK){LR$yH$w8 zP||UeU49;lw0Ng7*(n~Bo8@5d=L8oo*p_ACZp)vc>YKL80+#}9(NF>foQ5V86<0LD zT%8ix6%aY_hS8^kjC-{Bc#b5k4>~Pr-8g8GJiN}-B;UbZkMIE^59vKOf~U=&X|WF@ zOL>#@q#@|04ncvcSTxq3g!Pb^J%Icn>JSom>xcU!3UnPx4@-rBY5AU{HRZkV5lUq6 zZyIIX2DfWtiGV4(wTr`PLF=_BP9)$R5TYOIudFgJ2lJ`cSlp>_yIt~T+!51zy1KV= ze*iNTPoE7ZOVV#yD)&NiliQ2dtXjy0@LJ$VcRVuDbLdN13dkLGF765pZ)c`SR*6{P z*Dds7RAB#7`1kaegSu!&BCs;kX-SxQqE5^M=lL+f6K0k_hQ;hUhOUpmNCO&{8@LLu{{>Hyhq}oFA8Nk)o5&(m=Q&P<|s=0{`spSM(6tjll zq73r3k{ongyb}Rd@#i6LC$c%2Rfo$4z%J)FYS7zUjovN*BOeAjN?nt;~YC{K>hiBGtaE*d8(+;v7*OoL~P?VW5*k%Oy#!N(;vhJto5csecY-0TpJ|3=vyV$vm|FO3K@f(ALr+DwQW()kGh>>w1ICt2h$%`0qpINFR3y3oegA zFp0Ig%s~XVk~)24G_2Io#s>IY?QOcGdrHb%&bUX}aW$&F6nLP2vd~HV{z{r?_;dQN zn?fRvI1J(HWB}r;TdXtgov-{FoWz(P=Wkc)(jLWuQFd@-sPd< zf1#Wi~1#T;wqNA|Im^uJ57?nB%cRvH}w%K8O!jyCpg_?9>qeik59Ame{3U-19 zXx0-S#q);<_xCO^1@@U7f74JvC5akc@9cQn-D8rLKNz4E=xK2<2-qp-LO zEg2JU^>Avz(mc}#1s%s6>)k-R&8bjc)CgL%s21bd_0xBp(b3%;?6 z>Q*4{)W`Qjlc-h$^%fkPQILT5SOYy z!%sUBhDiXzf!(V`k8s%BP(gn=VK7H>OQZ}<83qr9`_%6m|VmdH}wT(ycNT^Fa{re zIO*Dq>pTsC{`DF~P>3?i0y<;%fvaa_pwlisj`A#Cs}$LDvcA z{o}fyMHnxBy0JlQd9H_liA=_zrHvd)>QnSUY6|K`!7FFNZx&|)_ z;4L1+>3h)(nkk9$0tGBq`4dj9v2puvlns5(S)C?)^2wx-ll%a(nQ5J`?$>l{PDXGx zE)7yDVpjomczoMY3~KsJ-n$&JhZP;^^pME@@VdZ%r0m4E0;3{G?{s>;+m6Un2kb{t zjhWK9osO%@6KZaFDnpd08E@wo1tB&}z4urY&mW3$qze!37zw-l0%VhR(U%|wUm$A7 zWGY^2#f#lzPlDoO#@aQ$QHJbEB`vxCKT z@O%pmFqC1}x0)=pyGaaJdz_U=4|32Ao$@WzjP&a7(H8u|7*+qtPH0c3^7;1L$aThX zv|@^0VKtc$@X{<+Gc7rI2zfIH;4S(qb}M@{38uz!pvF=oxpmtaGnybc_NyPGK#0@E zZW;{w8Ln^faEl&ZSg5lJ%0i0~wZ+hTVN6U7E;S8gEYXxAvUVwZv<(06M*bdF^tQ8$wJD$PxCK*fW|lcaQE6s zz6j#GhP7_N!H7Q>deeCd74OXw#QcXxV8tCZIpE=7|ab`i< zb-ChM&b}8(UbAxZDa?$*nTybzXjcF*YhnplUnC;5L^;5=9Dgz{@yCU7);IjyprLK( zGDFGKiWFcVa#SQM4+b4znMvz7J5GZsE+Pd({*K)y4372KoBEslA2KR=q1jXMh67-! z0GUxz%y`(44_&Ns{rdmX$#Idh>EybL+BI_vUYPXDc#~H#6GZhgeJ`QH&H#885YAXN zB{$S7E)!}iJH&VdwyLoVUuB~EuHjc;GDvaDb@+tTw(8A_v*MuNV0L$$w+~k8u!`*YI#K3=Co8#^5sl zmh0Zs?Y5SIgIC}9Yx~SVvKUuOn}&jKr-EpA6;6!Z0Az=XrfNrz1-;EM8@ zT1^A?suVx3RrF;^-Dzjd%6VWda**hqauHQD2?9`#)NPF#1YCf&hhpvDje1vPTwp%{ z!lKO!-XxEL3Lca&M=lb#iKx9RYBE|x==h1Q=?B z9-`T%L!h?E@K%gv0q%7u4s+kiE`lp7orD9sWxC4dF)8frU}i)?X0DPYxn1E`3Nd8T z9dZzQ^=>SDy$7qOSSd7g@>d(z8g3OqvY^CR$HjP2?4BuRNE7_HO*4qSrJu8@ty#%X zp=jMr>$g!XpC?s>h`y2yH{CJ|U#W8OxL{ zvA37HHI}EeNwNJcfh)$Xf!Gw2e|T+zytha4La(sL+gPpjoYq+q^dy;gqVxBISHzC^ zg~yxnnXbUS2Vw3Zk*yJ2aCAUdW|qw)auG3b+>S{UT%#hev6=vzP0H~)t}0G?{)xoW z3EzvPv2X_ffe(sGpPgsBd)SyRT2)P(u6$DJD20MBaASYNvLSaMxTpHvWzAad`@r%} zvGwrb`F5tx*w&BPhZg_3yWih(XPJT+jt5;UXnfbP-cXmbB@)6cHpv1Kf^-%zO<2aN zDR~VZwpasMpi#6dgxv-l6366%c7t9f9F2Gi0ez^NUo`-TOml%vOpJv7i^4tsNwch6 z=VdVdxYB!+7_2BH1hGV*I7D_YNcw)pP9b9a`gTxkI;t!rLZFm64z(&5wRpM=e7%y! zIGfK~%Z3kq#LHuPTp&ORWeg1{uy)@#7S{uSw)pjx&3Vq5dkaKxb+|zveZgP;@RNQv zd{VBW<~>uBFG0TP2sLjJ#SmYS#pa=xQgTRkXdlGCa5YBE77%8lpQ(ieZBa4YdMir( zAAA&t-M;&zfy-_V*mXC>#CTlCP1+I3lI8h;u)4P!Rrp~Hmb5GNc_LV2JKd$X%dpH1 zxbi9?K4oX-M$jEb;FK8PhO+5YP6jpsRbPdO`nQ$?7bw0Wt{ge|GRn;Er8^5_x(;?{ zL*~tLkvfn-txCXI0^wVBC7h}1e6huG8{tz2;VuN_{{d=ASZb!(bpp6!y}ph~hh!$H z>*l(jj6?%|5^}&lC$Q&TjT_dFFxAg{eG{Y1hspHDRj0>UIQMi)&U8WZ;0#Wt#-YPU ztpynWaXPiTQg0hjUJlDSiN;%i#COUoBhkwuKoXX^pD~&)dWxS0aJPVINn3ImYG{QD zNk`kMM~7B1-AXK!p!ue*j36N>-L}WXZ8va%(N@rN`q)WH33I|pVyLXl)|l#-(SDcv zB>Ndp+FZSS>Ry^M+B9NUVLHXX$_g9JNUGy7Q;NdTDx)${;ZbgaKBpS{P{!)m1vl z|Nf=Uz$PJI+{RvpSlPm92ZDtRe6e+R&@lv}*&HWzwQeIq617IEp;I`Isrz%6>h`Q< z=vSknMBk0o2e_w|FJf8iLtw9RG`(p{p~cAVq99L;qB$;(%6&aPt9Bn~eTx*YQwBwQ z^U3<1MhxqrPC*4L;Fj`$hvlxkk3VW5&^^W~(cSp#7--n317~F;Z`n$89al5d^xA3=&Sg)b z5Xo&bh_49Up3E8!Q&w(x^#iJi9jJxvB?4E!+MN;xW@sO(vgquIGSPmEA{Bh?ATR zK+@=(G427JsN7@OlFO=39r%5NE+Ywy$Bcr%twDq6-MV$=sA6&kKXDun6PpclXy$QR zbu4ogc-7?`k;vDq*po3p{oExBUX&#In;c{5!Y2w3@eV;R?W)p?;@=dRZi&}ZhyhuLd-2hm@DqB3s!qEAhrzCoyogiNh9(wTwdD@ljj>g6Vr%^93 zs2=8eNmwR^Rx$bucnXAoKW-#i*uR06EGKbQg`4nzo^eR5Z+ZP@RN-t#^R+nvJ}@Zo z<$(MCsG8O)Co({C}&HLGF3?vu0mXuyta zB3POGVI*4wEU$RGI}?j=1*=xqlP(TJax%T!-r9RcFdnk0^7S7}#0clus$L>PT31*q z0RG_SxH#Vw(3MpI0^WELm6rjJY!>%=ZXx|Us_%q$en*SYRW|!=T^HcGBof=Vv9Hc} ztA#YvzRhmJNzz?NQqh|uJTFLQ-q7x{Kno`7EuB09b_Z<|z1_OmG z>K^8cu0uqSSX__Ocjnyyq(6wSZ!u;{+ZUzHv+NK92oZG)vtFSi2JIET2x?*k-|GLG zamCkqw*kL9QK+J15YfL^K#y7c2jr!O4ue=@t6m>&SQ(kLbb|Q(8*EQ6PxR`CupY9n z!mg?-RU%D6QPX~jSyXys_*8k*cGms4k7z6;90qv4 zL>dea!WGhKoro(Oc%~o(xpkfu{GG;r|tM!+VF1lC}NiFBz#bKiN8~F0(~Cj zPP8k8ci%OH_?~<4x+*z%*$*;os2|RuJ1OBNnr`WeA3Ahio-CZQRjcJR9koUHtlN0h zPd1ti>?cB!rPn#c-u2niwsIn^TPeuXIwwVz2eC)eG)kjLvauoN?G`%T8 za_xo>(x@nfmy9!!G=V1q@p#MnZQ0%?h)E+PA> zrP0zrAH!L-l8|VXWh&miHexjTzjCY{_NgUFqqg!Z%FlbiFy~&%fi(=gZBS2IT3Q3& zvcgf7;I-;X!<|Id6Ry&WXUaIUDt@ahO~=uTmyxxeLA8H+kvX(sXU1cQag~9|8MIzG zV6nSS53+=KYv9y~RbeC~_4WHW0-B%vSLL2nTU1q=28F-0j8GSVUFf-1iiBXhkg5CB z3LqQ}zvz&@Dp#Lo!-*<=cSV^b}2P5m6_zRvnMpwm_vyRgojXfOEl_L9hN`i!G1rv_+)+paq*FBJX;)K8Z;I?RbIZXY0^x!cOV6*R zv{9w3l+IE|9~gRdAV4PiYeAN^-r^E`2I8>7IwNnl)vTDk+3p6>f%-d zxPnC2doyJwuUt_;-ysT7GFpSCl)Gk4Rqxa`YC-NeYVKt@mO~>j{XA zvs5-3WRC|lz3iz4-#kt--Nt(EAs8H1)cWvZo6l+`xf~LFRsld+hR&c30a{v|?gwCc z$T?H8uW$UX#ShVuOTy*cF5>*QK6OD#@jJo`n2xFAHkQ_kMw#Z+dIEXzGl=WNdCM-) ziTE7~^l%7{_ViTnAiU1lmeO10}nW&{U<6oy}pt zb}{Lo--Zw^>IYx2xmGm>q9iOuB;!sK9$axH%LelfFXM_pz=&V-oBmljIy6Rr7tV}4 z>C=J>7@dkl{k-bCF9%r7NEbB>h`Zv4*RZ^J!H5zkcRHOKLc#0J5h$nq8b@}KL`DNB zmr*e~Z<9(T>%I!5` zKU!vaP@_oZwlnfG<)_@Z4%hHB&q@`GW&Nb%9B^nN&3bMX5p$6YD?KpM|AUhctdZ2W zV&)7pob(wuguvKu|Ehsdcs<@k_c4Dvu1*JLBgt>i;A*icxB|A?d718-Lm)6&;>*9@aHGt zBLZ#IkJRqWOqw>)riZz33F^(0MKDvHoIR_#MB~q%Y6;~B#GRxW^VIvBGn$J@>}k_w zm!K|}_x{?v#i1pQcX;R@rsUf%8T?71TOs|U!+PKGA&jL6THe}y?Mqy=K?gU1^;LXR z6dDV7gGFBAtcw(zrlF&`C-bn-z8dLb3y#fr2OLCk@7T<#(+Ii8g)xtOAK6Ds=J%Ao$yLE)A!$P)9!BSipib_oi~vq>tshjLN^Amv5>VzS6|ucBvv;O+ znn*@)=Ct5FP~moeDR4a5R5Hq}#M9de=(!|B89hs}aj*D4LE##z1U9KdzzekI4$K z`uw!4R#Kg#y^ofl#%4a+^&lS3Iy$#3I}K@CcUT^r%%9}YVOhPP_RE7?34fIBcuf9Q z8vqL0aiD^PZ3_)!piaP;!TV(>;psoj&Y4xq5vB_7FQITU0AL@CbVqE&{?; z44ODm6A*;U&K1omy|+8Er=ZBA>7lj{%3%jNI9S+sFDeE@UPPUjbiCY?%4em3KGeue1j4moA!RUSL06%IL11YZ5 zoT!O5zUHly>wr&8#3{V8V&ZJ};K*f*Ymzn(BTIRKYWK+XpKB=0@6Z4*{j7Mb(y`!| zhQ2PrEi+kaWyOKkgrXiGv|;L@<2SD@Kpk`+vK-$#p#n)ZkT$RQ)T1{4_wGPVle^tz z?V}C$KLSl*O%n}*T8wkjdGGA?yJLMIGJc9u93&#wk=^ojh*~d&`hEDlAIJ~VbV|9i8E=v70c<$|Ll1^% z5AT#cFtfB?{O@9U*F0H2^6C3V+q|~1#$?4Ht+9n@>EFeh>*Fh#>v}`*N4h6QdF@f7 zG2aG~%e`}M#3ms_NhOQNMlFo`^&}X>3mybpk;isF%%TuYlBnmqIT;oOm2$7f5t~)& z1Qlgfmlo|sJs-V<{cwPBvchnFn%`BDtySd)1EZ1ZHxr6th8F(ur3!;_6DEES!E0q4 z#On|9o}90H@SmKJp`dK9$`?4>+8vQ^j1#ZhBo7w{hD~#J8A_5Bbf_f?tl!#9$mVxZ zieEG3IEx_;u^pis*dcD^Fb4^@I4N5z8nU2beQ=0}eC=oca0ef4EZ~6TV_y`#|7=N# zQtbWbZOiK=7DDi_sAvbsQsG_NSCa@5&KvI&&y+Z$L!V8G{K8XaVfb23DHJA2(j`&Y zECJp+IEtt32YT@FQdEQNbtyJKOC@q;K~y+^-7XaM);XS0^^X2I^@}}zxMbHCxS+a5 zB;IvAgihGcPFp*5=+zbue8@xv&8N<>EF8`f`a2#w*3e{ne~KddtL#a!-DrqHldB zuF3Uc#BaE7%7wxXRS_)Lq(FYe7J*LbYQ#wz-f3#;v?Q-1#7=?@#A2#T1eC55AZAQE zwUjw#tq3Qz;0xes>oTU+M;VRciBsjpau43oR8Z=aOczFbt&j-f4p}I~{uU&yn;4Cl zT^+Or{Fl3fk2oL?Rz~DElPy+8+LI&_=H|ntG^hZFpBeJ)e=IkM(Y0;TyI?L3I7ot- ziizLPpP!ZW0ZNyh;GB*0obbL@HPQ@Rq%c?I4VTn$qe#dgJnqtGw&M~?!Vy%|&I+Kd zJV$*gnGy_SARg;Vl}?wBTyaD*lsyqL-EX>nb}h)0$g$~krxfBq&uiCR-Wy5&eAMCI zsz<~JMEab39z8nMxBl=>mbt|{z}XHce^gVoC(lel@w9UASt-~SZ7QiW>cOuKH4IoG za50Dj`v0e8E7tC*ej8i5E%Ea+HZz=61`lzK%ejLCZC!y8K}R$FKyJVqD9^!im$xTb^S9_#s>Goyde`^$O&KL@f$zx%$xT)3Lv7)KC9^7x?C z6$TDHF~rwT`IG8@Z`JY)#9vu!#Je~!*clI_--?{3sHBQ!6X4Tu@?2gSmeO<}#JfS{ zDlAOoM_Q13=92n;r}AYQtRClx4*QaKZ$szavCj6;Dj;_L9e9vX7}pDwBMo~YNnhqT zq@oHrBSO2RU}pye^t$8vfcE8GxadqkQ0}m_mHJEt2< zkNj)FRhVPMDD>@gblQyYaVvBl9Rkz4ft*8xqX?E1v>i$pSaK-TKMC?QCa%SWbc=~g z(icAhdwN~R8%!9Aug8VfQD*x}8Yx8n(oLi@6d|oJtkM{9Sa6Iz(LRuctq!Z#JhBC4 zT;oPdTDm&6z0o6T>_4r705?F$zeIY%BPRQfnfT<1{E{zb3;%nAMmLqawS0e)tIN@) zyyPXO64ylJZo>y)K6KPh*e4Z98OCJ18NUshfYa7=cv4GX#HaPxfVgj4F#wJz{D+4f z?fwKj%-^kIEQ~@Zc*hz>NK_DXj)Eyw9xGAtj9Q!@tPCvb4&kb`De>AkYi3et^&ICG zB?`O_!$pwlbuk==Ro~PY{4dNJZ}9{h3+cli16df8Dw{rVAHt7FYXwKY{7gbaA?Sdp zI#DpWS)4NTfOz26!)MWFt}YFT&feqi{ZnwER8zLQn*w$TR;(smU_oscYl27dUDB_yI~qbp0AK&t=`3WiFD0@n(IvoI{u_FgnmkG^-q*(g$YU zRX-BjhR-zD<79XYw#^=v>{p}>aH<`9LSAQO5rYr9r*|+mz&fS;St4}mX$ml^uzI9v z2g#1NHO{Bkx2%0^!pvD5g`#g~2zS`1&+9q?p2%Q4bXqeX^gI)PFIM3$k@H0(i0vJ| zkc{Gl?AAe$%%fHFl6>dH#3$EN*zzI_7gh!qE$C}FaEawBhvV4HaJowK`@5QG0>57J zxK%6`p8cvzY{J>U|FvB2xxK0<0@Y^~a54X_$8%_jpySFt8BZ3M2#LOL0>suGN*I}n z(IP3<)&$eem4%ueqUu>|6i6kVy&toU!Ex7u_5eF4Qeztcd{N<|Mz(<)1oKHthsD9? zlcGU}nwma${e2f0l6|ssE=GBso*^n9G`=^Bbi~D(LZa*Ei78Q90-GU_t9UVx;{L@_ zoWokZ@b8#1D{0OkYgq_;Oc9L5@XJOf|MCI_fQ;);E3M5lRiH%|%$f&_IOq5PFvp~r1kArdeTz1qPKWC1hhY*sL1l^vC(;R?;N#)A^tt+{Jxt#? zIG@!U{*BHJ{Q!(4-xRJwgNC$Et&?B@PJ0nn7u6`=&qe|8J0gh+iF7^%91_%LuYQaGeI5S5uMK4LYI=9dO#S0 z0!-2@pfcI73!-EJ2`-@5^pu7INoUCo9{;>5lR#O}=5P-ri+v6egl+}(pl-|BuL4b_ zIZyJaF2keazw@CDPUOq9=V~!?E=Dk^p{knl{{1#N_g*ET!Fun}U^o)1pI`2)(%=w% zr}g<-e2)MAQgDKr)4}#~5t5>)7Yb^pi8dCh5v=@b!dupq`^;p}>oX8Db?R10z20(S@%#SU36jvwzGPmq`sTNWO4<*}ut znEheaMyFpg+19x$#4S+_V=1;0fHLwLpbwxV8i{-3rwiS8D7^7PVw8zolyg3C*)}_u zWBhI^pBjenqyN!XnW%GM!c+Z0C1}@;kuJjrrMRHt(&G}vpr;Uiis5BqHRb#sTV`N9 z@G8A;XJj_)h8aQa_u*0FHx0X@AM;`9DbM%Jy|N^-1G#Ozge8oHO$8|7qi=G+eRpqA z)OxUT5bZtQ%dl+2TdUqeVDs~oqu0Fvs99KvYz`#1=-G`bMl8nIUY1ZBZGnL05(wYE zgF6CWnI+aSU)Z0PRchkcbY9gE+zO3XNOBDI@TeMAlX`O)cg@z+9uVO-P;y8KuB#h$ zTP60-TUbyG5UE5f$Fk9&b%b`byqZ#El~}A7y1s|7IRKKnd0hGN@E0KQV{!dw55~p; zZ*1qmOHAJhAIm6xKzyl^^OTifUR*qH^!|x z(rrvv_AeaY*Jxv(cFRyCQEql@qUn?w;|+t;C`XH)(W!*dI&Jp9a&ZQ{!?fIquf&R0 znwcV1apz{>Gi{zp79)qg%0xf4n4JNAvR}02yUb!|O|8%R06VEv$ ziZRtT0usZFqXj`U5nrZX?WncG4esV$$4fA97@;T;rs9F^;S<0FUg`Urx9ZS;Vz>Q~ zV~N*W_}-E6vrgr*`rI}&`_?T1$jkw`^j#SZ z3$sO$*toM+-vFcEhoZJ(%G`QSS^C$`gzlHC(nHS%b5D&)?!mQxZQ=`%KjYtUmQ`|w zo5Q2*IaIy`hq%HLT6i$$iry3GLZZrHGu$G?=T(NE>e%n(j}_tjI=nmZ$8{<_aoWR_ z*V$eem8O17Kd>&5qJ5RFNs%-S_*#3Bn@T*;gsgipzIoQ_I)P7Q25+S4R%Y?#Mnk>c zs&2*P|s_2ZL8p5gvW2X*h<0a=+o`0POlMLO zEsq~Fc(bZ^6#Sz+yL4Cr2(F@SZBO4@mAGNU%~0V(OHO7#{R40gJ;A1FqE|2E`w>wJ zaITUG9V!gEN1-wi55V()tqvsEu97`!>wE0VhXxnH(AddVv;%0jIg(%!(U)cC`C#<| z+V~D2J~yepRnG3Z^Z$qQ+hUbQB?GV3CB5LuRAedo4%_Tq<;oAoFwS;!?d7NPF!uy@ zqe{F zK@FQax8+I+_FYNHh+b%RvMuco=IXs6`P%<9`twEufN^tq;S#bBw2Rwfl*ZkG=SA4{ zt068vIQ)I95uu_@_EirA$GIKVjzz=x<|{G)HAYwGO)ZwsN0xJ(rb4|{v9gf|blN$9 zs&U$wJi-`b#BF=ZZ^02O$EQalk^4JMbwPyxq1gM%pUd_Ml-{>-*XWLZpk`?)H+w`% zPtfH`X!-715i@iGjN~#{-;ilIzgIP_Ly(}+MXsvg-}n#E`wKMu4?KvwV#xl@O!fHx zrWe;qh8HXGl5Fn65(Bu*1jC!WEB&Lc&6AdeF~=ULsN)%i6m_JfX0q&wMrW5fjHSM$ zH)rkb8xypsja)0dnK;k+Vj85U9Wi0!_X$7Kz9|Gx1Ln1dYqY!I;3axh2Q0>L$%)G( zIJ@Vppm7k@T(4UPtfs^O-{85qDl6dH3U2~q zF*>C6Uf-s;b=Zd&*|fzehWsR#aEhk^oB3pEX89J=jZ!M$T6Snj#0BTd{tLc0fwKi1 zWj?+Xj3?JjI&y=Mt9Kh8MS>V0;Z<<2x^@ZQ^|bOwR50>X=1yYYrD~y8z0fEWj${xP ztnNlU7g`_`q=-~Xg6*MvbrSybUlIT>y`me<*^=Z*O1E%RLVpeB)rJz8TjI3rhBTGkFTBvXra zTNUqtLqk@@Z3bs--FPE_dC0i2^IAxAl_5HeBd(hLcPBjblBofDP35wUzzhKqhWIW^ zDhxlzYbihp&*Dh5#!L2P9vxVot zwc%h+?8$UcyFOr^SDYP9m*EjpvTK+gC21$sb_9r>pJ#ygpb2J>sR{aC><|6(z+rFb zEz)tbCPQ0#uU;GC?OJR0_? z@AR{RSf^hh1-nns!9-zEvRn6QyG$AHa1TAh8Z7ekVynBGY1PFpN%w%%b)7~{TlMH} zR4~dN&}p3!2j`PgC3wetKE=)1R?Oh5dMG3{?LpdTObAVNI2ZBIb|j=1N9tB#34~7> zjMw3eI@=bSsNd*Z zx`%*jwrR?mhRwyV=<)y2oNzt{28inilSJ7LE)`nF*EsctN+C3A(TQo*p#W%rfU6|@kQ}-AL$%`Q=?@^iG3w*Lp15mPc8hz= z9#-bh;|{`OPjw!amenFWt$ zti-E!neMFB7L34A&PQ_68#!63>uW&W!mD6az`6;*Rou4U34*E6VV?9aXD{D{nk7?? zqgL9>EK)Vujq&`$ou1*)H?K%jy3a~LJpe04IsdORJCKqR6J`jM8nZZuk^`J zuX|(;jxC%>k!SsU8*};Ry~I#*LeAYjeGhik!uh9lV=Ok|U5nOv<2wJ09x;X4?U<;N zc2iv|AhS1NL6P3!oz)R_*g8IYvWin>eo&HRYDcl%C#O56bZUZbhLnlcPw@-x#_}%w z$mk{4gpuE6MN%z)VfTgGG^xkLLY8$dnb(t%b+mG$*bquE+VqrFPX0Pvci4q=G3Fq8 z6JWT6c%upc#ob#O%u9x_+J$~T53&1sr-g(y_CI349G*CMk%=x96Y3Rb{;` zHvatIXkZ!BJm>>(vpUp$DQ1DZwVLtpKtZN*HynIkwu}ydrd`+dq^vXaN8+AmIZAyU zakZxF#@h7`zz7>R@xVFk*Ogra3DTZYilU!qZKtn%OFjRN0w#A|rbTZ^;3e8fr1==; zL+*ZaOPHCGZ!5Cn-4|`M3B7f2N3VA6S*ZHR!t}nmG=UZCQj;cPG?b3Uvj#}tWd;Q&O47k{++@il6g%%PY zk_=L#VVHz6%;%`QII9|AhpA;x{DH64wYC;(Iqs8Ip}JI8vy>no)q1Th6zhrza9|$N zc3FTB(Uinvi#&>dz;(u%hAM*hBkpQN0u9C1AI!5@Ucu3v|Gw!sCF zlbYRzqL_*!?t8d&y;&T>$c1+phq^BDnox+%x~trgwuKQONlZNYzY2f!g}Py_RvELf zb!dZng*`5T>=jSs6>K?vurgr9G{EAp^AQbu2lT>)WohEpuEUz!OyUsPQ49? z_Z`PA*@*4T{CeaG#C@OC+FB6`7j3YJzmj^8ENl4^<5OD_SHEK3vu=D%E*w^8k;iFR zkv8Z2QO#RDTT!axWz36z_Ek(XN@nn|aiO3G1d;bHwh4mtob~ibK^4nvCjrP*VXryi z99-zI1tr&aUm**&sQCqVr3U2hjmYmxNB>tv&@^CTh_=kDJ#YSOx_Kz!o#|ScHH*7G zJWp#in=YnCeDmQxEa8y|oVNu9`ww^Th7pegJ{@w*7{)}`SEwppBGR2#kb-CFR-#RF zg9ZRJS91cwDL5I4+Px!t)u7p*47m^yZ^lvO|dAfSn#g!8^G% z!y>_rS0V?Q__pOA%9A8H#?V0o=+0{NhxiOEv6*tX754La#ofpS-5#G6BWWME(Y)rjhZF|L{_ z(*mBo*WHrV&|>s|@5a*1RK!~%2ElT<)}SjAJGX9bSI|t(eCZY}1?Oi_+^*K@e;ZW% zcEp*ME^J|a=$rrQ`E^{wu@KCfdUDIy0~)u_s}Y{*lcF$H8FP#7H8JNyhbyaTWXd6@ zhn%(nnF@M{E;@(q1GIuQAtDn}@3(sfzwy1lk?M2c=?LeGcVUAJ+^+=IUflUCJ415` zTNZnR(dv8h=ItTJ5~WHPuK6#1w0F_oLe8VH)fXXk{-v-0%cw&(eDgU6M7jWw|J-6NIyC$ULkI${e};AqVbb|I5D%AW>;bM+ldyvPdJ0g zp_qpiEPp_%z~9bk%9SeWhP9`)U0v5tz^FzWuA7=9sS=aA*{Kit?1Aeh-&O{N_pzk_ znBm{3S<_@cyotg4)e+mksn+|> z9e}J>ipskBysoHb^jWiXX7dP7E|8U05I^ z%Eua;x}0U~JPWIou*rNI(~a8F*arX!%H5fKI_g|YIeyvj+P~^M`8<7U%0e_8d&e) zsoRz)ZV<12r%zu1D~#x`xs9>PEvoV=t`WS%HqOugCSDQ>&4CF6bf%QI-wES9<^FNl zDhkT&Ch&GHHCGv2Q+`qbK#}!K_6QHrP5iJIl%JPU4TUI=tTc>3+<*W7%}q5au)k?L z8e<|aiy(hjerg{YIngKV#Z_?NzUi<~KO`vGZ`4--8ba=O{xr&dbcA@_#>l{gX;CE) zHk>@Oh;@O$nTF5A&RW^hDGN)Wmz-#q)HwnCs2{vp!n&UIwhP;8qOt=dEgsN@4P_@;8tLJ?f~82%_}AVB5_0)|!F zLw(Hn%^zh86#G4#zqH=Bf}63Cj5Dm{EvcJqm^ANRLrjk10gDl?WDWmiPD z5Qpr7?~xZ36+5fDpn{ydx>$IY9o33{0nHTSjJ9Sqfm`rJQHwA4eao&(~B ze~04wc!XbH_8aJ?Dy7c_0j#KIARom_P!*)hTy*T}tM5;DYvT{QB93*B@*&bYgYV>H z2tt+?ff<%D^QAPq+@8 zdQK~I)*RM^{oj9yXH@vy>-$-WzoH~~(;iy6SB8qdfkQEpU?sM)F0HNPeD6nMvLj_0 z^i7GP`avrehfkxk1E17Ya5x$$Kq%pu&3;HhUWC32*GKc98z9L^J8 zU!aZ?vP9xd%R8_*)Qp3}PYs1cOszm;aDnxeK&%?sT{xY}U=XkKh6HZ0>90o|A_dU3 z`9)gOBaK~2dwKkem$T!D$W|&TnP=VN#zTvXMkD#6b#TmKw?P;8!MUbrl>I!}rXVBP zTr0`Z%x>f#m?c=sbBNhl3=3xw4{xHyn&$B69bc~I(a&Nc8Kj-G8n*!OiBHQ>j!y0*?=1iaL2ag2>f zZeWH)K#;Kx#BOd$*wfOJlN{_|!DU0Z8XBdE47~_oG$qI185St|H*X*8{{94HP+GYH zQ;9K(`tANs$->qh%UJC3BlBA9jeqRaJW6wzSie3auViC%a{!DEmk@?&Y9W>W#>CYI1_ok6<*NoBQ=8TjK{! zKoYCez*Y{Xm`6U@zRbdzZJY1u5J`!)u1{s>WO;PWO4=3|CI+AaMI%ZB7zC)tN9W@y z>en9RTAEh_QU2z|q;o-c335eo+`~}Y{BnIG1i(Mt<6Y^Wq3zk#(OE0i68=vx^7I8K z9&l-^ZlfDx&G}REoLL{YI>$yc6mx=MyjuZ?{q#THv;r$lWb3R_2eotj1G?rDuL`11 ztU3_H7FSoc@S8}?I`G<}) zWa&*S{Iod2lS1cT&c$iTrUNPiRBdFl(RHJKr1&mA(%u;Rd^kOMnE!JQ*!C-}Jc8K; zY<_N8X0^obndRp^(fm*!Cd}M%DNHe1GiPN())T8$&gS~JL4opL$jK(3SEsI*j@xIW zdAGX87XeU>=6h1{l)}xe*0Ujq?5|l|2@O5&zJPC#cKKs|*{>oW>8&zKa4y%HHrxB3 zmj|9ix`pw?kUAdEcIRkN;{>4lL86m^Ylx9IJ|YCsdlLBIZ<2MDOP~1+$ly`Qem{KC zV}E%Gc-(MmlqNg}vAaZ0*gtWS@~zR&NQ(3 zv!k@Qrix?1VUv=uFIAl%HRo1a$RY>AsDI^rEUs4O`B!%eay8qaNk51D2;aOl-qZ>M zfOemmgM7NL{>979S1c3gn18R@`M*#|Xt#<0ig-vHTL< zrxZL#bN9)-me68&3(suO9OmiF7K&Q2hK%{`@ygulFj&JBCH1JR|D28z>e&XVo-G%` z6xmW{V|W}++?&-we{|;UCGBrM8~`2g>7Yu1)qT`K@IYbK0ITr5di2b^t><|OP2L5~ zc6$%kQ2pAr9l?MrtE>oO+UhoysjoB z@1_ILS=K8O+HKMLJUyw=Osk&)_RO0x3F3p0sdt*uOHrfv*Z2=h(yh6hUov#Ay@ASq zf21NtQolt^$Pdu{GW_I-;wpdJ22});em^>gApGYhH%Q`%P#%L*8ccSp>0WPVNa zb{fbwhG883_t3I`phcSN^lna@7W)=~By0srDZ(VyFUpZQn|PUO1|zZ?_AckG%%QbD zk~q!rK-OjQLW^k@XtnH!NS?AlmBfPf%DvRF&y zLcLU`okVO=DdlHGIf%|?2yYzlTU!O}yfLJ^R#7sKzw>+ozk`|g z>su68ZiCFf|H(>s0}K`TZylaV&n|5s!zTdiFVS`I-AXCbWP;y_qn{5qW-sM$zL<55 zSQHF^*yYn=Gr{ttqyBrDNwOG-sn?l~X}#f_P!tGX*-MQW0_6urLhj zsa<|bYm=!=P@AmVfP?tacI?x-+UB5U`#{SmuXKgRR6@Iqz0G_B75Cwv?Fq4 z2iA+olaufDemT*$BwDMefXHNSU?$Qr68B~+Omw2Jt5^jp#ez>yk=rrIMHB{-(i|k1 zY4wPT<;SHqkDWX%5BW#0I{ysMd{j;?+25N)XSS0x8ABDn*D!3#l_>-inG(t`Tx3SW z_Y8i1DdM!Hb32oe_?zFu`{$A-gurvTbNlJRrMgpr75&?TzZ}#i+(X??CnxBVR~9X2 zS>vl=baFbKua4^74bSGa23!2i)N0jr-^Lc#VW=(bV{0hFf0?+C)g;3q0FD}cOg{)L z$IxM{l~{=qUSp&7RLiKJbl<6A8cq5}D!j8W^UTcVR3FUGG!H6Yg=li^Rdy}uNSp?> zQwU)MQYEmh;r-VozH3{iK4Nvpcd*jl1tJJ3q5a4fNR>-2WsMfepaSr1d&0N)5KDGs z7iP_bxjU5j7dPR>KmH;;7Esp0IscVht>1}0r7Jwmjo~0RaIxy%(t}N-+j8NJg}m}y z420E!R#7mGu+HU5L_r%}t^=-YQ6x*?FV`6u*AFS}|ERxtwJ}J-|7=VN%akl`iQjwG z6ViF9z7y5B>(My#(+>G%0dYKo;z7fLZ6t75TV3^mx=yG(qDLmqe~-i&{ne?vzv^Z9 zSMXseRp6tDUcY)R8-99TsC8q18_v6B=bbkxIQ3>YriC=V!21Ig#7ISlb!^!Z1OydJ zX&mYoX{22@?~nB+9m+;lUtp<@vkZ;6@11W3_>#WdEv3tar6nF3El8)&Hvp%LY)4qW zR6HZttq^+k?-RU1M?KDFF)&RWMe(i~!i#;KIYWTJ7yJ4T9@tbi)zbps{3(s)xduz* zxn+zD)yeqlzOt{YlWh2L!XspkK3+(lb*6%((Q2&I4bhVN#j-^RJe_nxa6K>!+NLjr z#~5UDU=LvWW|?UwGz#z&5<95gukU~AQy8$?`kY|>g3zx0xeeoz{Ft6RF9--=%^)ZI zz`;(1i`tSQqqd%McHtt~eD&15p$pXkn3Ov$NG5q1I=^v*?}PaAE+`PMZ0MCb0vEJH zOcT0|^h8@yuelNL*GXjHjKUid==b3`;*n`1xb*zoFYDy9Il#xo1{K{~{guSA99P6rE!_-yQ)w(d=ITY}N_{4ccRg|X!c@|`3> z--IP|S70HygD5qf9*-8sMktO~Go@}{dD2~B8pgWnu~nzAmYQ8M*{0=DiN)-Zq&)L=#2bKM z(Ed14Z8!$wWyHiF%VcT;-RHKb)5n$r6X{Z@c`0FeSo)qfd64#RitA0LETnpXKNL`2 zWB1t0L>*dwk!|;TjhyMuRJv6K@NRv}yS^XXnBgF=!_(Y60}O4Zf6z6c^MQG3U4mQ; z8R+LFSZ)JQ7EV7ufr3$-kE+@a88PrHtbKNU-*a(vjx?r#Ao&xQmL%Bvp+wq*Xb|#V zy#j@Z>`AMbkIGP7Rek28&~b%sy%r6Q#i)9HI<-UAW?_Yng39k280Rq;@AjRNJdFmH zVbgc+`(^IGZf^(ABSy4Hu?g$HdLk8Sm95W{e0Cy1&bMG}OUCe{$|z8EfKK#%1BPd- zu72Hw%gamovuFApr)Z{DjxF1Gwzav200+C`3;|F5e*=06IDiM6H}p{f>9&)1@(5jd z0wjkNZIW<>H458`1}(<3eJAI-b3*n?n|1g!r?j70587DH~Z_^}C>UEc@0&-H{32PnXgr zAof5g$SnXID|6OwReFumTy)vG?f8=|^oQ0Sbomel6!0}M?H+f1+asNu##%a8xidD| z;gC=0=8p6zaLL>PN|OIk2{SJR%z-@vAw^zUZVoD-(+kLR(^TR>0SQdrew8^=bMDrS1f|=ag zGR4Fe*4T;tpP^}eSIEt(Gg{+tPk8l98&~Egv{2-0DLC${rbM&tlbq0xQb(mo$Jt&A zJBlr!+kN4%e0;^2aZ9e2BJ=OMBk5BNCm3!Zx5o(xBt4gh?!BRGri`M@7(VK5xZ_3O zIU4ycj*#_}=9&u3a89RgbsR}XwiMEaJ`3+{Q!onRBajZpoX7DicT~)Ahti4Uh~-#t zy^(UIb=N3SWj}`8agzJ!vg?XS{m^C3b3%cWgd zOR}+BbrM=YDa~2V^+Kl~_a#W)zg#8C*o^p^$pv+!37L}CB1EV7qR1Mhjc;g#h}QFZ zseEQ_Bfr`d96`DSZHc_rXcJmKPom&ELvGI!C;B8}a--rG^D_bqZNh<-vW;gdu>qot z*<;S=S%9epGzj_i{R6)#k225=B9Y!;FzZK4Bw$A>2LiUXE{g4XKu)*@GQY60ZPX1r~O*nSF+5>}>FQW=RPq`gS6GPHPkAxjun3&yq?xwPC z{38g#-b{b-1}{0ZFX#5tVf=$UL(QY6Ne_P)7!Bvup)uE>m;R+>K8`b?NO4k{Vpb@Q zfG~YhTJJtkY0<%m&BDkDtSP*Z!-+3K(HZX8OqN!(qoGp%T+ND6r5ui@V9u$LkDQ)v z9?h%Pi)7&&6zrgGHgwp}gr@ZT;gCO}Yqyb;BMm#Y2aad-qOcSF`$XlkGFgHtvf9;; z#-iUd3s|oSW7Cj>>yt05PoO&Z6Rr8@Oh#E|bu<})@n7#58HUy4UwbSJQ<4%&3fkl9 z$Hh}w&UsyiM*70Y-a3 zbgby5C(j;4oKqbAVf_n3My91h!kFbO(CNvZK};pZG_Q$MMvBx10caJwISS$Cs!T{R zg{|@{Z_i7;+=G@^A2krrl{NC?=zT1l1azo#YA(3GuC2|SIeY@I2Ip*T8IGQQ`%YoVOzs~qrGMQ@QsrSDzqA?!dj zcc~TSvTX(tN}#JkR5= zxv-39vG#hK){pp3_S~z$`DJbGUE2CohJ;G`7o~AlB*JNa=(3eara@{S%Po1lKdHC= zS{P~Ol$EtI7^n#Et-3hOx9b#?b?)v*S@c~UddMpn8^dglcnWi*Cu6(J zWE?cqk(XWlWvgclA6L9|3$_EV5+~3v*+f6no0R-_8^~_1)5O|`M zKP5f0K@wCqm<0WbC{!_UusMcBTwJA;sPYVm+0|O}NR@aN%n<37B37%aT+^QSCVgr& zTIJobWY=)G?&mYID4UaVr7A{IKg_t9)LCQnw3V>NhyXxm*tf#3jh0JB-sBF6EI0Vr zS{$;Uy@{lWe(7dxN{wmQua3=C;0!p@tud@SJnNjr1{tSo>&LNd%M1ee4HEO(Pc0t| zVv#0-k0e^W;UYt}uWl%EDxto{bT~8lZwjTXsMDO@&&5%ln-#S73IT*~%rO@;q;`(D zXNZzD>6nPRKezQv%8HLBrU=09$W#j~@jhs6QzrbvY8Q!hNlF$SWj#zQ!Iip&EKte) zpe=1(6I038W~NW_Q?imUF*5(SpiibYBfkM7(6*0h0EI$1vXZ=C?;y%QrP4nlb#|LI zWeJf)LN>runLIsK>_!4o?zbut7xZ{Ry3&MDa)jv@2>SLl?U=c;pg+=3*l@RnVjxeb z&1q)No)WnuvG7fMHMMb7B);$HBxST}72**H>x9JHv58Bm3oF&e2PjtIu|(aV#|TH=+1lFEM5t(dI!p2C}R3NFc*3-i^91jqfCP! z{z5sD&?Ju>BpbArjYALveTuX3#^5i&)j6HRA)@N&E*={&?XVihKT11SYlf&kpl=@W z^3X90%K)~&>nl5XRkFtJx$&kXb)r_JUyeFLA$^yy9Mi9M4287+#WFba1B%tCE64sk zh{n9aAkDO2HTMZiK%=-&m46_H6d2q(2OGA>Z3Rj;m7ZejezN`!yn>|=BvPoP(ZZ?| z5VgHc3|$V42TVGW3E-fG8esYB=G6zgw-2vZ13`uw24ubR6z-7Z7FerwC(osVPa4E?L-`YpQ7O7Z zBCGiyt^P7wz(5OQ5jVw%;WMGk9SxVZ=l^Cc+y)oSP)AgNE@(bfb%b`mlnfFS5d60i zerDY$;^QUIW2X6FRq%VU7WBB3pawe8Yh)Ce7^f&}d ze1zoc%=)s}-XSD^YBlf`YtPUVBt%~WA!2*0Cb1+}d0}daPNh_X6DeCHclouWBTi=O zlutU+Bk!|dw~}Zs|z5T-5ZgNljAD|Wm;b$H|mi6E5{iI6~IXW zRpq9K5?-4r6}smp5ZUg}a}`Focl~!VZ9wFTEou(F@gz3oXN4mk@x$1BO^^+H{vty^ zdjJGj{OG8NJ(&ED!OgCc;yU$=ZUZ?uB$Sl{)9zzV;?*hB@3#)UMSN5Z)e(qmLA@M#+WGFXZ3ii=DxER6^Ce~ z!|vh+G$z}7>~FfebkT3JhISq%jc{fG7ZnW*_%kHq?i^#nfFsL;@k!+FYATghNZDHO zD&K0NUN1bqiRKIRsj6rv+Jn@Cg3C*k^D20{3CEvlQr>UCzW>c;k)DI=q^$OhrS3x$#%~B)P?+%u$P?`ti zEl3b67GMl0L#Eb9>`W)!9sXS_aBIdRk|qGaDL(UYLpU&8-iCL^9(}!%Ex<_k)0rqi zaTM?_6)bB-0G{%FfF#rMQ{t|auuJ(H^MV;$u^>&qFzef>5?>FeL;BjILBDyGiA6B8 z(*v{1l~^L;_U`+}*B5x9mAK5E0Y7wMk zm;xDPoYHfGiI7VHy(K0bsp#l7ugfT2AQYjXHcGAGe0p=z?b*-uMDZ)400id5e<7TX zbs})0qrutB;u!Hz5<>{?k+Ad6)5i5r$&($twlnBQW_~iyAn>uV*p25H)d{4!VVDJF z?ouNI6726!gKC`U3w-Pfu?|_4+ot-DfAhDss|^(kIK{S~iHBo~(wA-EkQyCS}u3rC}DAx>qh`5*yt3Q(6PLs#mhmz2Q`&_W$au3 z=-v;%Z4_&DVZ8#FlTSFVW7Cdx%x-W@VP}MOKK5-ki9#F(#qzhsb@m5MO1&)hW_nl( zP=lYGPC2|ad8;DMxBe;id6<1BW%~gm&vsX}z|f~KRhA+pyvCI5ik&my-gu{jp*QTf z2=2BqZ*7+6D8V{ya6H!RAbIt*Yq5CC(W?*MEiO0(&q}%9#88@1CC#ChC}j%)3WMUA zi&3gII2=?m!U((v$R3F~tC3T>b8NrdHV&u472E{=3<`gtJ`|UzluDa11Is2h6*0|8 z^4HBuIKpNIAe<1CFUO#_29uAx!tqNz5dF!!H}YZ=Bi?(Jc+-0l#%7=Age99hyNNm) z0ZP(4hP=F2Ud%aXlk0=o1T`12BH*3^Mc4_D5Q7i%lUb*ga=Kq&&^^J-q%xlOh4E^C z@GLXto=^po-&a#UH9rlxHetR01_GoS51->FvGVhiPHkWue7qE$G)b48kAUcqykr=N zmkH;EDdg4B+HuxKdClfwm4h1I(C(|wi#0HTw`h9dom;d#J!;i6+;ei3|wpcJAgFH|J62L>!8T|si9@POsUM)<0IbG z_V!y3VhZ#R%UqWUj{$NZi%b zw}l)vLPeMsenI@=3{0SEQg0%n^id#Q@PR3FP3BSC-D*WWfD}&2I_7mQPD9u%RV?8$ zIKdbDg{cqLF&>%;t_ao(9P6if$Un1~uho$paEDiMHd^PX?BGfw&>$?Bna!8S{Y7@G zjxoMOEifZ?ve89rTkIfP_>!gGxHfls5`sEYG(T>z3JB?YR*&@>X)f`P{V{S(388Ux z%7K(*b>6DDe*sKsZRjO5*RT@S&y4X3$F&iZUSj2HCg7VF-^1 zBRWg>2#Qa4pSo5=UJWRn0M0>VKb*0S9j_B)YwZoSz&r($xw!o)2q@rUs;Gd#fHg4n z^`)yN4_iG}_oD`cz%e1TBrXtl2R=WsCOiqhvy z=;c-sWg|}SM0UEdS;s#K$)5IKAtxfa+-|si2S{UC&SGl?hlWl1tN$47t6O`S zIc!Tsjx4car26a6Qrg)qU?~D1aXn6I*$r>%?lI41J#17i`&QVH*T&S?_ZM?m%!rwM z*+4l;B8vQ39ID1n3zKgiQ|=-cl_GTKR?o(!p}3`mdPwFI<3b+u<$Gr%Z8kR1h@jTa zBTmUtbStw65LeCBWHtUh0a60&YcTMX#&v7dT$e)zvWlwFre)WyXpF+zC$;EkmM zTDage{^6p5ac{P&1UJ~8yk1nIYr{2g#2&N8tI;tHe@Tp3^aNgJ9*n4a*`Nt!LGlQ) zPi(+4;p7fWG!EkIyVdaD{M_yozT8NYGS#)q1&9Yn#bXe>8sj;gZ{;`i3S*?+>d;Do!Teq)A8`!K0PCc@Ki)cpjII_hSR@?LTjRKN1~3>L^Y=xSs1l zh$zd-yvMqtSTCYBto!4O8Q&P0fz z=N)mJQ;`Ax-wX4lz+s!hCvH0D`!=L&@yXPnTsi3RrvRaX6s5_)&*;!N%{>&fVuKlP z{@m*g&0^|aR9RpnByOVvoPyGM24(H`s-jU8C{`5x3sn?~<8ypGkWV zMRYSw9+#zmCm2mU2Y7{^IXSPtd!?(|#3GY)u zeU>PL)4QxF#t?}~99YRL?FpAC+c=i4fZ5+|)r@VSpF_OkLQUU2M3x)(=L8(X5jpf0f|@T4~3JM zcf_k2;0#9VAvf`uLTVY1OlmCjt0WGK ze(Q|Yd$(K;KdEHxEG>j1?_2KV^9lo@U8F;VroRj#6Co%>W>54WK^co)i;gj=gk<03 z=9vJ1hd?K>s|rgGXplx_>(#3!-KQ(|pk!)#eF-Ue3L7Y8<9+JPRv9ZxXy-Q1)O0FG;as4Q6}Bad@nbF9cjAS%cnzVT?ptRqxlGuHoJPwzXn zYedz0PE~2VN^pZbv30%NhF`|*?8qzDeX;cWTy02gKoab9Or3Yaq-o$z(*H|L=`kwC ziJwxHK>JNa-KC*ot7&ihgY#i;$=ufEx914`7WK3zX5^UG+n$CjG_D-ah251>Hs}0{ zFJd?bS~RfG@EXFduJVIc80NcsfdVb^V(x|z;^=JGu?Ow|Ph$rFSlcxZlN9;kw(7Vu zP<&=q^V_1Qrc-n!4o5o}lhVt01%f<;!C;mewbaP*UYgFPrO?!&j>sA&qRdm%(>sR>1gf$1{UtDyZk(~Hx`V%%X{ z&NTDpnJJq5)>)3{*(#i#p|ITLua2mpnENK9qbwb9?qTzH%!i_R=lb!Ut*O@2io2?K zyIH~!3(kBsvOx?lWW5Dy*x0y&Uy9;K;P4?S>l>bzflxR{>I&G0FG^zzhVRRq(?jUW zXvZdM9qSyT)br1)u7ru8;^MfF)zS6|D;3TG4V~Mn2fZx`6AyQDeHsYO66aSb@|0|h zg;L0Kb0Sqk5HpohFQ}ZXj3;tGU*CAmeior`^NV{}YX;dnP%uesvgLE=gM|(#!b0as zQq;`1c#7(-%N;8g%kyK)k>95+2>I8**O!giGbK?bf=(rtQ!-ujMXVnV^DZc7d^lV_ zYSKe}&Zq`2RA57@(+_i~T-96fa}fe09z`!o@1DLSy|EQMg^iB)aq*6-#qW?Wt%AF{ z!(EuIen{z)za3X(vo{iPL6HC2v#h&7Z#hc>S>9AKbEJ)P57oD=zRn!-bf&~yCWNId zXH?fXskYXT{%7eko7@IzeLOB+S_8agtshC0ysq#%*}c6HS*T5$M+P&Q476L?_Fbga z?GZdqBJQcPlgx4VwWIlqRnO=yuc|@mljN(Qm7LLIua5zP2|=PlX_D>ArUzBPe*bfI(rfA3m=VOYY8N@Z}pm(V4If8KAv_Zb9vJtVk3M$3=TNCXR`kmsj zdyOO9n|3f!f%r}^X2bHFgg!g^bQfCRf{1&EPhA{8shV;wZ0tTna$og1$e8Y2$=^h(_Ru>ZV_^a*0;+;yEH^X z?W!syNsZL_i)gt?U_>`PXu@X;Z@4@BQ)A}JoJR;`RnS^t!`FL@>f%)bRqaGv7FEZ9 zf*mc{Y~Wu3Hb^7VH{Slz1j7q6#Q#j1z4|wuCG{&x5`y+SO0h5l9>to0lxGGlR_J0i z4~rGf3ozYNbj&LVxS5wr9_w_N>*5%%|}F@+sGy_kkuC7|3cGGP>|P z=VX*xnrss9YWH5w;5|V?or1MJcuL^-hL=AUZqlm&Qj9hj1*b`FL4W&w3^l8WmI;-u zf0Ci=Ge}1?ZEPFVvl>VKPBRo^0u4KcJ-S9saM}uUiCQ6MXrT;qnxYItsA-FB{0AI+ znz4!Q-J2qEWBoOmp5Cldb1Eq#FLZxhI+v03#kIHgDkKz1Q!1gU)U%4BXyUe(nXiow zLffJWA&rz@QL#44f9-8fE{aAs?`iR5y+1G>koGH@T#ub*klr7%&u4{-712)le7Dwx z@<%Tmad_IC+H2}{|CExbyDmc5UBB&Pk$c+{DvmkVS;a<8DZH<%q?`7lOJIPaHBFQ8 zX#@>I*2<`R5a-UdweZM()y@MPd%b!fS|3a!H>m3Af73tjT5`X2cZvhHDd;G==+@_` zlZGfvl>dc*cIWdg!dfd|nJArsw9a~{wZP6u88b<$v-qnZo;dbk_MR6tTn=QX^=`$8 z#n3bb*)bzaIuwSqh(^$8*f(oU(K4C4?0OPK55eI9fyNaw{=<>EdKC9#WfJa#tr_%t)lo$lR4#`JHoUT<>Yvw-u1SXc+{+LL#(F=pQA0*@Xs9ZhyZQ5L#7#u zGQP0oieQA)dK!4>wmbc}wTAXAhYZ)u6FNlaBZSBVvz+R$Fw&5rjoiUN@=VOCF!M;V zES%qFtPPrlMls|WpO;B{<~31U#6*lvC*v&iI*s`phQF0Gc)5wJV&$#iS{X}N^G$gx zftp1PtaLtj7usEe(>#WFMrVDYL#{uz;^+M=y)YUIGD*6Vpi z#6g?2cu9olR37J9wVl}M%}I=#Q1wO7Nr#rp^%5XF_ZP}QxLRhhOSP=H4p5`%ZyC+T z>X@XOP%QDHlcP?}F-iI`?zE*XKm=~5bZ%S~iNaj=u|64cHffN3TZX6WD#`T*k*Toq zWQ{%SicrKWVE}&0hB%Q z0s`9E`-g?>0!rdiZ5YQV3*reQu2* z&L4r@SEsv3$hzqav)hUB&L<_(62*vrAbOn(^;Jm!VE$J+qcBXKkHdsvZ+D_&>4hj+ z)j607s~EbLPa(lh5q_p>Xl&83PcpLqB2tMPn`H@S%#pELf|ZW=!9tc4+z$7`NQd=& z{XL#E=Z_ISr*XlF9J|=ts_9rgH~YJWi)>XU*&1o^5o6(=Ri;I*H4Avcear}86W?qa zsy#6Ad*uXK^XSOM{ayz zxlfEQ8C3NY_5`h9Ne`vzu)-bg#48ZI$N~6{hOxC3q=Q|S_OM~F7h*Z}vrMWjf_Zbe z6rC0(IjUEwUQx$_axpLD+ujj2BJ61_yfsYwXe2B8~ zOabtt;K}sf-Qo-LH88vq(!#i2726inr(U3duO0d5ymc0WOv)Lq$HP_yf>|!exCR>WlW zM6x|uvfUbl`8r3)kX!~?jnZMTLRpuWpi4WteYJ8<%hCAF>Uf)%R?BjprC~!m`v33G z5AJO7cX>J#O>;(>okP_Dr1z72kZzYGXe^h4TR z0I+R9+F6)O1%2X(Y$RU0V-P)M;LErwF(?+C`=2+m*zd}etT0(Ee4qfNz2heCIvg$x z;QD9ts!)xUlY-joWxD|2sdk)^i;Rfc%aazC6tqNc=Ue6ui|PM9sx1G*zCT9*Do@Xm zAB!xGsFOpkHg<{WtX5)p#|1X|+)j6&(LLKxJ6zYw304v3Lr~3l%u#YYUtI(5#AB@nOj{IaVy!Hx#s6xJ2E6Xw6*ep*xgjof7 zy1S$(9sZdwgfm~q%XzYwS(%J|$_Dp4#gq@|)|PQ5qb$hNd-@s?Lkn0L*Tx*9be>a9Jug-vu|VHdE4)*a5oLR>MdZAyv6RJPKdI#8 zaMnE+p|q!1kg=c(GVikpS1hl+j5KZnLy!=nM9i-QG(q};h8Yg3$=oh{H`uXQ1%<3- z8WOTB^l0-K%J@RM;f%L|G4iF782COKzMe}UCd1cUbDpE8YaW9g$qzy`DeEHC%y$gA z!{x=}wg^Okz^nV7{YaN@eCxKh!$c8yv6+tr1S~p~CAmwc^HgXe73N_PLu*BobIA?J z+d0B(BDM_DDS+B476Lb5?dUgj=rb~<2>cWNkZ-@- z$4=(84a9vQo-4C+19ZVmhe%z8wpq8xJ5zN8iwwMEtIkzEg4OQSw+zbkj=`>iu;}fE z3bXc?W6B8w){Q+JL9aFj&`8CmTijMQ{-;KCN1)t9#T}X}=@k2RssenAbo+D!>G3%R zj1;uH2XHD{YC|CO0*au5f44@gvTlvYdEL>kO#>ol1}XVuzb^YM(Y&w=>{rT~oi(p? z)#J6XH4&Dmpy?^DBJIDPOI`&8QG7qhK?b*X%u7!vM=}Zd3KU(R?e$!<*@-ll_H^a@pu;Z9vpAG9yHlQ)JcngxVMWb+eLCtxod~w@k(I*g{67 z(;5(sekoA<8c4(Z&o(`aAyl1&DjNE_YJZ-*%~5ltr(U0%rXuS~bjDuP?5Js_n_cUm z1c1RWV)Tm_6QvLA5nmkl{K?Sq<~VR>=2o(0k%)fJ3Hw1Vc$!Oq2eZ!YNs`qaEq4NP{{_fWER(6+Ni{j6q?Ipa=& z%dGD6rl1AH7u!z7u;LlwE_e383}VX2Q2vFZ9!5xYyVWuNL;FA|^ki3LLl_`%D(;Ze z6W$%lW!=wo8$XCwt2#9k5xzvPrsY#a#qfY2jUjUHnV4)RUQih588KkgX%mIvOr8az zwDi{Q*_^jLrF}>b1}{2#WW?S?!oX+rdrzhjteY`fr=SoKm`!&~;#p>FXnn5RwkHKh zqgHA{$V`-w?{lCTtHDZuiYRNS3NMoSmN9zGy#+mnO4W1^0dIfXFW^+xJn*S|IK*MizX}Wmc4;f zsXToWh1JYeQh`~E%Z? zR6T%L6s*R?Y8X-XX>#;}v&?x`Y+yZh#PUa{1NN+}Yow-l2p?;9d!0;}%fGbe9CI5C zI>x2u=(d4eJ05k}JomB3``>~h?7e`+1sK;3Hr2U(WcFW59WPaC=L# zF*>Bw>cE>gb_dgl*Rg@>tUygja+XkjeCVcEEwuwQ0{~n^jAN#HG8j3e2FEcl1$$7N zQ8XEn37wuCGoX0I811OQ(NVheIY@Id2`!0JN)jR3y}4V%!6*cVX=P_}ER#Aq$M|l( zKIOvEc!V%+)@y2w%c*<9^1ztHn2EWyt5HW0wNfPBK60$O%cv9UH(hpdoOz|R9G!u>mus80TWh7b?={%1_Z!1P}E}w)&YtUJtn%Sza>rV$JxAWtcbbbJXM*8TLfaktpq>Q{t{qy__)09-I?GlG;=dE4V6m@%3-)=}7DdnGu8g6o{$Yc7Se z%+J-yPK1g;#D!z9R#g85#kxx`8Au}4h7267An9YYC*l{bhxH6_5*g{#h`_-+kYkCK z7qY_6-7+B0&+RELvFB^cTg19Qo8QuyPHH>2KkpXD%`dYr!X1L%7r2V?%*z(}f`1+hIQHnt+ zA?w=*Y%dT+wEr$!$Z<9zLuZZ}o91`k6ipDLVR*mSLfu@g=L)r zml6yRuL%aLqOgPJn#1+Wr!rT!5EItmZZOKXME7|CTcz@s&Mbt|VW*f; zg*a&#vLFVZ;}i-Q792wlZA2tiyo1C_VA;oMQI^xa0jtbNaMZh-R_qXN zwr(s1k)Bc^19dQc;bv3=_5ia4RJ6kz;rxWUZf%4<(l zb>V9gcN9%D1ZRQN-*DfqBS6q%J5z9aR3Y)E^K6dUj)cFPlheIYu$10|=y{L^F0?H3 zVR$oY%(D6S;?;wSA)&wW`K=slZM0X*749U5^VM2`Z9>;;LbI|RJU>{o@Wnby3Q@Q} z8(^G9Z20v@nn-<^V)}cAS;s$G5)9T9K)4)u0OaS;$c`vUW2~#T7WMg0voigHADJxv zZCW+M!$<0iU6p&g-`XtXmuXS64mTft&fW+v|kzH`gmPOXfj6s|E?$ik3@ zLbBzwLa!)Tc$xGJUlwX<=z(x=$lM0(iZk&pNUPN>M-HDI&xReE&oxUZ+mFhao+j?{?yYCVOmfx3mNYnQxZU67MMUo33d6u60GXh=@xXwbrXzRfl8&m(=eY6anz z&b*#R%gi9tpV_`A5L3M8vlrLQdyerH7Rf)jtiB3C>Io+wE4YbIR6l~TH9%rRtSAXH z9#@QkjpL8G=?M%>Bj3esuioxTWU4>_7dH{4huc*L1Wyo@-00-0U9Ph|~u*J((HqV0@%j5E+J9I@1_AJmoU%7Cg{oA?WE*uakWvvRN~KQJ8H7$DRnci0C@52X$n80)hl2xX&axKP<#;<19F8g>eyW`|Ue!-Cf zKi!(@IjLF*$7Z#ql1$dKGu!y%zmEm0oBtV{ba9IAE)vIn=JxZoRMY)bZ^)-m+63%p#-7^CX~=R4iw;>lQ)zc|d93z24}?7Em>iT2!c9}*ZfKCTml$j)=0~Od zGV5}SDL`&oE?*hiiLLp-Equ=#I?OK6el z)UVQ*>(k$tQqEc+L9p!aG=frK1Itaq+Rg3Y2E{+n@=^E?53vtCaFJgLo{y*3O^D59 zUN+QSJC@IA#iVUSY8srXBK5Cu_HowhpLOyp0s4owy_5H1R?P)n*~0b3gywvibaN)X zLBSY}pLfJhM{S7|JTeya!`TgRv0fAIB1usE{%uF_bD9o52h!H7_Y{~`-aQXBVb^e3 zSb&D=uejO2EAh$w_7~l(l8z4eKKl2i6!!sjXd;fw(Cnutuj_yP92UCpgu1nU%3+#0 z(&Dsf7Oo?Q+w(yuu@_7M0;f7p^?~Owrd)Cb0xgud`6%S@q}sPt=*{o|P1qgG|6 zZYVO9YsSsZmRv!C#`(ZCQNA_beoir0ox^YnQ~aAjex*`#5_)}aP-%Q6qSGfftx6_D9H0j&lNt+PGgMPm<-KzJHkaxMr!w6QH4=d`@j{#`wJ-P=M%tCfoS{ zJ$+-c#XT)HGS2idsCqJ6fJMjnHg|p}sYbyhy~oK(dTl>4f`zCQoh5Pe-@b{kf=wqX zSr+Pzo&B??RAxm9cqzkrMAvcqgEbYbQ*OlgIY9#8Qgke395j;ZMO!KmL_s;3>ih80 z!f7|tsZ8$#cHQQoo*l@k?{^dl_<7_JFZx?Ffq+(k$Ry1p?H+=E^Nquv|3sHoz9wdC zJ-`0t9LCEEGGoMuPfzDT0TOP>FO($jPZ+Rp=K1at7>#{R znUG+~Xd#`i!qx!-u}16(P$dihz$G!hy1n}c!~-I#pXn!-cE{`lqstN!iPAjH4`2=4MQ_s=O`; z@57ymnFJ@IS$Ng_^*zg2p-x2GWSDh_;tfiNh)&n&5v>I2Y079me_a^yh^Xd%$8Jtx zv-n|+lZef1Lr)MxGq-M$97fq0Dcmp)X~y4Bg|DI4=+qhNS z^C3{!4}`r({?u1P(n=a7_E2~Wz_j4z%ap8P7>1ab6e0>xwocZ1SP1WZ+)c&Qm+F2z zaT++>@Oro0sX&zZfiuB9K)LV%?HDRhd_kqGri(gA05*{Em+{Xx2_q$1PH9xGP>r`t z47zlk{kC}Mkl|yRB?yeHy~Zg3XvLAj6r^- z*$he^5dhB_oC(HIUsG10>Gx8{nmL$9sL*IlMCDqAmGa-RDcbb{+^BBQ)93;cwU}qe zgM53+PRuwER#O+c+}y{qZ#|yjq-5=dZg<((Ba7#n>KfHS{*UgbSHupV*yONRav#%NV(`!*)1;DE^ zVp6c;!Ems3*3VHduq*(s`5^4F<%=!7MzY4%^SX*8cM^|SAaIz#vM7W!WCqXF_-xmj znJWw%89Aq=go__dS_$t=*aulYZvAFy_m@ilRG5Dko!N@CYM39kEC{bR zXw`eY*5^j^CqHw}zN$0~HG<0)@k-Y;vbO&+Kg^%O+r6PbuIlO5nA1v;d+J7&RFe68xIzFx~qK+3(}(wJz(fIexX( zJqr7|R^beq&S64Uf3A~1TiU}znFct3aZ|MctI`6od&0CUkzWUoNZ8vf&kW&}FlgOo zaO#l~9B}EZq+)-#>E7Xg%9zcAsyd4OPLts;`gK%*5BDM&qgE4wE~E?QHRXoa9Hk|= zfB$-PY9`~RsH15%^XnyoyDKed<%p*0M_LfM&*;>Yfqf*y_Jey98X2|2f1(0^6+M+N zsQ%34L=L|BmH<-Sq(2OCKsFk~jw7aW))0$||bgDXR_b zDgt^@ATqHa9j2?Ud5uS1|LeyAKg%vx{VoR5%bOF*rlGA8$MLC2Uj-MQcC{S)eYDW8 zI2t*Z;gK<^#KgkQEBNb80^fj*x(nq69q~X9qscPaB1%Eo5+8ivumVms@{Ix$k2Opf zjfn*=(GOE5?&0Zhags|e4FIRr&=-PUo|+4+EG4?}!UXn(?!N_({o%v!AiIP|Yj1iS zeB6;GNjAywg)wB+)M9kpS}RYJxl2;FEx_&BMEu*Z=@IJf_O|tC z`8+i#wqey=!$OVARWY%!%WbCf0WA_I5O1FKE6EcLmAbv}NqhAPd4$gP<^SPxUA#jK zQa|0$Ph;$o^)UjR(&?1?OUlk%LpuA+hQ15)X`2cqBIis&qD9{$zqxIRvVPu9$M)65 zZlehY-oX$YrRYek^h_ypLKDKmLVKXEr_JwUqcQkE!XS8H4B0%Y zP4l#!Q>NtELR`|(B3}Jh21K|y+qixY^4}+W*-}%J7VHCc*g|7bI{fpQMy>Y>iKq_D zWZQv<4GsWJbO@fqMYwYB9v_uO7-GRakgAM-g*5@xrh);DE2}%$v~4^@#5rmi02gt$ zgL-42c%kjwZcD^eJSJy(kG*443|y}P)TUga(i4GVaW5t;+^olx%^1vGeqfj~0)NTL z{{xskC(BMdD$R@|W=PX0@N4NZ?yrhgb=b(Hv$sZ?6_Xnhu;#Oq{(Bo6i$h73@W@qF zQa(oy7jBPxm7u&u<_W-|?genopgA2G1*0WEki}^TdD8uH+&p-2oxq<+s!c)240lgy` z4Qq6~to&`L0)(v9)qTn_TWa=XL4(7*|9}8GP?`ZDqV*VLbE&uo)TBi%>)6wFz!cc2l(uLYd*;Mq;@U76C*JX` zr5t)LcOHq5Sr=qj4aa^B>7|WcU>f4TN9ZCA|3{1oK*r7TlNzuR44A#7i^R-ro{Q6; zx}kx^Z>S|>Si>QTUe(y%KPqHyc0n<-;5UyVAple;-As)9%1em5tLCO2WKzBm-YaO=tNQoR>ig8O=K zEeE6~ipg$GE60^8Bg8@WUiZ&X@BTF01E;W#1t;zk$H7Y|1P>lgDTM45jvl<8^WZ`o z@ac6w(};&B^sfy7uXJH~r*i2SHfxbl(cR0tC&F(2as_EBV8AuYAdscRlR8o;Y?q3> zLuQFP2Fe45N;9N5pNKD6b+4Ql(?SGO9Z=B$|>b6aHf%!7Xwk9h}jJVYU% z!RA{&Rt_G}Csco97OtW@VlH)h z6c9Ks@&d=ASdi2lp&uZ(4u~+;$dOL6j>6CfY+#`fB++GyQBKJN2W&H%SKexbe+t zGAAWbprOQS1lxbB|J!AbN=4|t#Ki$8bG5A+7-LdTCz-EhRjWGa-f}{+RZQ%3t;G?V z8WYlxffiTYsV8^t;`)`8lKRaEKFF6UoOINi)>JL-oL7CIKeTtk>nqI);jhT&BG_nF z7k|c%=_MkpLuEYV3^29!<^=~UaZ)K!= z+BL4xB}|PPcDHeS?F)Vu$Gw@B!q~Jpz5Df1LP%E@?3A1JUuy3^hkwjEm0;k?1cRY= z3_(aK_qlZKbY?|;PGCm#dxo?Q(M=CycY^RFh5Ei4@;(1P3Wa%U5U7 zjGa5;Jy|q9TegExNQKCuUPy=K#9cME9^`Kkr_S(mvw9SHwE4jz8A^~roK3^qcy*vy zE)*}vWDG*wCgDy`hDbDZU*kE1YwvxK4Vu4LzKWJ0$|lW}NabaSWQn{?0A|C%{}|0+ z(HQMnf(uJe4dqr=yOS`pDVHB#xs8ZR$SDnW=TU!VVbkA_fV>cK6WbaaCBjbh<`3F$ z3Bs9CRGoAvaM1g(!O1&ka=DuwtSws$dm`N~;MGdTh7@K~O#6za7Unf5AI2ev*HtZH zxdj_nmF!mJ5yOr`82InHOAl(nxi9%M%}kpArw7#YD%QA!m5^dl_kQ_Nq7;4YI-88=WB&X#|Xcsdt!oc!Qd(GkQYQ>}y6?>z`Y!ZhU0D^HBp4=|?x z15aZm>B!0~ZrxFYVaUsb?8-Prt#emwl=+D%{qZ?8?wOrWVQKRi|1^zBblwTcr(U9v znupe~ENPD)Y%JZcA|Po9Y)9i%@hw%5gPYuuw4`AO^1T+R9+0#n3;yehl`{UL8`luc zWvJ$$qSR`OAS5kF1T!}xPY}*v8ztgMJWdb)o`g&uOh`n@+qov+ir6(gFiJzq=p|px z@e)OeC-*C98@~0Cct#t&?^*f{Fm{kZ1}Lxo(sLBjK1(1zQl_>7xQf$hrY?UZHu-$$ zgaMa$&c0Rp3I7$ELND|X8W$vdA~C8=g?O-z>%rQ1BXz9$pR28E0XRmP8xqJADy z`S>fD3*q~EBf^&h35ys(i+@JSo81^#;C_x!iwBZ}H6$X;6HV}b|FS4% z25cg*ny27we+t^^V4uzt5jO>o<*)IJ0+~vRxH%f^ODc2r9PU)4-ownN9J)FXM`okH z5oI!QVHqHb>nW{SUGO8@rei%@2#C1^>R+B=C|9t{gI<(Nh!SG72m1M5h4xqOu;{FT zH`4`{MPfgfK-P!rk3d>Jb(}{+_N<@AINDSECheDMwb>S|sMM-;rQc@NZ6TC(9bUeE z^@Tc9$%r^^$uRt;Jolu(v1r{;27%4rkIjLjta}2iHQSCdm*O&27EmqD58sx#nZM68!p1kNkoCpLm3@6!=X;Ju|$vCc4_wC8CJ4 z&q|>-WIwdyNj;1VZVvaX;Ec6PtliKH-W|MgHWDfR)$8-z77H~#ozqTlX_j1Z6P~ROq-l>F9 z!-9c6GGY^IMFZGUFNSomtc7|#Wm9Nvjeix?Rv0gD*mlt^mTJX-wtCEhmd5pnSM+r0 zZpGK+Bf#U06H_2p7kjvaC3I4*=d^kGpno$;%QYVP4K?$dl$_HA{l@JcXQqd0#qj2V zlxA@rL{1l!o2G(8Pek#X$;lo-sQF&S~k)p3qd8LDR zNsU^_nOW(_@E~qRzaSZwu!`zBXj3-Kd7rNC<6q4}w66LIXrfI1hS8JX~cwojLdQ9RX%*0ihiV6K z;U&iBUrGX5kMb35lVh$;Ai+!H?jQp`d@wGga;DvABGA@rK=f_?xY5tm5;rtx0rr@5 z#Hc%|gzK~s(QR-#6%J-P=wBU6mP7#rL~i{#B4g}v9n?r4A+yu?D1m{kG!TSyZEDy; zmuJ8iv5lrWE*2Nz-cmPe48z@b{K1wN=n6w4w4B!i5p+C*+mk%ORMUMvH+)bA{prM# z!Q4W7Q-Lhyi6C^$&$?wVdOd@OB#!DPQ{*=Yow{yadho~B(fZJ?`tw z&9hYP{*KHs7l_8T<3NDS zJqlvHWA)N=PfWh*j=9`Nuq=4w*r*tI3rI)c zs~9%csE}U=J%KQ106FusO@c@>0=FMLA@HA@LcKRO3}u#a5{ye6HHPv~+N|n$W1_Jn z-(~szfn&^vom|XQ+fFFqrT7`z8U+Wtoks7Q)nvAxuzoLry=GO%9kdxpeMn?t1a6DS z*^ezReB((su&PoeBWKS~K06ek)Z%d3>@Qq8GomGq1n3+mr7ro}nM%FCK!A?`(=t<6 zZ&o^eR)L1_X^tf()HH*enWTP6nN$r|DrxKW(X5YV;QKk@Qo^Wqb%^*z$_X`Q!TL9< zcc2JMcfTm1jW*E#wPEGE{OZE7&?sok@k~fF_xD3M>?wcZSDnK{=>bw+r;9L@FMe(h z$O9||fFu2zTVQL%;=L?C&1k`TMVu~x*rqY^MG9xJ{U9V*Na0gwRB`eDi6Ej?<_;8$ zMKvL{_!V5^dLz9+KzM`hPSRbHP_(?Z99a1GKbR#_{|K=W3#4^`S zGD$sk*`CuUYYme63+Sbi?iDPlZ}P3^|;I`GZD(meHIX=0Au*yAPBfcON#~OZiddCD@pHzrvnY%ye%!i^|U-8mWtDJA`v0Po(H1u^lg_4atr`p))!6M#r5_-IUU4@dn ztQ{*v%TvV8zg2xW2QWQYXttYYW30x}l;hZEi0sF?pPhoweKcg#>h;W+6KK3-!j-cq zT(^OZ(_2cLUAR9Z-lBU!T#d%sZ%*Tj3?XxXNZJ{Nm2O@NQnyHWw`Ad!Qdq6_p^uMa z_-e=9u?k^vH0xuJ0J3N1-}=i7YK9_0Ruy0?E7lg37$rgLK-Zt(OvnWY^37L zjUT9O6&L^IG-jQ%|D&mj40Y|YB*jg>6{eIwX>-P|DubrAA33#Sdw_}GU|Cy~trM3? zOj@Z7l$-2SWVal3w#cstrPavAoQsXY{rvNfVHujc)4|Ucw7s8@5hFl;?1@J4VB9pb4!{RMvQr zf6e}wORi6+7WyCefwFX+=}NgsI313buy6zN%KV%3Y@4J&cLBX98apRse*Z{((Iey( z>d>*?Vv6e&%g%xadiGNJF&Pe(3?C;WKs+FETS7}$ozNd0ZTOBgNQ^LGf)gk;lZ{(y7~fsvfW;@q+>rNN6Fl_AJr z2zO4CK*=b@1CoD5n$ODHPB=y7lw}Yg=apU;T~EQ!pdmEj)-81Nyn-i!MZuD_5eecT+Zsr(8 zKK11ocG(K&G-XBEGXRrzd}y;~BA%gW&P5sRhq04@F%M6s!2gT5$s?k1+Hc~rVD8Or zvlXE;^p!8#%*d>Z{=P`D#}VU)f{03fEOy%R#9J$xGEBv`WU@NQ{g$k5Ys9gS_4jFS zK^h80X|EcXYv`4({KjJ!hzMW;y^Z^65+_gY*-TzTyAk2)?DqD<;H>djVm3b|kM3Z{ zXg-waVaIL<_?{0asIq!7%z`ojj*IZp12Ct$FSK>_-W6z5uI1Dr`Y&TC4?zo7v=v3) zsp`m@L-NWiCaq0>{q{EiRl zGInBK)&JU}&4v*yME)q0VyoeQbwYB~yGAJ&7tA~tt*f^&%AqAiAVCT3)ANNI0ejz~ z7_Imv?|mj-L*O(i#83IRaQX2O$o*rCc!MCU`7TqQ$r{$;G^3^Sxr877 zxeb#eZ>t3*Tv-l6?B_6CF_!0;O$qXnT{$ga^d^W<$jj;DT_RM6ZL`ulYdy~WVP2xk zHfN-^e}7x0u^QZA)w(8qoHhT=jS(ei^=r%tw{Lxw+FvaQV)XTh2q^G^q)YoxM^Ck< z2(&g=uh#0YuZ{Y=lwF-$XBy?0)2{d$i*>pTY56D=B8wfy21fsjCF+iHrM8P$ zy_%RzDmBsRUawzsIPDI^%V)I6a2h`6Z~bqNgbiLyv5I&ii1}g`&jq(^<9r2$j^A1{vdf60eT2aso0 z04{ytk?WkmJnw4$d=NRETfb58dg10L)*&W}JHEh?hP3?p-wQ7^%cv-y>489P&^{(< zN0?UMN}Th}ax4)?%^%dVj&7Fh?`Iv!UVX(+4_*41aq2vtHHb|%(D8lVr7g>)2EV}# zSue;|nReo1<(qat?M|s1FO%Lfl%3@+P2!+vZh8S=<6|^5_AA4S8pv#0RlKGOk^Nmj zb7i^kiMv1Bxiv7`iNmnPRzAkU{QC;Y6b0EpsWL|2S%Wq)GGE@GS$tv)~4pRN!iHJqPd>rHCwE+>Fg*1XH`(TKJn_h znq;>G=P$xnRTB91b-XVRT2ejY!!!fs7d~Q{l(jI}6%RTDLa9_#TLp$)l?SHe0De7%}giu z^4#iBWv&29^9twDjK4fy4W5)AzuD8f`D9FrVtoVah+83}Zed&L7l5++xIe@d+>UwR z<+u3^L&jd_o#I!%KfaaRDX^i0KqBoU;)`G<)W zA-IFniT`CStjFc2RYnCQf>d&6$J=(HgmGdI)%C2yx};S+AwgL@>Nbv?=x-goFOPB7 zn;iNISh&n}ii04D@V7mo6{t+IY_@iD)hXMb%)HE>9v6=KS~S5Fy5Upc@uuc-7p}{J z!sgjaD+dGw+b>wX$u~0oCuHvq!FOCuVtDC43+L3t=w1b~KCrJG;BLG$d+c}!J zEIU7kFyzlS zBVqDeUuEs-x&>MWIk@l|yRRMJ#-JZ4lgH%EEA>t9`#%YTCeaB2@du^HcHWoV{Qq3S zbk?M@%SC~`)n+;FM;C4fBf{@VN=7E4$JRK#_Q*nM{D6j~QACpR__cVZwBRGG5l0R& zwd1v`ugsvU_alZjJAs$8*1H14upz0})FcUq2}&a=td7!9lg<^CX&~>!sMCQPxdaHE zMl2iS){dXAe+@GC`g=-c3VjQzgn}Aa*?#e0wdDT&fk7bU7r7-+*&ojbnHc5L^pc0E zVw~Q9h&b2(v-^QfH>}W)Qk)LT@A-IJ4?ovMYn7#q~3{NN3LO}-i3HE zk!Hu!N93yIyF?4uQ5*m>{~1km+M8y!Ph7NiS!AyyPuo-`Ph_rsJ12&q5Fp#WpRK-4 zKmbBzO2551KZuF+GiK;>F_8Q|9~`can&gq@>S0x64c?eZs)p2pHK%9owgBep8;5zXtcRdY67{>T?{cBc>*2hz|%I)-T+&7PqFRn@9O#wsERubpO* z*2f;N{#*KFMW9Jih@NADj}sW*&5*|(J3{N;O#CD;RM|o zn~}4w&+Jq++ybceo^0pmFB}@pKxt!)l2tM%y_uA86G;Z2vbzX)_+xN~7|?nYB+~(Z zuQ3W8dMo=f|4ULI#K0S16ZqSuAyNfS#}JWFe_xO;{oUORV@Ip_KS^JZFywUQQwPOE zEQlycG)j(TVxwlHLw>R!4f)-uQt@h+WN55He;#`D@P6~K;p9Ipeio>yVeoqMKK91w z|NG8NZym0}5K^w89JItgqRqIE0%hBu}g>LsgxTk%eb^_{S<8y%oFfCDcoq4JJSNT5eJ`UrzZY|eb_&T?eJ;Wg%SfMp@yTje3 zt^7{a&d6apWm&KI#(971a9{HglLo*K;Egn|Hzs1JbNFNdk)06IHu?s}a`Pb^KoHGNe=Kmnj(ywNt0oVDzzlpYwdbWY;z^VhAWg}Vkn%fGQG zs$`qiST7aYGZ+{HmbUE1MnRdcTgcCQm6%fzD;qV}CqRW$z+O1>j=y9OVg}^MtRO#U z!f+nFLJwKuQRd~YEUpz1Qun- z%yYK?wweSy2vMGikb7nAg}#KX?t1=H=#lwh@&w$Y%f8jlooJg@(GdYzH6$jV&cJsP zy=iRsf&Xzf57<{^Xem$1E6Conn;sY9P1o(tR4oy~U?T@Zj!vo9&o?{Y=@<@0c-g6^ zEIVuK&cVSAA2?3|IFje#c(hpM>Rg+ z7N1MeGvTA%Emmo$1qsOP0VQtmGWOGl$}^5DRK>krG+!;Rym>=}f;nf<@h zM3EtuaJZv~*(-2nKPxIH@!oJ&f42UwW8c5L~9ohBZ&P zT|j%iE`nm7K&)vAO<~*cpjX8(!gWE_s5vG^xzNNQyAwhF045J7MR&ZT6RW2jGE8Qc z$1I|kX%bN<%i^qPQn@1%TJDCmM2C|xDS&UP4>iqx1APxI@v8C3pok!~5yd96--8T~ zQe64Ni3JhTI7M0rA1my=U}z);C#!WJd-%&++Ky+LNs57zD^)bsJ&v124~>BmQnvx79Qg-o5TtD%Sd%h!x{ z_+6|;bB#e(adS!!l}Wn!NanN+M!IIS8t#@-y3dw?UaxM4;sHLOl7j6ZtOrRHhXJXH zf3{QWiuvuR+wekH$qEH%Rw%F&$J2(ixnoh-QAC_+4=IKTC9TaHzp!RXJMzG5?*xgj zerOs(CgF(45RI$UF{76(fop>!`Yhluubx0x#;F~d96%hm_J`5-Z^okWK~W#W-P349^#7vE6T zSG-55;q%|N;vGA~mO@8tArA9}=LbJ1|M>dipiobJSK8k5#OZV(cgeNfUzAR<1ms@X z^|fMZRhzc3je_wey$@xviYsj#5+Js-)K2|S1A8Jup7^iPK&)~*XO5tv%;qj3uA_W3+ zIV&;1wrHqUX?v5;wL3?7a6|9w`pR)tna3yz)fiXHfMn<1@zY#Ms zkAq5T*o@ad-+?a1eK=1T-A>+coCx((06GH^P4>bfP?!s0ac8HBW=%Gg0L(1Xw}auTU(Whf~ET4-LlW zD|MKIhwXvk;%~r6txM-x zI1$ZMy8S&)VL&cJS*1>`H;6N3^~&>2CO=>+(T;nzI#4`xq$`V+!I@l|+k8;KnH4lL zLHv^>iG17hojZ`17~zVdUGmt-==qTNSf5ig?Kk$xD$j-F_#4P$;+;7zZ1YT&*R+bx zlD-WDN1*Xn=ba^uLpZbvcChjg0fZ1XS*3%2^wK_DWw$5@goJyjF(ijrzN6GhwKU^< zsgPY=@NM|@07pr!ErLe9I#eS`L}MXM$`93$LFl=P`jrJxb8gl%dUt?z3Ru5|5|}x6Cc*)-T)xDsm+4Vd#?swAiQ~!Bnk%Kf)_s>b@CF+5k;zT7A&yqKJpd1 zvmW=HYC0}^CkAd~7D4_{1GEA`HU}okfo@N;ICuGuHFWd*>nZh=NRI{7m9-h+)*I4? znj0)1HeMnM!49)d$Pf$HQQIqJ{QiZH4=;6jg*ETHa$w$-AP0pz15m?qPC@CGzmWCI zd|a;&z@FJAy@U^Cx$Ug0IQPub-s0P=HPgGd!BbcUAUR5Bb}X~MQE>x4<=syPJ>gX@ z#|(hK&%hw_*tV^=1v7}|bt)b(r`_+d3N|G8tvGh2-i@7S$Pp>Pn=RY#-@4q(Z)_cM zoDEZHu`tO*F=Cs_*pxJ+S?73 z!$9QfkDa?mh-gVfjb>!K9=O=pK}Nfxsk=!DlA))9F1!f}s350OfkHU@t;XSIE#Hn3 z4JhBj$QcHPZ7e%k=qxO-n*v%-2YI4Rl>*fBilZEG`=Fx7fLT%UzCO7-tT84qid}^p z>EgDnI*RGmsjO@fGFEbOE4o;EeaR&D=AZSexx5Nw5-+ZPz~xUp4l1Zu2?LSnCkO?B zb`HZwc&A_CdyH6f>B;Ctq$)NEBxudg2ND~I4t*ouw2Zx?rcUeZzkML)VaM%(1mFlB z=fi^?TJr#hAqR}y8|#bVrUg+})p7#2`4`CPcd7nUvL@qJ+xTBW=0r$03f6)PaC=O= zHQpVY60GDsX79VTa4Y>M_O1w~aFBi$ODiOo%Pnd4G3=3wPS}nduQ^9~ty6mw{olSH z%fB=xmclBIDKrfsFZ!%XuCWIH<`U}PV@^Jp-sv^%{l~eqWYA*5QLy;EeuMcxhJEQS zvSv1??OIz}5&kfb)bN)9p81F_>tFx$gKtw)bB6o=hQcSN6(Op?=#)Sf&B1<9-yVH+ z?$K<>%NH$?&wmzNAs{&ENS|;)gT_9rFYLV;HdQcN_*tBZ3_glXx|g<7svxp54{YNn z1@~|dO)U9=QK>Z9k;)9(!6L8t{57C}28C|6bpNR<2s2Kgq0XB70POA6;%IW+kVxbQ zs}2J{(&sI1t&$-WMFGCuTId}}2DKY`Cc_eZ-Xr74=;TPsW^P%MBZb*GAJ@}}{gRQ~ z`idg>b3XLf;Sx6G*MOqsVk}^ecrir8=-N9PHc7%Kvs?SDr?=wbI!BcA<|$fQncw?; zlkB&EJ8y^o0&YLs&Z4$BW?WYL^G<4DmgEHzMoJ3_g_xE4IF4`6d?_Pr-OO)9?nx30 zATs;~`yNI-$n^j?=NG0Qd>?E2M{Z+ za$#9EQfW|7X@l1(iG7?3%yCk!Hl;yk66#V7RA?8h=sPf9S5P-YkqEuh3NMSLyN$Wp zJti0?joS>l!2BHrYHTMdkBZXxR!jcb*?=zt;zCi<6)(+E|BEnCGPb~pA&gG2=+uCS z&`aEF)Hba9q?lE$k>$7CC|b+4(1$e33Ig5%wNZKo*-q78XFPKv?f(bl3KX+)`% zppUJK$qe1ohgoR=0> zY1bKSO3X$RZ`))FZ4i}K7w%(9!65rqAnjdw#b-+-!_rRGUHv$q0kAxJbaDH+ll860 zbpFr~i)v|uUyA4~XtB@fC$B8Wp**`}ZpscDP8Um2ieeR*w7lhh)LJT1SZ(sbDJXEB z{S^6yLu!uMC0Bv=N{&4|M2NU`2FZXr90kA9pOlysG;xbh+u27%{B(-lUbe;hhdZ zvzv_V;Q*nFG_`N&PG$E%Ohmdjd$%5RVY$ouHFL4Uaq`#v6G^9lceaxsHez&LZ*(R# zH!%bOLj^h3QD?mRh^r=lZWRh=|00brY+v%xP^yAKj)Md$7fD)mYkHe7nQruv7TygS z1%1l*0^H8IH_kn68j2X1NCVkMC^btZHkeViZwJmRwXp@!DojZ8E@ zVWf@0B`2u#ShhqybG7k&EoYw0g{0p@l!7*z`U){fS+K6aXOGKC_nNRQN%j?eIoR|DRx#JZhPso?49ap)jmY|-jB zP_567##?-*z3t8gd!THf0-0)1+-|#kdR0buobvAi9N(Nu^M9iT#X>-sRtr%v2JHi1 zT;kBxwR6X%0a!U#h~eJvEf#(kg-sH@G-BP3(Q1e?KYmWYpYGU`tNE{INk@I=%5zZz z8saUCyKVQhDdzts1rkbV&ih)a0MNkhAx{=OdzqiJf|&;5eB2J46*C^Q{+!|wor(T? zsN5w5Lnv32r2J|(roEENI`Iz)$JMjjxIWpWvZ!x;YR-qaBG(cH4Lo8h=v&`oEyVp@ z_UrLF@?6*@n8taSTH)v2Fmb*5wyi`buH##^2-GiN8slQAy)AqBJm2$?OwLRmNr&E z7(pW#o6D?oIsIe?U_$YjAmFQt%CNdyErZeJ?O%aX=>&{eg|j68n8~wgu`P5JU#Tz| zgb`Obu^gczZ5Dji0fOXeT}05`gq9b(7#0UtM}OMF9bnf*rynOSFL9Vsom79QEAg@@ zqyt|^^i8hf6XODHRBuL%Wg>p~GFff%{wkr6jcVZqbvNn{9Pk^CDe<&QbnCl)*4X~d zQ?IYlx+6Qp^HOCW3Vf}&`l>{xaL=mrRQxo>Y4J_~w~~UvNg87Y8XVUP!gs9vfk5h3 zTJcZ(v$lwY!XyzD2@x~~&r=+mUuC1@6rEQ*1{W)W2O8=KNL>L@krQ*tG%3GDO`8W? ztRvOTPH`a4fiC)^t#OEtvH*($Zm|&G&Y@Xm(e3QZ>Fc)*^Lkdl#L~GsKrJIfe$J&E)(6S}Z-aa_U%zh|Eb!J# z%wiKG(4cn|)$QN98tfU0#hcs}Fi+RR|JqoxLmew@zX}4$-nGM|pVLP=`BjVm%iJ)Q zCwEhnB>h?%LEg?ZTS1wDFmX(W5L(`zDA2gE94;G*a9)V|u$yoB{M!=r?K=S%vG;!p zWZabQuKfH*Y-hU#Cf;5OEXkr|H7Sd#{E(Hc2Ko=dn2O_>+_D%XY~h2g-LZxfU~Q7C zw#9-t*JSKOmX13OMH2I_JvSpJfpm;0W0g7v$~lJwVI8bc3CLc5OImG|F1?+!E;*C8R$Mq_TVir*R)^DG)^N?POb4-ok1OXRm<~PDqzOX z#*bN4gS!1yqE>;>1iv*?Bwoi%IS{~Vojo~ol$(V@&FcRe1Zg^=uKx8(AB3?@;i3|cI3DcuFJ3-o$|ANRely}4UXC_WN5b7F7t+4P*Mf|;Hq?# zqe6Ioifr~HTHqrG;&)r9uS({vna~0f5{`-2>||yDQ_Jc08tskJIeM2#e-F0ZMn4Sa zQLheRmW@}gdC)hcVX)gboaD@8Fs0oqLkvd~sU=F%H4vdSxuO@#f_b+{jR5D{S;nr@ zfv@u|Uvlnu77@DHz>~Z;GLOa5IYL$;fq|v0es|BELxUsE+>}&Y(yAluoy!!7k=kw4<|F51b?3 zG%YpWH|=g|oAmM#!vjMzj~oV<0Bp59iCxu(t4Kh7J4PZ9M1JPsAKz#$?MBmzcWtGw zxAqtxN`$QnPdQdXGZx|PQ7+g4hE%S7V(q58%S?Ka>;51Dv5}sNuo;p+RrTVzlLJ;W zq&}Z@?Lf@pZ@Qo%4QZFU5X_AhPr`%&-x}t&9FWb21Z+)tJt#{Pv9y{x1^K%IM?VA^ z*R2mxGPe8nMP#W;jdZ?mmNa>|FUsQn|7&6~+KSY`xi}B7BhzP(wRJpZ+qhB$Ku1%4 zEV`j7D6#jdf^UmE0~xggv61M8G>q=D0^GK&6!NR!izRxtS@C_ecrRURLDix{In3Mi z{E#90KvVolW>k^fFWclav}hH1kduStq}v@%L>9RMEwZ+GZNq41jC)-tbqf0h*OEKi zuNwxC8$o`Wu0@Of?k7bD^E0i#*tP>Q2n-z|b>KL4(!@BhEMEL*bn^x6)gS=PHxOQS zPiiA$1*JoLXkHWG<9M1}&gT;sDCBpzeqNou)WkZ_aqfx4*8B+Sw{GSVCA#>yg<4l- zzYGs0Sq-%jU{q1HlYFUbsIOMMkvqU8uq$SUsDt7I%$-M6O%XlHoewUvR*=~&nRBub zew-l(rvOo!@6_k@uGW(WX873JPf|{Lp5{6`%LgD7IG#KKs4_(*v0PNKysWz2Ck}xx z424Mimjcw_=wF?|iLbYta^zr($8QVAKcAp=pqRx#sB9knGzf`jeVat|7UE$O1TxAv z*1fAi>Wv!EYk}}shs(VCl9_JR`>+c^KEo2O5db5mozkFc|0OH^A2mkp)U{W0&peu} zXA{^o)E!HuOAXLJrxL<0W>B5hDO(IVgI}MBJN`A-*YL?lT0b+w}0BTfZ$6nTz@;8s7QPvxGhqRzU-rOVahXENTSP)rn zdD|!?KlqVit@8fY7U`rtzEcG zU}+$qe!WOqG;w^in(G2j=P7GDmYg0+s~gAARu;x5dN+m!outT_$RF2Ea+ic*P#f?5 zsJADR8;hN1W)2r+T>JXvzl%!_NZBTh^1Pc+L$JmeVnxNW7eqx;k=xU|<2I;uR^e2= zoK@@4m~;M?$|c~b9Q@+|ISj9KS)PLd>xw!(w5w+n?AJWLE$nLkkqX<7(SuivZM3nQ z3ZQp0#1Sftk1=_e#Y?K-YzoUI6v$a`hm)VkyR|~wSif+%oJr{;w%YBVlSGya^~Y4P zbgjlanKSAST(`bZEG&B(QIlMT5k8Xrs2O|ympEg(f0JL|BVqSnfw+|xF|kxQEGBFUTK0Rd2Z^Y(3TQ_#^CQ&XN7t zg!ozSlDtYE-Z=By@{IKmRZJO#aRwXL(Y_~Cl0)Z z0=@{?gM2?<>AQs9M)k7F_Cb!Nr7yiGl za{||-#7@Am#SLN0Swqf;BLjZSSWCRgAAKNp#+FJ#nw6ooN5C!z2E;-p$2Qsv4*mO33?s)xJq^;Z0s3E z>}x;}y%zr}LfW#@>J5}nxxT2q(>Uwlu2VE@tfgr*LtC&KT%t>KwUQhU-JN1l;s!zg zNvtq*oF1e+1p1P&&uZlKQ&246?w2a;q%RM(hM-;N(N%$l)}x7P@h_|%Mp0o-1f&Ce z!TQd{7LZ6)v3^dd^irg2035_8{NxTBch&cjUj!ljMrzB-+gn}dZO#H zC|!WY&^chji`)*?r{;tPxGZghhZ_3-@YmW{kG?|azI~WyKH{4mOPEAP7~?pAAtfH% zH=CRGuXFAVY#@4xzrGN4=#D8ikwTSKjah%Lv#9KY46lDOm~QkI6M^2Lr`9=L9E&0S z9sD{s8(vVB>u8P5hmnok`YcuntS)CHOzN`3=bI!9P%xOGaUTZJ$OBfBxJ^$-q{QF@ zKRFr8b1CQXBTawTG5m|Xf-Ss`9LVmfzo=ZtE!!-KZx8D&F6kVk!KenddGG15LGNP? z6$mRhh|)2+#Qvj?yWYfj@w3IK|unCS!cQwPGO>xrfWa>5`1oCmQ}VX;8QO z#2*||SgMq-QUDuPTv6EvtmlEQW+WmvvxkZ;4JFliQsd1Dt6MvhbZM^1J92>}DtrZ} zhc60*O`f{5sH(NB--gPkY%a4bfrdZ0MksGYwV)58r zT&4Rn^RQ86JpK8mQlkTsbJ59#uc_JwgHOQOMD=!L?&BVF*QIrs$wLpV=;Vn&(ZbG{ z^xBm)U_6M~AHd(0T~dLgkaT?4q1D4lpCai&y=gb|H-b9i9xYJ~Z+4l;!g#x74H>&J z-y5rA%bQWk)!jZvKRx8~tF^WckQ`f{b!d7ILif`)zF4L7AwK?=N+_HJq=%9ImmO?lgJYSUFRe5+Z` z;Btw>Zh=zy$oVxHsUFTguTWeluGJoGHF*Rad4MeASKf-!x&k)f=4$u7R~973H?U9% z$N%**+ctd4mK}d}1@B>U!RHmh=EHilDnnT>{D(6o3v%fCvAk8MB+9{vPygD(<<>wo zi!unDZmD4`pa6Q6RG>=@TJmupZ1UlZRXkv`9S% zRC{N^U*lyP9d{i`;bjFkbGZwy1Nl_VmaA(qA)-5VHy0^fLii6Q8YLYya-w&I0r|BK zCI88Yb>qz+Wb_bN<1IJ+(e$a>z{jXwEkNQ?`g{FNHki91{cqs4t}&E0ic(ew9{#2A z0VqKYLodNXS~R>0SitlA+MPi(@cg;Kh&2Cj06VO+3_}b^(BeEFrlG{A{%d5v=o!I- zg`c7s6uVP)V<|Qxy~&9KJIBZ(;>YrUQ_eDb($Ba;fqCdX@g@?9AGzX>rU0G2&TAT6 zR~>dG^=Z=6?!dPSy=A0whbE?Q(G|)8k&vhShnpaFZ@#xf{SJ|>D@{c4x?{I&lnstt5Gqj71BluyShxN zO1qS+Q181}KtIELeMs1<59Q9tmuXG6}d2A`GTwlp!HZRj-H;e0@_d zhOh^O#{}h=W}*b@G3Q!Etw0r4gI9pv4~$EZfcnz>qTu~{svvRaBicW*@FjJXW3}BwAmOq@vCE`Yjwq5 zEakl1{e!>?s>vWwhM}!O7+*tq1@kiEnT6{*S(DK*_2awNuRm@_l)gLLX$~%Zpmp~> zoV*bhiW55BkOq0)PNBXG(#qgyj{e=s4bvj8l&dVo%o}=PFaIF=m?baZ9;NR<1 zMH573u%s85m$5?vEZc+sxLOziV+4t-;)O>;4_9lvGmW;({!Q5CQY}>imimO!_P6l9 z{#$By^^HEbGP{zRD0y0!HG|6&($!i3MB)zeqTyG!RnlKlkl~w2lv0Uo-uq_Qs>xu? zI>0bVVygPerF}WIm~wI97Dh)l&xHdTiX=&dVGMA6^#yrpp0-ApSmKZrrN!FeL7rrQ zs>0@*s}k_zByGO`e|FZO(SF9axWj2kH#ttHQGTIQ^pI630$YyxLMaS?S=SjC zBc7gbnkCv9*G2fvwtJ*tMfc#;f`Sq5L|7Q?ee0&t?8&1#%_}bd5`iyNbkD%;A(}oG z1Vnzs30%ek6I%r@ryu0!KuGwk^p2}?_q~IrJNZtbdcB8C1WeZ#&F$zI?fjNmTplE+ z0O)5hOwXflJ-@ZeT(z?Pz&gIA5#?qwua4l`#q@AqfjkS^1`v27eOwYS}o-_6k2phlodi&smY_`TnMdlsGPBsy94U}%eOIT zXkue)&kF)@k(h1d3KAD%zOagzd;S7R9lTsKXja*4WCIU_)p7GlH$e1eHDEoDpIDTs z6`fALlptCj8KkC*a&uUd5gs65RxdP`zQ_2e37i)pyFoHs5_uYhaji zzDSS8G<%-}pKn(B)TtbtqV@VQ7{oQZ5j6&Ul?Nt+`A2Inkeg!#WeI7QSY#aWWwd$q z;$uJ9?j*X-femZ9c{Jp5lxGFOheWzu8qBjkwwciZLGophX7>>VsBmrXhg zm05R}JC>uXW70>w2vYrOe1+Y$Q6c0GIKH%U0a(&aBFap=kqSwYD4B$jQEl<1tb_4` zTl1LzO>jE$9jO)-`cA!7*6JeI0QGmrwV<&5Sjq$6Ol ztPYa!TSz8Gr(lQ#yHm+C2*1twA z>JQ$-m!KGlNe5XEYP^4gU5$@xT<92cZo*4TW28mR+ihj~9S-fMVi=>3+|N1ug@vBgyyAd>FmlZ*h%eLAT0+^QC#Pe-4k!J5ZSJx(a!}b4h8=mLp z0-cG?4}fv<@uIk~{#C3dC?`(*!b36i$r6!rd1p?2*w|+!(8rz>N@x77>7n_Td@jM~2q6 z;FGglMp}-ZvU5dnqg>#hsDw+7MjRGiZMNAzNJu$Ky^WYP+FJxlB!_g!SDnbblzuuz z!w`gM$7RZ%4xETYF&RXNLKi4N#-d$>xf*q;nt=p`&&+DypshUu#VmqE-0{kF$y_9% zMgf=VE8qr$@S^*H=sSI2f8Kf+{QZeqKMo*#A(R z{d+Oknq{BEm5_~aEca1JU?Oo>!BazywNR;GGG7DI!Gk&@+{40+>yDAGkgQ?8caXm7 z;-TT1QUu;hU0IhK6(yFY3CfFn=8z?VYNfm<$gBqSmGaT_bZ%CnPSCB!`)egf2uNEI zyXm%Q{|m^^|@(BoRCMHXxrNTYX5?8Qy8CdhpPX!vTeg0_w8H^yh%bg zQq5rIr%Ka2`i6GY;i43^-jQ|k+n_Z&_+89?zN@df8XI8#Ji?;Wi>d7jz4OuYpgwV; zOT*7;+nu`SWp%CQAaV_@!%`f5vIgMa-g&-BvdBqvCRqd`RrNtXW}ex$s(zYJUMJ?# z`Beb!y)0a8b^!`9fA}f?Nx9Wz2Mi|Cbx}VkB{4ne(*pMmcVPYh487;?Uesc?Os+cJ zD(b|$2OFrc{i-@1m0ZipS8IVMb^2-j0Y=8pGJbsxkDA5}Ni8G5MV5xUsvrrwt%~Hw zZLR8z>$}H2HOjL`eV$dw{)-1xuki8wD8ZDKg!I9tc!QOBY1bG8oZX?$E zV#BA5$mG9PxYy9g<3i>%(}nAK7&>L}9_dTg-8hRVWS!#hCJR>6AMB6l4Ns@%=2uUE1 zsQI&M9&xIA@Us!`Rc)ZxMgPF!m)V3#b2dVE$`}qmr5Vf>5Ed$b&;Iee$ynnzaYsZ# za7$Ftfe9P#@;Ixcab6k6G5b7R$UX;J_v>L@zEhyljWn6FFbp1#Z5OS#F?U$`bcVe# zBeiKrRM_GFCznVdK)1#~30TpNM2@^M)Fu&Gf|q^1QZ^m%p|DpUkr}Qxb0#0Kx(*i= z4oNa#Ep%jMH}F!NlgXI}@DY8to%bhWQ*V9})`h_h($I2qAw`IVc@5*PsW9E=H0|h$=^ptqO1N)eAt@+AZG`10b_VEIggU1K&s;HF3S7ciyYO`YX+&sX~a_ zhk@r$QQh+qaSiw{dT^7)p|`(O&5R)-70OOLjJ`Hd#m7}6#9z~X`sO{7(Vm2ZV+b7n*`7F{QU_?8LFxq$_LIB01 zRlivWx{r-1S6ZS0We6ULvpcG?ccR`Zu?smswfrJn@Z6OgC@!3oO`jxhIve77^7|)G zp%I!T5?b96J%ScNF$<*}cmEvqR$q6GA!I=5se?`=OK>Wu;78I*tF;va?eL@0PA= zlg&JEc2+Lb?}sQO)ya7&m+4;?&W12+*i|x}fvka{^tk5ZRJH^E1Sa1h>qmtojrK0c z;)%VL1L6SwB!oS0?Np`EGsKct{c(4n-Z@KPIC{f;bJB@kpOYd0R&cbv|G@&)T!~yn zUBT+vfXBriS}B@h0NSwhm*>m)*H=^&v=A@NMK-S4xPx5(B|cD#Zn{1}ZId?sFBz@# zu59ejP>}L_CH<#*Fgt&`(K^mx1Y`|Xhsc>sXCezvcf)(ZzUhJ|eC^v+QZhI)#n@#f zyWMC?2@RFB5t{y+=oQFjJIJgx_4lZSm6uUJ2c@eC?uPC{4~3|A8nNO=%jEaeb?`rr za7nTE<<7pJSe}n@K<4L~42|}s%>??U^I0ZS1~r*}ZEa^y z1bh`W{0RjX&(d53I3G+@A60B|0_y>%X&n$km!w?|zv z1>lnG%|dM|`_+LoMbd^BYH6Pbgq~GdjkuiaY?t}qf{_ld&JPYPbbn^v5$mh<(@l$p zK!-RGb%_NE%15_DeD$vWj2mhZ%gOKCWBz)qu=I1`Xbwr?#kk?xFe}yW>FD(ItAgd~ z%%X?UP2T2UR3k66(bbZAnDi25S(L#2r9oldJ7BMYXZ99oqF@pVeAl68RQZhuwg$fR ztu?V~jxd5n$}7Q3EO?o?u?Vz+sAVk@Wo9d^@Ayo690x!kUpi~HKy=r1E`_H&d9-ke zc-j`}2v*%*!nFV`K+?a9FMA*bx|Q4_##Wi9zBL~*2cQ0z&PaB(^>&A3_17I!i$ttz z=mq>IJ^X>}D#8$Zv00#<>POkGG{NOY6Ga|J$Gw~0+3X~udhL6W7Xj3>4U?}Yt?5L? zeZrF7NWjG5gDH1aIG6BCQd`6J3J;ZD`h=)fbFl5IgCLV+uObvtS1n z`dn9Uz@r||V)NYKUM{lQi1M);?uhF8a57na@Rk)F#hp3MY z>f?g#%o(+R1@IXPOV4Jhe20jsCCuCVpA6p?qZ z-%ms27JRT`Hf8iQo)AF=ZSs(Yd;KV{`R9#Jf4jS=!>t5|f z>NIf)0T2}_7STUU;|K(w{jZ_iv`B9N1_2|;ggROswYD*(xfiE^4q>1D-L*ra&ERYG zYY{!_`SJ}x#o`ENTRlN_t55JJXrsi#^Us{4jGUeqlBgRZ&?Oi7J{VVYzae@hTP+7UV;?H_HB-XKw zU7A4w#vz)AS{9q|5dqr$H&m*Nz$x!s-*4fr)Q%Ni3LZV*yF$uJ#R=AxITc@o{>y-DmR(RqNw$v?|=GhKW8nmBs zWZytN2~x^DM~s%~<@jl0KH)Zi}@2rFk2nMrfB z8d7AG%XkkgiU(pJ?Js0i51_`L0Af5~jr$cy0RD{|mi#Po?)~d9Ki=Gc7yeCa*Q7Yj zx}@P%v7;2kZl_B|i$!EH`+|t@bngAN1%NnpsZdm|0RvLWps+Yaxc3qy^|AsRY^?~g zM=$uk3?J@KzsMm~^VPYbbQU#}=};^zKJdTh>Sy?A1RaSM3B}GwXuWtz+Nit(CFSxs z+;Ld3R+QW&@LyuHyd@EFL;?mu89 zARrAo;)kj+0T3Sj-G{PC_PI8M`ZlwdHL!a(F0Q{+EhG_3@fD>~sRKD(fLOe7?3#!r zPFtIvBxdpNe~;+<9WL!E7M??y%!2LFvod}KA~;b%X~FvO@Jwov`SXqnv0#qWAFkBC zb;1)0PFZKd&5G8-c*e*|`36VgZx%7W=kA$~ZlpVx1Yl3E$q;rwLpQ0J37xKIfDBY` z(4ppC%H$<&tig^OG>4+{z38N|9)Zl&*an;45im|3w@-6Bi;Sefwvj67I1)_Wy0d4j z+ng@@&pefwXeXtD<~soY3>3W7r5StLa5LLp%nxN@byl}Bp>pN}eBDr#blkENfJ?pY zNaYh*rj@9k@x9&3@tAD>eNf8Qmsjtfqo?Pw1s^H?r&xDCMRqr1zW6OHtRIEeOsIMHU7 zgG-Akz4zVRddpbJM#9kFz4&o6$E?8^u~ilyOHmN0eB+X%_yZ9FL`i1;jtE*Zv+t&P zl#79%Xo2gbA6(Su=&XZpCASnU_`RiImGti23@lKL7J2u zJNu<3W0Q6`b60BfW7x}b0-1$bAS`IC!#4Fz5Erg&IR)}c$MNo3XWtA$kD!8Wb@+&OqJb7>KK%^E$zF0!dW1KB;5^FMePy5ee}t*ufm4$|V(%I#(SNq$(||uQU0jjgfU*}; zD`vmsssdlN-q9=>@@eEbjvOH~=r-F0B<}T6xNSGsrn-R^h7e9ub!403(&wlW-^^EW$M{&YZF(b;+JUKPxI~6&1ZU>Gf^4HH^ z?Jf_+aKdmfT(8@hxu(4>TRtd@zYpdW#aKF8*y>f8pgTw}iGzuJWPPbdGms`$P|@Ds+9HmAd@N7~_(K2rhvsq1Yvy=;yboU?{S2+-EB zOtFxX<4vpobAj75z`OQro<}9fR0gpzcY?Dna{kP` ze3~0F$5&epR4S^Pe_(jhMxLx4DuJchm|8^8w*R zt3a!&1VmSlXgCp35nGSN(qgoVk115o)?zNbX88-b!_$|Cll9pVV=P-Hqmv$pWV|sN z@ZhO}-^x8N?FYmiHy0D@(V~~oS58QUR#waeVK$Y1egnwB;fXsH+s1k&@JmCvB@dvVrJQ7uWF)u67Kuj3_B zDm|bGi1zS}x#A40jv#+G{oz{%F*hTG0LNnXUFyl+R2)8M>z-4>+c#2*?lhl6d&^VU z(hCCBgPOZTJstm*dtPPrB&LU|ne!9X{;YBXw>H}~pX>-8x<9Gp^+mZKjZA1hy&j((f=PbO@hW}< zZ?FZzHC7R`27JkT;vp6#RQ%9?~YXTg4zX29Srk-QVhQ+6l<=UUd z?*Hs=q1rK0Tcf9=^@iY=xT>G{W|NC77f`#VF0rK0#VSx^~4EkFYoGDJ*m@2)IN;oeg?HVbVBxh4mIKjDtw8y{izoXL1N?O zxcX1Dxe z`+gI=koNNj*^{JK@BWvfh~{I3WVT5$zRZ8i&T&k^9?5)c>Bl%$Nk-EqALp4g8Vy0&os z(OjPgj*Jp!U7UXNfb|8onlM((&AugxYaNc{=NPB+p?((T$MOrKw zo%b#<7c+vptR-@l9^}M-_;8=FUv%n8oSa zg(R`{R3F?I1Q>V5jvjE`AVdHJ&c$RadG#fa#%eCso-tnH-@*qNk<03l=?`)DJe52N z?hfyuF5)L^q4aC)=?^zdUF@fT%mGhc0c%Rx;_v$gB)j62>>#VLMI>_MuiS`zuxR{< zq30{o?|}9cqF%x7gifyI@xHa@M+^kIm43dauu1q2yPldciO1P-_<4hWGcV2aXFmme z_3+j5l|kd#m@utmoQf#d)w$6n3hi!2W@;?3-#-s-rujNjRGi~#a|{+Q4cmO_j)Nmi zPfo@5U3%E!y~&G>OC2_UPVb1@7xji}A4T#_tdiaa?%Qw}OdPj2E(mCxJc7Do*dr*9 z^#ek|>BxweEvCR_L9q()?-E#W4yCC6O#g>~2-i3e6Fj4SNkob}t>vJbRDY!1an*eL z7^V6MSw6<`^73uz)&LxC2zgr>rUxiWK)A2WNpmr*eQtGqv?M5_s)(JV4f3G=!wuQq zMrm%-Wf0wq<`|AsAdTlNFVfA0Wwh?jAm4n(Nlbl8bYf%eLwI40NE4USi=mNva`_I! z&pF(O8)x&^`cntu4XJu4kcy}l)l<_e5{DVj%UZ8{enN|bMK(#UKTq~Br3t=cp30uF zd>i3YZbYnB#dTb^x1kTg=8C}%v7m#f`+O;YYyq3BmuisTV$)9oNdfROVp_pte@37D zid!B}C`6K)ooP*pNjJXy47nL_1`7+T51tz;j}!-KkJh{ssk|T93e_nET4ceenBwTE zzC^Q-CS6l-F4ozS{WwfOu>O#Qd_&502W@)dNiaD}`MqnCO?~X8*)3U`hz8-!tBzyw z5d@zR`g-N>(3vX87szUXq+xJO)3e}?SABD50=rzPM+DR8=E3JC6yLNXegmfg1q37> z6SbUf`w0EBv zl?VI@u4{NylU2pm+Y?Z0B7N+y07MmQA`U`@Iwz|V?_#9G5Ri&C>hlwdrPCDHq<_>I zf(><>pS`+y_LHDCziLWUp04n|fo(Z3T?PT&)z>Zi21l&kUMHXD!zX2DM$OiSKM{@ zPasde%V#gxoh_F9pF_7}A!>VkK|Fx#oOI_HvHXx6*8DWCKHo99v|BTs0#LfI1m0lp zb2q=H=UR(O|8ZS$)py7P!ZI0x_y&}Rwk=#aKw8ExpJf_q_d@*nu9dy=It%fIzyII^ zUdrka*_|NH6r^_)t#xo%oOO-e{dgU}3K=S$4h<`A2!Kb8JXX;n43#X6WheulZGNhN z8-2uxS;CsP0?jqlm8UDiWU&&ipp<}1jKC!KWa#mONqdJR)SLV|1LdnAxuUU{8a%ON z(P^+vZPX8>-qo_tARF1#^N<4DyB;_kS7BfT(OEavV}`LAm@}Q7zpfP^fDo@EYI@S7 zBp=1iGbSqn<{~xyi2oRC`QX^b%aCGu5>IV?TGBP|(IYwpx1Icv8(OB<^ZIxf`$d-W7&m0zjy!bz^z!3jCKTYX=?8hl-68E=^Sf_i35w$v6w*FI zm2N`|7l$WWx{xGW0C0yG?v;6I7bTbYex?-p( zZp8euqJZ!6UKQ0GD&79gY9cM>!dhZSaWt2l3I#%m`J}aQjUz3S5L;^MB<>xOK(M+l zRe3JpfnPk!j1up>jh=kjZK(2xw(fVqBo!PJViO1bZqBj-GMv&oXi1E7^dL6YIWWe^ zyOljf;Ep1Dde6akik*Gy1b6NBmJE{7KuIDeoy~*biU52Wo`{@Nm5b9~vcV1929Jgl ztj<_z7{*>uG#pck<<)0;%D=6rm-mf1PD?^PA~MZU<4;RG zFM~ldR}iUYq>ybBG&Uo<*3lsEl>7Qx|E)Q`V*+u1Bpu8u`5iiPwX@tEqCunr5nrn z`@)WP8R}qDxGs?b-9EK5`=Nqw{xAebflA_6C-|6Qo6uP~S&h8Yf*Ouc&ay0nASe(Lh>qI+sF*Q;7=S8S?YXhsBuSB( z@tU6GSqv-#N0dcp;PHwTFIytO2}LAu&>&a2CFf?BLWg3C~;b;|hs--6`e z&PF+0RM7DpvK7>yg21oFn)@7pPBvF1-)bDaNL}@0v7$rwZ>&6#CkPODOiE}ujrt{|qpuoGXpgm2v+^spx z4+vKUfVSW`Y(9ngDwS@(e)eS=9T@C*MF2OR%w}<1ioGcxP= z_j&*5@eIrjy3F{W-D6aWAXF)o}+fNSh)a?(+GQm1hNuz_w&?!GT$d`5y;`o%<$ z3iSXfrCo*owp2ST9dT_8yyODn)&qR=^|ioG^RYDn(MGPIgXB{e;|q+@!f%xKZBdGx z>BbkoElV)Yv9$_P zZd4r(02SwwRe;9gEZ0pLt|K+-f%|hFED2CUoQlBAVwXm&5)nLucVH$D_+H@A*~7+J zd_fa^Nh2{appGz9?#AAfW0;)N(Z04GJq6rjrJDJbII(;Je!t`c5iZwXDX%X5gCau!FF(a zH`L%#8P{CZBuLoU8lEH&`2T9<3!U-v;iiyvNu@ffSJ%}6^30fDHtqeT9P$NnKrY+R zqieb-{zU@T(rjrbzva8b)sowj&LWpm%ge5`l0n_X~*O zU1I^^+SY#6U*`tnW>)5<{uZXYMTSWa{rA1{%g}}>T^>_)U4VfDjHu;>Um^ zh0wh_fU)QF#^lJuXzCl{7a>|Aes;U~wVNWylO($|ZgGL~A>LCKO%!QLDaTk?AY!x9?;8b08*s$(b_#k&(iau)c-ff~wE2(X>SF}fbFS!a?zd?-k zN?WN9d5!7@YeIx}9!r(`w?Dy3k!+3X=a?HuJVuH=3S$la1yFm7ehkaH+PMkz7Rf zofz`#VhMa-<}ran*u;@4fqlwV$8EtWcFcTE<_b)DzW&b1AmruRnaTK=H8`LVduey} zWEeT7T!V&URtg1kh!;}cA%r>kH%a9@jyz*)PR3Ia(7FVV@citjSsxhnre>Eg!?ZEl zl04liHy!33iAa9kNU8k&_ecY7r4QeSOS(s3dvpM}rzk+#lpCV+tEvHse7iY>d1a@; z0Sjkj%g;A|Eu1+Kq)_nDn5p$&0PP<)p(?ypD@7ASio=BU9IC5rSlffJxijOUL!svk z-h&FJW31)6tF^icMCQ>+PAHjO!F*47LR&rAr*dULm+9V5FWRmvvrUVMcvK6hBj=MH zD0xXcen+Q{!hmwQq%N^%Dukg=Td&`Pbs%q44dpnY^1=aAcgPIk!XyZF{V*0VCa8+B zbkt7g%RbtUamII0Uf3$d5Ry@XcD-NxR|JD*hHu|v3KxKrkDmQts;jzUT}SN3BCu||!?lX3FG$G6s~c)} zW^7YlMgs;Fmu$g6bg;+gQ2^YoqhAH7FJsk}glIZr=dv64!f>`t{;uNyOj^)!FV?jt zz^N^@EZrFQO=M?y059o+Dcmg@lH8AV?Z*W{T+ooxJUP23F^n^}kA@ZymUW8Fpx7Qh zZf37oLXZ&Vd!2~{MQCvNdC_e|yRYxQf$%OPp&KbrlFb3Iv*QFxXYEy;i4vy@!@9Z>ark?>3YcV{)zkjM*%$5}f)k_Aq7gye@c=WrG$9;po z1J19<$BoO2o7ExAKP^#l#53`5njBJ0Cobnnp^~tN(cw{*MwYD{z!*KwY+ck_)6VaPJsjPkZ>v^l?(fnP85&S`h=i|++D#e}~K6(JCNc6jx8?<}qLQ0?v6ZM32^cSvyMq9F%xxU!uFN&B5 zC%Y(3a~66D+=1&wnr(R|a5_FXaDc+zf7c90njkE6>xM)70aUEqWXF`X%ZuH-fIR4F z4q(Zt6&F&r2{ISN$TGmbCMqi)U`(<3t+XXSoA?s*V~x?M_i4CFtr<9*Xpr|>`v3SW zhjCA%l(u4N0N*IdlJZ7>6yWwZE#;9mSu;YaITT)gdKnl(THYH*A;)E0rGK?q;in_EPM6o|~mSeyd@}m*` zUxPy9SUv-7h9<6_lsiev0&aGUswDWF=e7>&w3sEoUMvOM_k!lXQLju-D37J2ZF(6@ znhxp``0i0Bt1@uN!eJ%9K@Q(DDfff~V}Mmgf4CQNZ#^SPWUNRg6){AlRQwN@0l83y zOy-iq5?KmR(AfhUWU;pW`quIC7J=LP8d(CtBa%%aBsQ%B8LB`X+$7HDA- zNo}uyl;Jc8Sk#(%VNI~^M$KKcH0SBws}QoE)<@+zzlkw3*S5o*egKhLD1B>tL*LIP z2(&pK_IlfnZ1M)T$^ZFXFRdmJo~?2IJlEhKkly_K7i$*1dA&TlbOr#mmFZh(lcy6X z0M2tF0LzP@XA}-A2^6W-DW1|U&oOSjzCQ^dqSoMfttA!ZQUkTC*moE597*2>JF`it zFQ!RttcdS!3K(@)HLO#jRtx9;!jhhcfKm;KrFaqj3I=&fU6&JK2O-mWHh)NZwV zUb-N{St7wc>Oh*X^~Uns7GbbNk$%lVaR`8a8xm6dibERRmuQ@1rZ8x>vAf*zcKcnn zNA*EA-Q3vFx$}^OHF)YnMi5JAa`ShNk^@jeRveZ%JL}9+a}l+KTGMY+s{DQ; zFMz1V*x{I2J<%<~iv|n{K>0y7tG>pnP;#|k{y=?$n!3pYz(wSyWUr(??G2$a30n58 z;v^%n*2Hve(HdtWg1O3(xcA1{c1yY&ebJ7;P){;xkAbi#k3SntQzWQnic`GD=%jf7 z_r95)&kvX&S!oA#2D%~!Jf@if2Cy=5{`ho@3RcG9O&lCR;3(&FOw;e;H;bDpyAcc! z&w#<`cEF{etw`=iER&+Emu>|&RzBj21YAZ3)mggI9>FU9KT~q!)L51c^x{281e&Y8 z^=w;mqoHX>sUZ)}GvaiNdQU3JM6J0{gwFcS%J>53El)i1$PKSD#tN|%{rnM_p|m&q zkM!h)A3j5vlAc9)fEec!RDeA(_TnL=OYhxZ&hvQ5)+|!r5M-z;-M(lk1Oo`An4<9! zy0au4c2BmteoDV{@xCt#Au`Chb2NNjed80Y*L|GElV4Uu^eSz1g0?+^4@Q@e6VybY zUCcxMwW(x1Z)f(}ewCr}oW#nYsB{0>LlCG{HSJxw#S5oQ5M9$%0&UkwY6fqjux#Y~ zzrjUL@fG;xY1$=7qazl1>bU4E@IEp@3TKcOLK0>Kq_5SY3o+lY6cXyYapJ!~s(t{5 zx@%7TdPK&>NS3f8GJK4s%>@ucw6o&C=D4eOqxuigL}PS%kPD;wYari@={vo8bfNJ|qk><=l^$!7 zqsF_o0y-hk#I9uOp!zM@7KWWnk2z5ncaJ@r*Ucdvk;0>;MgY*KoCbCcE~zCt2|vry z)|29_yGX!fQ;%CBk&?i?!lDn4PEe^@5vc zPAJ8d_+Q?ol_-V{t+N8B%s(lk-MZfv&L&m-OA9^~aJ#EY<(WA|RVR*Dmgm{dgBkJ2 z9YeBYU|&=#7+ba!FK~NwnV?{0>9iH$WfJ5}*ku!K3p`@3nnNhUBsDO(#}q)%mA0Px zlWSUK)U+$0u1k6FZK$!Ya6lNH%*kIKDXyuWji93@5t$N58x08c2ab%5QW&mKLezaE zr}PcPlU(<=6z_wlF&_^_xyS3D15FR{-^lMJ``;+Sn?|I^=jYe?Mz$*qzgnovEXFFi z!f#QL`C^PFw#|9kTRoSc_&8*~7S9{5!BKK=e-GrKVaAgVo`8{`RZT^p)-u9Qwyzz= zk`=fob6>bDBq&9zU4$`a@|>+gFU5x>cGky~Z)q38#h68~;Sl%Pj(45)H!MvS746f< zi7UQ=r*Zoq7KAK2H36PxUfg?-Jl+OGlGZ}G?Wo1B*ibQ{GZb18v2ZPs|A`QU=FQz6 zXR3YVs$hdRURYO!l?Ln6LGaJ|G{_V`7T(NdCBguY@596d^A0IExt}b_OC>z@1e1UX zL`BhP!S~lx({X$3>@R!gREvB0yjlfzz==f%0rfxKr6Eq_$RW_}nSwS04Pg^z6ILvX zPrHCINU*0kL%|fwKB>1i)gR>nnu*=opcr{2S z)HB*^#k1j>BGuLv;b?crQi^9qcCFPHGq1(tiT#kM^-Tfzv$1g0Bg09| z{Qsy=f2uwmyPG~#ZxZE&*Vzs93PF$A)Ax!S0kEDdOXuMK zRR--Tc(rKLQsLuly(Xgzg$mhRxmhf{uwx*!+-_Yo?)0{XvWUwCJzclF0exh1M?$a^ z-ydKV!lbwOaIr1!0Y~;%a_HZ_A*`3)E%yjxNJ2sOaqpOD$bYc7Eq?d(@t<$a zA;vGdv)#xGZue;d;X!})-sKyEuxUB?7hjcWJMcdmEZH7ZuA%C1v*SD$o%C-42-*zA zJ0?;)5tzE;w*^un0boL2eXxN(?5a#iI|Bf4J832XpbQdgj>ai9fPeZiYGHJH2^y)v znF-db)Uu$i9rCA51*)h?_saNPH1q00c&>qt%hOg3NfDtZrPqRR(II%7VZn^_>^`oB zxmaqby^+Pg`=byku1VH?9?Fa;=7beiXkNk)a-zcISHN_1ub7+s9vo)~4eD=|_yq(DDwv&m^x;BWjD^e~t z_TG=_uD88~o%_{H8ffHI9TqFntwEFrxWdCj_%d4uMKB40scSXC1PE@k zmV?YokLX@hgSmBhUVuUjZep{(o0K-A{8<2L0-iDO1t~~3ota^NL7v_MV^yo)5n3+n zc4Hq=4d%`ks6NG~Rf(yaJRCq2fApyN9WGeWc1qu-yqH{}M@OZ+RSRzsi7Z(*?Y`;5 z9E<~`tTx*y{rWk()|devrfM}%mNo^xn-IpZHHd7S3_}_wt>8=#yYd;01>S<(IRFrT zAuOv26*>?$M|9+2pwn(!qM)-MXWzU&BQs1nw{VYht+?J5BB8+`8c)Wb15!gr7xx{M zneV?%=0z>^S3W}76RiA(OscimGZJI-%uIhElXJ$?-s%RXDtq0>!jk;v8!gJFgQkj_ zV2k|4Za^clriqcPm2?^4>mW5(N^p__G$wOVk@OBX#thCrd}^~!>%>%iX}YY}M&hXm zV6%exxTjXiz?;6jhPwTJQ&&s@tJ_Gi6!YEItnV%=3v0}mk7j?|{bopWO{&G1?6eWK z2T+|fXukLOQlTigl_7aFV2YWNKkrOCIn8p(ZCIN{3!OQPByXP35cZ2K9`!>y$*H?L z)UK+@k55RGMq5OCIv!&|7;Kw{G2$*e44W#?!a6SdbJZTlyxLAc^k+jY7HA;*2M>s1;Jrh_tggq%uT_Ah4-`pW{XJRGq?D&I09O(H2^_mm)Q8 z!0gWb=>9Yz21tL8BlwzA2#*td0g%c=EtblYZsfcR%N!RSlH(=PdQ?+mmvoE=)KAOX ztO(nL_!pYfD6hcH#GCSwUXL#K+R|JYiz538~S9zvPOUOFH=? zf@^{^elT@NxIk2)wu8n;*JSXA_YP|t=#cQgd-1OjDaXOpW8zBd>s9;n^b2J(PtEl4 zepaI#5%(fWHptc%?)r&^V8;_o=kb6Y_mB!Kt@xC2f0?=Q!j2%U#$|39WuarOBZ|3! zs+Tt!t4k)kpoVBN8o1)cR3!)fb`hyA-Y{JM1{14UPcnVGb-%Szo_(<=o`(eMDRq@% zXC(ruzC!?~d6@D81~ec-Q6N*peN3M2?4S1av%PSmu_iZ3{jvS8b>JH{*f+mPWz1ph z!&_Q|gxO-YK+<~mVSD$mbBglx9X1Xd&Sgw?Z_wVwNKAw5`X5?I9fYc7aKh$RzBLLG zLHIl3CwIsRY&S9leH*3{9TVyhR1$jX6>?Qfhf=u+OM>^A32{4l4!ZynHNo4WB$O`& zG>H46G!s#HJB434uQpnt4xVyr%2&A{1gC+mx};>lwahY&#G>WM>Go7;?`?_&1X^og zX1&t45k7UcP1K}U9PheZXL!`fPOZ}<5$fVE~q=x>n0a#Nl;|)9@wAyA;e{GN!%#vv*Op>-Kn3*b0GL}p{AEZbDZxP;=is1hkpa%4PJs9Wh zVMyVpsN;3W`<@WGJPv)6a&zp)i_9$VN`^%#P1?owV(oRct50hr(O_=?NH84GxH`+4 zL0`5$(yYaVsS9wg0ryZpoQbl2hy^|BKBptpHZrx@ysRS>O9C5!A<5|?{Tl!U^PYVX zfi>Jrsw^cB&GK3H>@sV#Mf6G@a0GTr${|SMCXlV6^5$`R`_a7$d!Ap+094xE)AOzB znU2=)x;Lh%F<+7v)*8EZSIJ0or?m?m7AQu`_hpvl{@WPk&HQX$ZJA!`+3^Vxh|5y{ z2VQGO^P_2a*aIC42MO_EZFBHD`a{7I73D$RV~NgSsH0J>XOd)e`gS$iTp&_px59NR zC@?{8Kb#N0OjO369`3n7c|y47b?OH)&mUqmt)#Z~1pQ(uu%3XfLr-uND8AZA4zk<( zPVT$MMjk+M&vOp#jW8@`i;C1;@Jb4Gg{D>UG++x}2`dp|ZJIDbv56u(oskep`GoWf zt#U1A_^_K;H`?dhWUR2)Y=~wa{NsEf17aaAP>xFYCgHPiYprj%%UFR1s2u2u9W_O^ z-h08~=vOCI4}t;QPVg@oHI)TuF_?Pppm0P6Vt?0D#dw+hDj21tIlmvG-2`lm&Mn?d zm$gtuIzhnfKFUliVIVt_jrY?ySgmT(eb>!pf&@d;d=Pe3A|t1i4`gQ z#TmX4An8ScrA4#nv`}Llk3Mg6s-L<7LI5GCjWKFSHVT@ zN@M9%#(GudlzOv5lmU!G5{*TCL;p#*wM@-YmG_<-FL-7{>ZxpOEHc-ae*|`cTI1gp zNRu}Q1<;%_7>1f;PMgjTIJjD;TgxRBv=clYLc*D4`9iZX*_vu<9Crj7|F@gO96wvD zg#PLLSz72KBC#ykz-n=+2Wk;lRonqC&X!H+U;Y2h1L^p;}{#+&vtWqkI0o+U;EZ|4fnzXg!i^{ zJ&dZp?#jy|M<_K7$Sae&?{9sfK?-FS{Kx1fUtC%dUO$`|R6XSb@(&U*nyG^=dewXE zV_9X!I%RI~#_}DX%p-hi=e)u@o{gyr+xs6XzGe8x$oE1L<8(XlK8vK8TpmPVI=4ZQfS@fhjkm ziQ?h-A#F_`&R72%#|*?BOEm(1?rP!dRg~2TNisY0!@Nhl-z`>)cLpY5heXZSJJ5D@ zVt~-ws?qR7VqX75HbnfOl5e^?n96nqlZ@Z9!m9VknaKbm*1(3Wa1hpU1Tfs^gyp~O zw7uNLhECz(hSi?AISjEk(Po^-5J8=tg7qNrjQj%j340iFAR>No`NA7nj1fLDfC!Lb zRj6bQL;L2Fhe>>Z8nkTqC=!N1xK#Ya`Eo1;IN?s(CGm&wx91gy*DWktzZeTBmaB$> zcok6>?Tvpi&t2NF`ABr~k=7UVIbv!t=qF(J?Imwe$Rv((A%}a$3D*CiMu9dF+jwnA zEJdhV5*gGOi-6)hxLZ@mC!sOx=-?Reo!uxXMxTJ9G=m~ZxL(c$&XqfQOl}mYD)ehA3%PEJC`HLL zML^!6i^h)zwsyio0NH+O)=1U?**l=@Ca)-Wr!Lex(!~~0&E9x-J$Jf#MPj=q<}eAV*BleSt1)iA8ANG zvN$bSXVBaKmee}OFkH$H;97)WY8Qwlxxkd{e}f1KwPAo(P@sFZatm|?Scqy_$PeoV zmxA&g__)_Er24!^zQ7T=WbRM1$&xXx1EWEFGunNRR1YMjBD3N>pQ&BZc(6cKnR8dL z%i6@yR%3W#BVKN0jn_IM%-{Be%!0fQ)ODc+`O85E(R0TYEbM@NVv%dK4vXZ~S7o^{ zJpItV;#eVevhLp5&rjrS8j;gl`C0DzX~IUq54#)IGy|oqQ_uUZQ81ou()(e)26?Zv z*h+CzykoT8zCvvb z!46MA4{mVBSndk@1ii*O2NIh;Uf&5hqiF@Baipx*zpVR>@VZn95JHfABIvCJK~Xwi zZOPETJ@*@kah5)CxZj$;BD4rMDyFwn3x8vlxIOK-Bbu&svzheP?4i>;`L5QaKwniy zGQ2w#@7}{YFNVmDji8iXTlTFdq6eq8XQOUIHv5I8V5ldkufmO9GE|(T4 z{`?PkT33v_3@w(?8|=awSq}>?qL<^I1H=HK?47my2+ujZ>FYw(bg38VR#9Ws3 zQtL!(q-f1E@hB~r`0s#+LzuLNX!wm)P~t?-xy3;rc4MpLK4`WbD#FMg2rd`>!)iQZ zn)at$+JoH7KKow?N!T#^`?w^c)ROszgCA!wi(y;ClNws91mPE06qm+t>-ki_7qKd7 zM>o-TXgUhj+4Om3SWOM0$_ClvxW~aebMQ&rR`kJ3+P)-{tbI9{eptocCUweKHDDq_ z{wp3hR1W*O^Cke4iC$LfV$R3CYhz6f?-R$dRxbZHKGAZHluNZISMG_yseLK2rsI`8 zdj_42Fa7;+k|3BlDz|#B<1P!25-Xc`#N>%*LcP?jDE{yZJTZ>hUSGa(4t#>K|9 zm=I%XG7R#F%?N)^=5GL`mNq#(>pL+_PTB#PA+hW-Y%&~{(FSQ$wJDP7mcyW`+V7Tw z>gy)FC-yw&26ApZ2_9bUNg3+&1dkk8R^!!!OdaHZkU60}7Q`4wkOpl4N%?B<#v|jk z{mUZvC9?cFw`<_=sRscXc2q+ueRiKmO&q`^ZIExq%#srz#Y@8+1 zPiTHm`%sV$7=@jH7uPwv$Fo#u*ZeG<3Q7~_-pJLVFp4A5e{MrrHcW5E9i)Pn&P(8^ zC+6+toy2C9AS4fu=m2@#iDx(}UN&3`3PuabV`431)fF22@2+|l2aw%MqHt+Su)*zV zbkW2TEwHK^E3|}BG_w$0M-e0DR`$D$AX#b9MyuBAOb%r>XnZG+daB`BaT$Eap?jLv zqzoS@N=gmk*5(d^3=8$RiC{O3^~e2}JcVWPSu4t8&`NpYU8(>xo@!9>w6C-|QK=8l zUd@N2OMGD2GDba)I2gHS2q9!5D^m-&dX$~(UZHV@0@24fd6bVJ z{=LmN*Q$-9ar%k@Cl=SB#brJC3G3L|9jj{TS zs*POo7K2Kxo??w^04qS$zet;_jmnLcSkR?C%G5g;u{l6)J1^tcDJ7PClns(g^PPNA zY(hl;d$LFRk1@2)e4?ynSwkVp=G@^1%3NUw2;_~n$V%>DqDk95@IhQWS>iR1sb@X? ziCK%ti1{X;rFt`EIqiPTWbeL4ivR9$yo@UbJFo|7(c7Qb=arF9BWcqbJv$6^QW*wn zZ$MDWr0`2?X$VV{^xXa6m*l!&JWtGxD24A=s_v^4D+G5hk!gJ_`*}h1v=?r@Wyd zFp$-#F3U_7B!~@5Bwbf88{vo}Xe~-*P)?D~-4XmxygiU0ziOFlor&${oO)`%xs{dNDiDn z{@jlvxOyr=JnoYnUz0Cij*vBhkT-%pi%nCA{c1k#CfQYj3lqD!49dLcQ2^ji_!Qnl zBnK|li6(Y`mlX2? zr8fpdU8gj~S(C0fG$mt8B#-K{+lS{rX%HyNv8mh4DJ8BPMT$8cN9i~+=tLMhIbKI#|+nWFURE_~uI{H7`iOVvf> zUOy`E;5KA;W)7W|x(@5Xbk-+4>ps}Ck0xUiG5tUFNuO-&DwLgWT!rDGt5KxQy%s3z zj?1lO2C3wSwNS$pND2rwcf&?!kd6<(1Pvun$Lt|Pj^9w z!T#%wUl|3xX?+_L+QAC%h~N-V)g%-T_}3s{|FZA)DM0bel>-+{O|PP-wb{)W{jn6! z5RxC(P+<}#M!CnYIRovXM*Fc=%$=33^L}pyrv+R}_SHL_%9k>J{2g3q$i#sfU02m@ zT3*#*PNKFBB$C0gxSP{9N$`?3^%bNEjZ>XIbV=LIKUTRP%bvmq?(GF&8gFZ&^#nG) zm3sLkOdk>{i0mz zgesiWt=s_#KRLmuj;~!K1ELpVXmx5kt#d<~<(O9_;!-2JETWehpsrx^?hD$L7?A0O6j%->?k zIsS~b?^S;q`SeA$joQ}IAzE1SBU1%mH%ppIjw|2g|NWN-Fh0y=ba%Yf~i;4AZhRz+mBKBM|c~ z=WP*h-$Ol~$7AMPFv3;WwK!ulZke8C8^{hPsEBNY^d=cjTm~mfCs)$5Y#&U0sauM^|`3U&U|>`g@b_Z`;MH9vc&>?eq-|u(uc9 z?f^yH<^oVL-;AE-ICYi5-Z+B&u2%qi%e`p&PXGRq17r;Q!s5Wtfa^yFSm(%xz%N;s z*(4$8U>8N{bM0dtc3`(Ny8LnBOV9MC0_xy`Qf$4xH~9vGUr+P-O89GqK#F9YQ{;eP zYbvL_kGqdlQK%aCf7_rOHV*Y8dMT|#dmLEp`w?kNeiW1uA9lL`<4+=2lIpoFim5{K z5kFc}+3!dU0Mt98rs&CH1JSdS{F-3+B~qykPR!twM1#AO2Br#}tOw0-31ah4?#<5{ z>2!!C*YJJ@J34rhBj6O$IvJXR2HaVcgQZa$@gL@!08!mlTliOVJS9=VKTglRrd&d5 zeSlX(=#uq=Lyfu5n;olL#654Z*zbfmL%4k^C$}R+1_ve$dERw}nWi+oko zUV}-0)?zGawz$wWCrsl0Rxgl&(Y&jLH2sX^;RjO)OLiWPX@)%4&e%`6gzqGUHmlLV z;D(+DvcF({TXmx72_%-nkVZ>Ku6yXTSXhz)w6R2H_Yc6ueodDUxqtsqT;^2TWRCrf zZJwmRt4gnFC^#()&D>-hOqvU#Q9@$?4`+0BQtBiuGk+G8lpX6dFF0?F9yW2~@cO`RnwxvR5y|&2d{T^LL3-O?j095wgyuu9X;A|foTcXCP)FIzf zFHI-b%SOcn~zZ9VuQ#Eh^561tLo2`Z+JOTkUS?w ztiUQ~`|;QTiQW?^2rbvJ!)(i$?gQ$@xiAlfbCGamF3>NaYx&CRKHrRdYi}$nQSGxfI-#^Zi zq<}X*6L(zX6AFLYV3xR_+zstExF(X1!?JjgWHxKMq1!6Z>QW{N5GG1{R}Ia?#!=UF zEL}EXGM7@x? z67x~WSX1N_e&(=a)T4=rE#0d+s&G2*^R#ii2P-4uANGYvH^{e&c$_uz8~>`?*|^p# zi=;>X{XNTtO8<{c5?{c{HCukmZnIpFc7BB0nj?N=gSrf{(*f0B^a4tF%Zo0P)rVQ$ z1=l8AQZK6e6m_dIz>2R(qr$2)FwDL%bnK(V@XLuGLEe`}3+kmA@|Rn|#y;ng8Hn99 zsGnZZT-4K;?+c2*vaK7vJJkxG7kZo&t zDP`0ms_?DEJ3MhH|1l55gA%gl_%f)gN|rnRZ+gP&;Imh<0;R3PHBGzEs5?*r#0|#S zS|`o%LN(ac$S|1A7YW#Xs?)HrDprf!M62JO%!m#Mw64PGD%I6{7BPUGQ5k`b_dv3G z=;xl*a^$4X(e$G-UE-5|fE6dpw z!XrAsw0)uE{XT!qT1ZtKq}SgHIFkAaN#bX_^HbCvi)9Q!*Qq4H0=7 z%1ZdVP4MKpCy%JXMChqZl3X~B@vZm#q6O%ndlb4?TmG_Vbsu@b1{G{kH7}$Fx?$c1 zO5}hPclu(Ek~7rxTU5pPlF5*+;J>V`e;j1XVD3Lw5n|Xrs(4Z*n`+F2`d0CU<$`-V zpc(gLIQ>D+?$=OS*W&!ljm;wCoxU{cPfX6&NIh^I% z3=9=aj{Zd?I9N+4E5>MDp#+V|NniADj~_08oa<8$-M>?D#xzpjJYJVsr4lXlz|XuI zd)X1~Y*2c%P^Bp27-HZeA_~a*{@@4VKBMndd_j2!t>Bs{5Kf> zk1jXoyKH)A*@Vqo?%7l{14shwTKsBB`Mdc!J~u4ET1kmCGp(e{d4|sIKNRqpPzhp^ zc|S)q2mGK65h|_xutB$$rE8`q^R@6gn%)pqUIza1tM+F%eo7~ID|)_)?JrgSzG5*l zFmwQ2K^}c4xAEWkT>eB)AC~EsB0iYWQmuL#Z>`-292LOA09odssDkYWDX8L>^Zjoj zcgMbUZ4d>p{Se^QuCmsSFKM5{BUtJ!vf2pZd62|3YQsGLI7uD#ER1X#5)Zq18_zwd zY{Xq=jg4Ng+VuNTPA#z+lwTftatA@rSv8ws4Q)~g=EF&agFJXX73o3VU`DLW$q6;? z6>V}nM5^5JqAVn$eTOee#e ziw(Am{MrN|&u{7Mj6UB`ig#5gu#Wni`E;>~j@9VQLP%Z>Q%6wjlyq1X&^ScZ^xF*O zP=NV9WPMQWu8-2HXyYZh#w~q755>XRDlY=cGl?i(XAg0Db7Ultj|*r}6Srcb`v{$e;VvQp=FaS2ko@Z@{2Nx8UTZ<;97>1d1_eGq$ImYS9sT-%>tGWm*E5(Nh=QOYCfRMXv@w z1ejPY6ZAI=XwuiGCHEN2>e_#`4Vyn=UgE?NK^CGtnH;Vx61C-B6^=9GygOCy`hJqd zI+!4OEg&rWUz-2#6vMlf*+N!Mw%jJy*#^Oj4m!DujOBQD1SMPOXk+{c)fN8{4hPQ` z#|6;76R|y5HbTr9k7yQ*%Mx8n0A!=C8$+vDuXu+)Fn$L@RXJH>Oee&{qy^sHey7AdS+kn~Uq(h_d20b;kE-gE({u@U~!XiR|>5OK77jIu*kKaZOR>7SZIN zWIjcr9}7~l>JV5DPQbontO@1C&e@}(I^E@x%4DrvhDtZ5Uo zUOvF~&<+RhbVHQh0Wp}ER@OPId{v*9C;xNjsi^4W{zh;l+=g6r6pBZzNjSi>x63n9 z%X*KXaL8oD@pnkDhaK)+N^^#%SbHy95xBq#H1=@+IhRw4+QgOO#FG9a!g;r+PjY|V zr56K|B`Bs-&W$q$?SMXLuetI4)-yTpY*~4IY_s6Yh!Kz3&d^0E!Ju?BBIsruz_Bi< zpH`!>#wF9&6zl*}#_?@0o6&RR>pD8-{N0Qn*V{f(Q}p>guy5JHEC-^HW! zKo6&3Qe-gtuTnX)$TG^G(GMlbTLeMy;pOfR!8s+6r(uNJ`;w5)iGK$>;aaI zoHwmd42jQhw7s=oX$!k-C~0TD#-9%;Pgw&JB4ReiNa*KmJSKJ3@No#?Sxb_P&mmOQ zXm=#u@bR*l$f5_$QWQIOhKxR-;y0XJw7#XzSfKA??eqs#Csuvhu0<$nC@0cPu`iPw ziiW-lA9s^{DY#u(6Dp*cbk=Nk3(DGvF>zIOT>RPW-V*R)i^XO~0^5YWmQiA{rK+}Y zyYig_5}`OZ6WzigMXae;gW{N9a}dpq6TTE;nB>Br`^=+i3ZdtG zML7_K1~YMYaiRqr)1AO4;nIfaGg5KD>0{Rj-n5C1_7x_*!C<3Ukrcd;o@yj$tc+Ox+X@gQaP7ds$rX;$AQHsKq6OiJkv!3Ho z>C0-3?KIzSpn-}!uc8T|L-H{MqMp-k_fqs%g(Z}|w*YxJS$P;Z_;YboK|FvL|L?$x z8ZiNIrmEljCQPzwuGDE=4p-?q1c7r1xwUnvi6mipaU8>h*?&JgsJ|pcR2e*?$mU>c z8iiIa!s#K~HxYNm#5BI0EAQ(&wr+$Ir;6;uN%&@Rzel5D?ZQODYVu;_CLdEvSg;TO zL!qv`tL+c#9^Yplo)mb2jO#C3N)$UC-0GXznp}nggxVblRBTn=suI901Sm29TRJxH zoQcwzbKL`Ql5-$C5Xd24fFF1(=gHt)8_JojV}^44Cv7 zsH&Hol}mnvimW5#cQ+saBhLVDq!3xyR9@MrE&QZ?Y@U6J)%HhYRK*m2x-4bq7D^+A28eJ!#^LOGCPuvSw?8QEH#VK%DpxgdBvlSK$45QM-LZz2lYY}Q)BGAppO`Sxp$ubk z;ct-<>~6!1_kcw>cg|J_e4ka#Ek9EOIp3e=q8m`4W$peqyJGN_8GB`8T4(Ld_tliu z>WcLA+RiM+g@y-?1=LL~>ZeBr*YInJWf!n&0h~JyrUOKoon0sRqrpfg#<)e_tZldq z-r5}b&U4V*?J&~vACWizEZ_w4EJT)d9|XLckxuOfT@wh4jIkD!2i`^559VEK#R{?; z;ntY2SYIcoQ9}6H!YpPR!!kZArGk3MmLpfJQ!ZUZtn22T<+hDJdb5%MfXo8(?~OV6 zb3(-R)AjmBUvwO_t1c<&TA`%ycshy*VNKb6=qCW37c5w)86qRzIVC9J*!=xq=5b7q z|D55gE~8ufMrWtitCiN(6e+G)Gb~N!l7n7#(usD6XYP%y_sqvWlQc`$8%RgT?**S{J za5a;&!P~Q9HK>aQA8+B{nV~f%b9WWlt1_f0@$NbTR%Z~+n6>+XbrHSi0zTDDUNmAq z+-41ll*Z~{CaZ8w4%G}E6|fU`+Mav`rk&1bpZKJH6=uyV^9Cw(@>c>J9JfQUa@2?d zz>6!VXSJ6V6uB6Zzu?}pemwcx7GivMoKZF0@w5jBX0T!`&4pAt96Sb-%W}l}H!*%7 zz5ot6fAa*4(=&PF_T4X8DWnvB{n6dHm*TsiV}6?7VThTo^r`iU{lg?88%6 zgQ}TB;M~eppC3b#XY{7&FH5UA=svq26gd*a7O|R*xRYe97Da%|Y1tI4dFz`1sN0nw zVhVXO?#gukHUFD-?jJks?3 zsbA1)iAe=LMPlwXUO=vqAJ{kGNM&rc1;Wb|>T=+L?8~xsHt!4qXNy&k&9H1j*HK0} z#{%GQFixOFCE&)nfH&9vZFrJMb2UApL^#fEl1P!bWm@izcbekIYn$C-yof5HXWrg(5LX zlsBs4?mf2eA7neOZRu)ls0B53tD4Wp{_E2l@g`ju;j?we{uz${OY-pD#Kzq2EUf~n zeLmh{cJV(YC5ZG$Jo=N*W}|GAD3fE?;swsAoGA09)ATxH44{nqJ2>xPYntP(G)N^b zV4|h66KgCa1N6EoTGs$gg91mtl8_#ffj`=jS+AF8B6KpjjnZvV$g*9+LG7m+-J6T` zOB)KbZCis)1%N1TRokC`?&p`gHU!)~r(yTMwl?WjwV@G{ z4VZ4S)5wYQZmE@Kms+l@Zlq6lw{O&rU&;&}bv;0^a|MTneBAC=nb`N9`KsEKHIO8E z+$=e9+=#{F5D>RlXCJGtpxZN;L6lh3Ffyw@mnd9PPT3VlNvx z&s2;rkRf7mGf}rFA`}povFyu#V)VX6U3*i43UH&#NA`Rb#v3G4HNw=axN`YT#mM3P zl6xo&Xf+{8+7Ik27SZPOQQ?J9`l{f)KhQW!qZ76@7~peAbHiM&Pg*9;Q?rTt+Oe)F zoaRADpE600X~zHI^qTT=fmb6Gz0(!=QcKA+n~FCaXX`jLjecCm^T_-z!HVDgV$E z05Qjca1;`X z^?i|1TLphw>P^aw;7pkDS0*d$hITiUd`D;vmUYAY5UU`D^g{9@ne2483a>yUG3l;I zfLx4>L$Cu*M%_B}p(cXY4LZb_;D zs8`IuQF= z$a;)W)rXD+0R}!!X;V6347)ed4fAZQ9r+u2&Bh}pL)lB$iHyVxg73CPVCW@XV#@N8$ z%s+|_@O~8h&WZeWXUTmfgpi&;F4S1Mvl8EJ<95(tA`WkA0{=%b_!gg)1H8T!f>SQ_ z8?M{#v)wbF2r1Xzq&e3_?Z2O6MFkraW1ZprbWRklv4jJ;Tpr>N`K>8Eg!i$SXP-0_ z=PJaQmuLYb_ckcC-H0@PgQk3-L>?3Y8Pcq7%2O6XXrezBl`dvEAT(i!d-39$T#{Vm z?^Qbi7Rch+sv3sj#G4NxOY*G8A6jOXdIV|St_+-bQd}`%LsfEQj2qI5sRFU|t3(!1 zKpn6cb+2j*S`XmqYhZ4b`-@NU$s(-0J4x8jyQBh79x#s;2%b?x4Zl4TP9w_1EWJ^JmEYONV zSJZQml>kkd2VGZ!ZXq6|on<<;5U>;n(k8pEuBppXJyZ|R->lO2w zxvvjhV7_IT#6d=)9--2RAXO2Fw7&y?*Z9V96~S20dFRV24Feu6MdZm zEYX)f%U)|Ak^v%dah9gy$m4iT+u+rO1+AC}FKbgMV^QDQcNjd1cbk=g3?ko8JQ^1y zqaFVpfW-hv9AdX|z9#PjhVUWkM-2h}2+`aYz#sIL7DQ}t=Nr>mY0RpFcp^>7bVpeW zPt(gZaqsyVU;X3koiQo#LM^l%B$_pKhKzP&MsIg*<5U_H>?eszf(pwE_c|mbXgHFI zUuSYFId}+Mq*7*fi&xtuxf3a9*0LTJ$W7KvRIdKFo<}it9_16_lbs&JME0g5^&K|t z(Xo94HQ8pKqq|qag%~QblB@lhPuCR{{bR8H`^ zM=zzmh$;?|o%J>uUgqpAaYlFN>+C^zEB)>aLF^{vdb`*0LJ(4qii0<+GKuXUVMNt# z&!Tfqhb?pQHUe?vPvwKDYV95o+yh~OtQ42`M_u4q$B}52>8Vg3<`{wCqyMRAY*A%K zj;4{OLst!4o_s{1ebJUOgjD%f)k&>|%Nml-Wu(DsOD!W)+QN_QQy;mbsF zdBKzLJQiY#efSh+Zc(?hDpn9}u@y=t^5vucT`Ob`T-tK}LwnafF#{B5%EkRcUIYXU z`tmPNk7W*X!oTf`lVeM*`}j{TF_3e!ZI9Cp7_xo}@In~prTvhF8MdGy91oZup84Ee=&b(2|sA+ z)0Ag$D}t*ccE9)LZO)v8rARU8u`mkaXnApbAtC1 z>8RAb2HAGfkJkb}lqX9Qr2@zWz$gf^MrGbDy_D_Z4b^=G^WWgLdgz(FZRw>XDF| zh)^xM($w&vhxf%lS{?b*PDptkbu=l*MfonLc&jN0ae+|(G>d=@$Nuys7vXwwz_ zfA36+R!{WHxuO%IB$rn=+qpgMTSQ(?|Wl)KfgZTRjmWd)#=X$f?} zpx~ExPmczyi{Ex@Yn=n$$3LrxT^p^S3Zo5v=>B9smW|mxpH=XLvLQGSLP8ZNk@63F z;1?;`Rn9SH0O~$I{{WH0Kqb{ftzoD5OxAn^2GWPlctqA3;iH{RdFLq09~#tMDm1Ar z8|zQG(jt=Wz00i5)rJmQNOGx|)Qky}q{p!$&z{%uD(x|lk7O2sV&dd9fu~zRtb4F% zAYLPxjo7=k zeoWg7wtuTMWDa`FBmIVrs8sx1s|j88^;y~f+CHVB*sQjwaO)X6=EE-8=GNlk zyx8<{3(EN99Fk*96V6!KH?F{A5Sp&V69GqvQ}t*At=j5pQ6Z9ha375{eXtHNNRHa= z0dM=Dv+V<9-#lN`ME#FE~ma4$|O$ z#p$X_$bFVFBJ*WNrMJ|h4NkBGuagPQA1Pa<0dfuZD43F@T2Y-Wr<+=QN^Db#W&BvQ z0&04j66>t%wLy83pv9>gQNPY09x0gsHq9fgBWy3KYpVTrk_@!~)Km3uy$qLa zqA?Q2TU$xeatX||5M9{l=tc3K+6`>ppfh|_nbTH~A6>94z_yW~WiREOs&036RG<%j z-FsfLEfo+t@BXrIYY1MFpnv_LXYk}SiJ-?au((D#1uG>~x$noMdyj3+=km7Wu~xur zATdU_rtb|fi)>n?bG&v`q){m_DkA#-5j5EfXW$p>WochksSV^{Q?i8K!+`wssqZ?X z3#u(-ejOOCzey`AQmzxwatg#S=#eYxNDW$oC?F%+?E+>7%{v8mPc&T3%s&_wFwxXd z4OZ^b{H9nF?0iEAKGTBE4G>_1?L3=?09}Q-g3|(zz&IHgYF{xF@&FrG!Gc^jg0tj@ zUI7`j&i@gaD~ExbbVa-hVwFHL#*mZ`HO-(UOmAvtIVDL&73zTACMD#v#|V^zc;lq4 zIGpN1sR2k+C>VmY7;hp=?m}~o>h%BTp<`tv(1Iby_cEoPv&A4pCXSfsCB)Mf{AXBI zef2Ri#i}xDaxvONqcI=2niE>g%xT}8>(j|8!LsL#Q`AOb=I$QV6nsK)i~W-8@PeoM z)-Nb1QIW*?S{x=tpSI_~H@nl$F!;#3Isp`r5Y@{=kj9N68+Ns$Cu3F_)Zw(jA>_?XR#~4Wx1tQGxm|NHydnZ;d43RQ*fd!&vE~7Qq1p zg6w`S5l%pbT}SJbRZqLePU#!a=W~aWvMbXZ>i--i`6do$gNa*W)!n{~X{OS~FYB1> zgOg9R)DK``RiH_R){I>(s#2g7KH7>&E^*3ZS$)J&nm}VOOMK!cz~z`-cNB18HjKr{ zIQBi0JZ@Y8^J89jf@eW88db-&=5usNa#c6i_;O*|;uXSb%y_r&pzyMBPw-05p`uI} zk4=70g(jaS&E33@3}y8lZ2w;ID@Lu;@v{e0F`JMaHXdRP(W;A@T!4`~jOyRjdaGc<}NiURHc{TQ) za5TVJ`8O!JYTm`9Eo?RjYnJ(yfYpmg0tG1g-HL zG^f>s61i51)8lmOxrOYa3mkH zh0C^$MbRsX__wXb()ZB;tH6_4eGziwQ-8=7v zT?}dCE)h4ja8RgbPAkFx@1SF}8XNG`H3XW?ejXA3B%h=MeF$rD!CRnGYSjVQ@s6L2 z{**LMUvg{?L-kRz*Q$<9-gCyNAYzEP+&4UeaQzsk0yPT|bzwhkKr7%hTQ23*BSv7V-aLYA6oIv}n zS+_|t1o8tJPru&`;l5%p`9fPA7PLjO4eBFDMikhe*$k#NQsYI|tyV3Z5)L9xh}L^H zd2T=kiNZjCyx0-eSl{pnX-&Yaf-TY4&6}j@Vse5_r|w=*Qq>t#YINzQJvt}lU$JX= z>9Dq=ncC&FDR)2SY;IN3+(Kl#_x-OmhL4a<9rKN!LWZe{Y@+s&`COpgY*)#x%a9U9 z37tQ?o~koo1xY?|;l`JI^FMIUOdjOaH?!S?yzuhD=PzyZ>}po+AQLq=v^ zh>AZ1B5{ktiG;z3Df>M9(g&AK*yKUda52K7q7H6s)+8HHprFV~C-0AkjiGl&KhlBR z`qFbW`L0^VZ-E|Dmmy>vcqeXZrRCbyPYD^}NRAbY;&;{nzFG=5b&fA^gd=jK#BU@) zFNK>QCG56EkZ3hJF&mu4)III&2Uif+{(0 zi$jo?&KF;>{xJAvlEW#5m^ra?MJz_whAI5Mn5}9Sd6ae!<4^d_c{}~H2r1%^{|7;Z zl*zORv-qYVt@3ZqU(!9s!eLO1`nSJMAMXzfMm&(qt)U>`t6~Olp2rS^Mc|GCI`1;1 zBn_)JucDsqvnUMdr7qBsL8K}4hZ2w3NCHsN|6)v9nG!)5NarXpMLuSDa%tdmJHrk7 zOkVk9Vb}0N1F;KR>MbWRU$9Sg)lqBHzWWZF$8fZL+xKYrWp2c~WdapaU^7Ypd$d@4 zKHy?JFW=ZSrzk5_#mQoNLtamf_{BmUHr#1EPcWu$yYSbjPMenUSe3q5O2hT5J7YX4 z=aERLXc?ys5E$kVHOpq#dt*F$8aL>0i`iYdTcv`rAzA>I4Xuu8%(FL*|6sjGji;x$ zJ0OS%iv)+a(51sC(@%MkfH@=AY9j<)(l)aG0R)r%ddW^WMv^-?|5FuIREGJnp2u2v ztO<(iK$0G_>_-pz@P>Zidfl|>e6DPs$Cx)Bowy^$yq;pfx8QEDjbZt!91#!uE00ro zqHR%l{YS(2H#~O4s{L+ zxVn>m(N7&HSx_m!8kD8owsdU_(~{LYn*SzTZfakEX&y;^_B>)iDHE0ANnioc zDDubC3B@9-=-H{#{OnRB?!4QU4K?_SP2;lIWqj4(clkJ2xsoK+UX@f%9fTH<=#rW6 zF|r?}O@q=2cwdC_d*xuheuw`_3=LM7xy2^o5&SA)5BG;(MPCR;t8Ou&fn#( zd`!V&AxkU~JHe0jXUQw~h2s`r&6g^FqGL)F6#KK_3TWSuZ)6bQBEW`~a_mC?k7QS5 zedUjt9CDw}bNv?$lo(Wjk02hPHV-(z&S24q; zU*cUgG(y{HOBpdfA%p+%KutGxAn)V-Pio8=n|@;hQZWhq@}P`u8PTzxWeaq5+ye0Q(E)<>0ERh0q00~KMCn9L^{ zQ_A9{891#UoE%BCOmw_oh7A3AX3aH_TI zY1IDi(M)X@`p*VbqqV2dWP{S(^d}Szf5WD`HEaRtg#}ors9WS0E2U^Dp^sY18#&9W z9h&aE8MRbFEtnFa3jz!|!Pg+1kI=DvR3PWTv;sOSLMgw^Bg?07g8k6q z&NO)B2Z0>KA^$ci%euMVU2J8Ybc%DAgZbtTs z`__AG*SSMxocu)Eg)AUP%_{$t*_5U}eE#PhDXo^is#s`#ESzzmiH%Sx7=j}Rb#&`E z(B}werNbzz6TTP+WWkjgUbEk5qfML}Y5y*zw!}7BOrccpPFnAvoQ;-(+s>Br1B+>4 zGZ3r%_c|UjJ4lFOnl-2_RIkN9K|^joaWPbbp*@>EWzj0>j`@4pivCqsJuIFz3clR? z2ihVn^pL<}f-5PXVH}eEoSI0YQEFrA-Yw67e;KM;9jAkO#0Dz$=R5k#~^Xz70k5sTu#2c z6kKnhwva3K@Ae~OwW-d^LsTfTCSy~rUZnu%;?1_fO3Mgnvo|YGbx|ogKu~;jm78Wv z+d<{hGl~qD$^Fd^v@Wz7IRWJxw0XHM=RDi(T>%p&sq zwDvsXi_tY$qqrsu!jwhDVQ4DC{3y%cN-99;z+c1e*_ex9*ExI4TsoX`1UJjJ!=z56 z7jCi0>xeHazI{d3B>c0Fffb}okat$3C~ zFa$Fgm%I*ZzbFKENFuitIYO_xvi^hQO^LRQz8!%y+Yd(Rb)D-4g?7XEi63JmQJ_CV zvnP)rV>62yc7Ix@wzA8-r@__ZtB5%_rM+X@+1+RSaZgEko* zBn#}7I-$8d}D zyVOScmONRpSXOmI<&%1vZ_653;@a7T=*5-T#H8y2Uy-wt-%K%iJqmOvA`mbtQ2{L4 zbP&Cuf|kMc(4X{f&4pXVEo!_-t+Z^6>yM}J+0s6yh~)DCzZn!E{u9{4Bx>BxUnq4P z*{riq><1F#Mn>{$Nm+_tgJIZxuH%#at50$>U=P`K>!jj9Z}o_eP852P^hWNQc~Y`6 zcEPLwjd0ua=AwZB_7!h(1uHiE^hy4Q@wXtAwuUVr;45Yy-IRt;)5;>dUQgV)DxQFz zPH|qP9W7ad&3l5_6z9ui{9gz3E40mzAf&gJ*QS*Di~PuH@1JkWc%gK#W5igQC^_tl zhZ`Cop0DNqQ-l*KInu5PE#0xe3s%Zj5#(WE_+zLqUL+b7v1oACxpD;nT#?ay+f&-f z+kj1JaxVb<4`$0NY3GWS5st$E8YAF+K?xjCd6by)Z_w-nruS$rYvd$JR*6QX%Lriz z`Bw4}GbQHq)9gtIwjI?P;duz1gy_IH_4!GfVL0gpBAH5*;8{?IjExMASwJ)Lw@L&+ z&=I7g?wf0do%R<1kJGyTv5(t!HRJB+6djhxP+lj+8Q5tsdq!H%o2?sJ7g`o39EN%i za0crLR>&ZWN4@2_qVAJa);Jm84y(}~{VElyk|_g2m%C;F|0(I#iUpj19%VuN&VpJ; z5%o4~+1q#Qrr_GO&7zII0T(Oyg$}B3nLCN|&iRg-Jqx3k>+Fl8A56Gz3z7T9985^B zTOMk*^if6Fc4=VYjO!A4f#u_Nu=N<lw;S-GArvU?kV zU4LlSWD1-UoNA518RKHm*^d8+>oc{1?r38nXlkhR1>m9p^D+rbDpZ~Y*@=$|N5>_l zFukMRPhy6n!t`twq`~2IlyP0?k%D!i-ntJW8akM3h4u)L>B2i5QA)pn0@_Zh?F|{^ zRb>QxB#``rwZ9{zq!KUFGGBqGlV1{UdFQjiH>13Nl)agugZ!LuOIQ>dW#?;Ctp}Y= z_Y1Z{{`~Esy+e~!GhhtTiCNzEVT7RQRUJMAi3UK%1Hx4P0QkPD_W71}p<4O=Ayf?( z7Q{$p49s4(OzxjX4=K5Fj`8*XI_3b(dn1Ds$T4)DNAms3`Ch0zE6G8(*m1x!#g z)?(mNf4=HKKNY7fHm$`$-X#my;*TBWeq95r!mcMu*)~Yfi&L1Kf?VLa*FQgJDsz`S zqC2jvu@SABZ8elHGurE+~@W^WShnKJI*9*P;^N3JW#nZzh6cZ?)<(lGUY0 zFJT|*I-x(Cn$&F606jp$zqz%dT^I5yq=P)=Ewm#Lmj(r|tMo)bXab&OWrQ|C=OCzM zww|!`RouBg!Cf%&Ip_Q>F#f7GviS$~`4eqkop#D0o+=+Shrak?&?<$Xlt`jGVPb@E z=}LyK1<9k6?C%5ql?z2N*H*(xYHGOU7sveYz`!n)J!4np8&(+1r5iw7BWn`@y7K-< zDwG3LQI;9wlm`+`RxJO3c@?=Ye$q>rF5o?zQUou~*m794teVDdO8*?;_lj{Z*F{55 z3RG9O+TvN!d;9EB1&>LrNw!HrqYQyj{xFGNv4*QZ< z@l}jiB&Gz(e=xuitE;e7fW>T}9Y6v8>{CY4?U0)0)AiM})UXtzNf2sAz)%9@sRuL} zqRkzz%suZ?{UZ%1UIKrq`I0IwY~t6FOWdAJVKKdQI-B#GeIN=M=384e4_PFCZ5}Dk zulm;NJlbumve&MiHL%q|eP_`wd72c93MJ`4?waWVJHsA@Y_kcQ;Ym};<;2~&)joJF zgKfteczUiwn(}fi=5~fpaX}B5>Yeq_{2LqaPLfW@JdLhb!n-9w#GJ0-gnawRGbz7* zIrn#3^mlgf-Rro2!(u5j##Qps(xG=V}NOJH{1X$7z3=0COStZ_cp62@pt)J zG_evq%eatLv@1jH;m!6*kK^19~{1pSg7!k8_a8H;`t^t9tgtq3n z`|d;f;7u=p{EeuwCWGic4E`IDe}n^g#bBFsG8h}|QDZfC7o6WF#|LKJb*}fuL@$ky zmjNnf>(P0tw$mF|yA#m`vI2qa*WZW2rZKT!fj1?-HOA1+=1Zka+#@p`nv)#KyL?+p z7Y&~nGXF9p^r(Wb0WN;)f+`FHd-oX*58mcHfbvy6kW?r_5ubUhU~Ixo9AckAqyvDl z@S`*T4B4B)Pih!Ykcshevc`HZbx31JVTXIO&*?RR)^mi|bI z(t)`oW^CL2)3V6I9C8nh%IET-gCMqcSvbRnR8LuW47gY+z+Q{&$B~ge=%cF7IEpK^;4qaUX=YVe>(E-}Jv2!@ zZY+x^=?B%D2$XvOZ&gO_R2o*`K-=*FH9dyHh8zhjL4|JU<0q=Hn@+RWx3m>LxHb|5 zU;-CU*E7@!r;T|A1uUw#mK}8l;E_JDJnN4hy9#3#!AI^b^7w<{G3$$~1zBzsIqPk- z9O2Un?D$1G#LAY`M~4DUVkCFK*+i`GuF2aB>+~d6MA~c62gLl5ZXJ7HgH-NOFSqk= zZL!wL z)LqR1#gN0}d#z~-`sJh2nz#XajWQ$+b?h?BPkP2a68_f}G>B_L@#j#62XgQWLXUnxMaN!huKWGXrHbSQRjv`v{cyotA{-B^F4{o35-B`JvxFy zwztdIKM08OMinn3ya#LzkgzjwF;PQRf(29uC7{V4-746Jv(6kOrC9bk;e<@brkn~e z+&X;0yf@Vx@>GeQ*(8VwgYdJ}GddRcjWX%rQnDf$hF+B5*{YOx@F)p{aa$!c> zqq1cDs-M|M5o*-Wtt?*j#@5e7$weh?n=I9%5e(>NhcXH;y=<>F0$1|JHo{+q~v%^R%Q=>n#-VZvguU{5ynJmJ}{<1;fR_-|0 z4Z%R>?Bv2{gbTW{y0gg?j&O*HCF(9(nf-W^Z0vgl4d=;5x@11FH9+% zEUSqxwN90eu3lFL7jWyQX6&jM1TVGS;4ipb%fz#3l*zO+2pZG-5BZo#BCRiv{LutA)w|l7+wdk{l*4ySqXB2V1-u4 z9WjCY1Ctte^w&pzv7^xC6?Av%lgtVe^k4txe<^nYX4qydmB?sL5&TxWnjj%K@Ub0P z`ZFAbJF#t4X>cEm9DC@|O^MK{o?u%C?^TFhV8dNsyIz*@-~OzBZ^R~SI0)GzCs|QlvuN-gY`#$qJMnxRK__Oytw?W^8?^{j z&jk|%2*qc(eF_v#HPVsGz!kvk-bIW9iG^8d2xg%+%IMohR1Yy#ulBq6tdRCNxx~a@ zzM9tZ$6zQE`1sbRqU|bL{~O1-uyqxW7!Q5|NJ4#wEf2sKDekf?viku$U8y7!8D8Y= zM(cLDD`(%WX{M-NPfO81$DS$cFQxb&OJQdk3}LaV%da=M?Yzzw z!FZ0NnoHpk;yQMs-fQG$Fksz`xQze5ojbvzm%QCNZ87?h0^?GXsf?osC0{7;F(Q_w z^|@A#8w`%C`E!6UPZBkKn(3H>#WE5mbN@;f=a%|FAiy-_6en2L-*(!(gqg1aYK%(J zIL4)0sr1XDISM00o@lIG&Ue5K}I3coI1y&q?wHm))*~nUekA z*REHmsezSRN1z%1RV~oFEiWzCxm&8e7qq61dQB z3HXpkCdyxQ%{{|i>_t#fWti=}%{;h;FazKRIG>@$TJC~Q()*c zWa76f(-|-ALP#EDQH$9Ik!B>*5p7vDXvMD3N7`LYpW%R!VfAevZmR1r+>c^-=|t-x z(t-BQ*ZwsI-GB0>N`)QG(Uax%$Wg3b9mY6djFEjtuD*~&;s9C?j4M zaIJZM&g>O__zVQXzZb+3w77kiXlro%4li=Y4%y!(4E0*tit95yp=$g}gi{((pGW&_ zJ3rPCP({bvu8V+xJiJQ(H&czVV{?u5$rYd%zSn61+txKFyvLO;hQ5VP?dKB)G#j0U z5%AdVciY*MQ4LK}u%38D^O!~&3U?>`$fOORm4Lza%?&XRLxc0cf2MM7A5^hEn;{ev zPw3)F43)A8HTQWt%<;w?%>rusIjQx7GZeK=N`&V?B6iL2!K8<;dNy{I#a^5@>{AGy zdR8a-yzoJB!$gc3%(B(0P2ziATzIqU)v0%A9q=8S%%;}E33laETBN{OzNz$gCwnPx zJLo0QuL-rvy(~N!Ck8xY*d2lv5iqM0o`xv!E>BX~4(E}085Cn^qRLv+?H`vlhZ;Mh zq2BoRJapN%#n9UK_HgrhepMqXqK_h&+6K9>JGbq;GTA^WXtb+%1n36Ar#7fOKsA#Q zkP>Mpi)w5h5%)JV83278zpJbkpI1*>asVtw+g_hR`iqfChp5Z9+bS}`m6x5v_?n&} z3e|7i4U(c}#0qDv9BX^eKkeKy5fmWUMqixYs_|_7Y3(m&2}yN(Ol?TyETFh9uV%gz zDLSf@k(wZX?2|>|yi0#Wu}&`JTI|84fP9us@l#4_WHTObB`0?G`?!I_?A%#*Y}Y)O zogFQC@E#pz-6juceD%__3_$o3+8eIpK7NPyjrPRA&dp6> zDLVgehvRC1h=qNlUorc{?65>Zx>=G~MRX`vd|uF4JNuXmGsvrj^~p_R4|GadB@7)K zfhdIJQ4vHx^7FSMvR58ez$jy5R_&n&6!ZUuA8>+?reHXmgfH;zt8aVcAAnUhJ1gH5vUNoO)}1 z->u$V*7>GiyN*`N^|P4+gzjmR5U9%Cfm#K8%0et%CUH@4dJskI&Z#Z&VZ0)CXYN1> z*t4{-uh5+ElX-x=?omD6{zS~8%ny~z&v-wwI<+exiW@x)0tjstRoNMygf&Zg=}E#d zc8_Nh*pif_Kv?|Z=E#74S6gFuGQ3MNKvXWGiCQrU0Q(l)eV(-ten1XJ!?HZWK$aKF3*Blf)h`4~G7c>U^I;DTV+gp>%D#Z8;{ILp+~&rMALTu3(L(=ovk?)nQQ}{<$VfBkNCj*t6g&lq&uFVan<@H)YkDvw|Lv?R(`S(+elmkkToygZd$OAm9`r*vR^fU&$1TToN$*)V zv6PJa8DqOx?H4HSmMD2io*Pq&mJtXDD} z1y~2R8C9D?YfE4$J*i{6i}2y^H#J1E@}2HXQ&e3ukjv^+*bISdu=SU3d40S^g*I-- zuPg97rfgAb87qXb=a}5;@)=pr+%4!t>mG}e=BGB@67iL<#6*r3pEPiFU|qa3%4v{F zD^pk~0_3Zhx&$2^YdiSWOhFze2Bo+#t=>!V$Nb5rf1!1wd}>lu)`)jYv$D6mh113} zH0&3;x%vcEX%M-+JzcbX9d6K2!}Ui9>L`FMll zk>~A^1093qm^zIR!Bqkux}{jU<{=@5)>u195*tRkxU?u{&1`cjx8KbsF*Ob zXEm@FT{8bP`@uwp!qXbYmh-9&)tpLURvDX~D<0oA4f?hQbxsA=2%Z$8#oyZ+(jGbe zO)iL@xgd=d?T^BDM^IAwAIzazB@YmH+-3NEyaN^}VVOckF!O^b|B0I^krSlPvTi~`Rkdz?rSWhD zd#6}CpMoiBLJjDQGnj?t{z3TiF(aC!Z`AW`rL$M0bN5L1J+D)oD00v&MM5=?+! z<7`eIb^Z)Lq1rW^LG?2`3a6#G+hClefokYND<34%E zFSJ-x!tnhR7NQL991Pfw-BmO>pJdd?0Gvi-N2tQla>6OEV~GET?9l?-NA2$*YIBhH zNwXS?UZlN5MZ!k{E+*ZCgUs5zz&)xYq^M<5G>X-?a@iautB< z-ni52G!nj4Gt06V6agMvET4gp>un!>wjLFO?D5ny7t`NkFR5xK!ahGfaRN$t6wM}c znyQ%=_E8fSdcZL|#C-38;{U@Z!PR#CO#6VBucP*=jWLeshcFg7)+~%YdArhVOx7+-le~UdS|`Fp z%sl{~L8qpMezbFz>e4sATzVY$_(y}GUOki7;R$sDw7;X=urV! zO})e+S8&6ai>#ppVK(OHKW_j1_+fJ)hxme4R#Us_f2IGFDNT)~Fe95+RucjthJN}wm2AZ$#Bsb$C)6H=?bRrRh6Vq}j z7r}~Ix`IlzrMYbouSRnyqO3#Pn2i#Y|P+q#C zauD`nB!?Rj3^I~k>K^G>3STPvF07|51F9KD`&AXB7n?*8~xOxeLXvdN?&$qCY$D{Q~Enlg}AuS9ms=p z@`-^{AuE6D3O5AdmvkHO|0PsYxDen}#JP(j!@@F&KnGd<0e>=~*kBLx=Xi6v-{^kx z#3OD035F8QdPQ|zc3-w?>~OEE#zKmQeusake(NsN>;&S4`0eJVN|z_1V9;$3#d}3a z%RC6xhx$|YdNaQ**T%5*jkr!b2cGfUjY%7{Pi2clz$HCN$T2cRbK`g&rgEet1y?gyHTI*pFLNNnZtqh zm$8w=Iku+52$CeoMs0kBGdf2UkR0a#?}%B;Bfp;IPrqECpr2DaZvX*MyFZzx;T=&9 z^e??o?(T{wuCEN@qtFvPLTK6Vsol>pi}KrLrj&w&71N4l<%dFk`wo$wOA!8&bg3dh zwLuNR3IpvqDvP{EN0Mc@{c7O3NVJ}*CHXqx?iBBF_J(fPs1ILm`hT^u-$4AlV_aq2bmJn>(VJ@}j~?Fw-9DGb z7y1_c+rZMRkwt|m8P~$IyZlth(iCdbP!FYcDw@y0Wj*!2*PZq41%QOgKIa5i%Bp^rIr~~bT1!pd1e8yo~;1WJ~)-AVU(>XRS zlZsKQ9}P9J#dz7=sUC(AVinnZ)?6%w+L)RmE=BI3_pLF5 zClJj`HG85UwZCldy`IQ7h(?F4E?Ify2=rd&WRb>%$`~^Fz2Z&d1p)H1HvIdthPQKfM8`5=Ah3L3OEHuGzSGuB>qCkbYl$Y3Pva`SUUy}_Xdw&ja zXgf@l%nKFlRgu_dEL~5>9bH93Gd~oJXAW1>4oN6$vm3@q=)1k1NDQc!I>g3Zb@1gWRebY4B9zdnG>o1ZCS%hd1K zg->GKM*5~}=le-dv=zymdA<{886b>`g=e$nhEXcTn1NOQWuP3j#gf}PTLZzJl5Ru6 zebfoN&$_gHAirY-#Ughue!+}Z#oZl;n4?e%ELU3f4i!NFidk3BZ#2l??HZY*-ww@$ z-y{?HaObRDu%9MAVwYDC2x+e}>;p{0leg0b72-;_o)xdj+i~8|sj4cIs&3O6VXV4-AqPv(O9dt*H}?$~t-%efV-&e{fe4-%R+QzP4l&jYA-HqSjgpHcK?t$IHOgz#7)%eB`j?u3_)7sFIDL#vaFBc zUd&TO^l%c2WUcrv6p>yY$j;s@<0j_3XVA*Rd35s*?A>b*KJDvhD|Guh&&Ou%A7_AHyJtmXev-gY(k1`Moa9dH@`W|1 zYu(G6>)~(%_wMO{>&K|kva(RPuZ-9`#o<$tDwP!FaC?;XijwjOhRstkuAJ(kD*EDP zS{ao5Kp-AG)hZrK;1gr_Z1JW|9+x~^>Rn$T)~txGm>C23!EO4LAp~B9$b`k%zL6(N z#TRoE=-{iKTO&&qX)1My_gpY4nszsA|3(Up7x<=d@WI@JIw;q0@eD07%}#)OCxBd% zc&Gg}2p%V&h?F$y#vt)JLfdpPToL;1=!gUe78YHBSGIM=t#1^uWboSl8vY^rKd~9u zC||p|OgqQ2O@nz#-g8QnN-=i`xBC`0EFz%YOhnM91o){4|Raa z*vz%ZC$ZObsLFy5f*!Zv_v44)km}FL_bl=fS@j9?u!P^#Odym!kvb0Js~vcp!sJem zIsEzv#rEC&zrHpk)A?uFVFZ$QZtWNUL?wfRq~DQvDk5Q(BZ^F(sZlNL$?UN~2{e-> z{VU8o@EG-h46SzlQGmCIYHEUAiw3q^=zWc)ucj{(yMLdFo6#GpusE-VF8~Fx3xQ*r zTtMY@1IsBvaVn}TmRtoDqOEoq?d@=lMZ!b@(fo zct`*pJ%t`!NAbJi3l+0tc_izwV~X`_J$NN`JS=mFwP88Mg1qPpPm22hq%0xl=BaZ^ zf}}h&&$RI$V^G(V9<7_*6l0Xwg#6`rDcv;&Yr;YO-zhKzQaib{-P*LQYkMLgEENXnzxl>=Eu004ex3~M`wDJ4!JJ<*&$i>9pzOQydagz8eC|Yzn!W> zJI$F0rHM{{6|WX6+td*Ut8KQJ_Gp8CyJAGA-ozN!8Asx~{_Jn$=&&9gT@Acmk|72% z=`b1ikxM78>K1^0m&2~B8Z5aU8rKVDlfRf*8u~F+^Z`w?e(le^@n#q?5D@>))By_# zWkMT_605p`jwoIwzK)AajT?_V8`U%@RotAxN$GwM>QHJ9)r(&+7KKAZMnCIkG3X<9 zF?ynyr*9YA<6h2IZqik=ePzY8|Mr%mc0n?IN_GzKPq}Qa;fL$$c`5 zq66R=+|DP~FtVYB`e#+5W$yJnyXq7&V>G471V!|G7jxg)^g04G2HpRtNAIs-UJND& zr0rVC#S^f0$$NQSINfgAQjBZL!=OlFh3ZuuRe6}0DUpf4V{0M2PQGvsiF@bi6|gU- zT$odh$&tjpabJyTN)}0INu8IJyKjsapPZ8y~su2!JL zlVMQ5U6PE1cwW6?XAJ=v*t!bl+X82;qOM)nO_h)Gb2%(Rh{i7KjEBwI@*{9e?!7~a zgQHg10dqB)*MxhJ)ya1is!K16d4B_VWiENuod-2o`=nMjG$Zo<(TW<2;j!_@NxPWeGi@siXQwXf&9lO@`&lF(~wU2yc~y zq91Arf@CqaFQri!342XF?QR(lzW{A812(}}41B&(E6|JGZ67y&3?Je~(rk^UDJc(N z$oR+l@F#pYrr3S0zp@$4-|Sipa5a+PL4R`{I)Z!L%(O`9OT+n0aBU|!)s9$X21ndu2u*rN$l)sZ(o&xMz^Zw|*D;9CT zk5u+S^g2*2q?}0{H8wHEIUKdf6ST&ma9H^O|3oLazAEo9_280=*#n!s z06Zx`bXoT97PV&GBpUc- z+52%t$NBj=BfJs`K3_X#q)ZMQ=p&wewD^4C>N0aR+=>2uX@13k{b?%n?ZjyMmuk5Z zbI^H9X^*2KNnf9vX0ZB4`W|>Sb}4^Kr`&BTG|)UDcl3W2?#Pwz<<*`YAMR3H%b1$Q zS8e=`P;^(6-D@1Gw-?s#JL-XzOKl<@*5{rWpfi-PXP#~`TgBw;w03k4ofx|bJ}+|bD(PCdS_odKWs28!KBAp!3}qf z8M`FShEhk2BzC>Dk5kQnQ)EXmlS%buU07Yoj*&57+s0W;d##ucsf{aUcna-Unkn(bc!lf0)?6#+09iW~KeN74M zhQTf99eEX{Tz8d*IAt8BY`Ap$HuuW7Kk7c{M?2z$Wdf>e$Sm2_f{g;F=gAmZ^w5En z%Y}kjauN~>UHCDPLo9`@mH*V5&S1%6UN(;0W6^Ve!HE5A?ng`9Gniwg(~PFv`E1QF z{XR$=bu!^Js%O-*;94Vmh!nrN9799sMV*H{Yvyi;VQZR>Vsh&|FK@wB zmuj)#)go9tk`KPdn@$I1l@4iyM|{2?O3Gavqyym9D(!UJ>3>HwrMkd3fNNn>$y_?8 zB@0b);fDCYRQBLcuh-!}Y4xaH!R_6B=&2S?@l4B1jqPrlRxmEQsK`DbF};qc6tY)^ z{sei1Abq6ZzPFAG6B!texT+=4N^kb}R)QW(wQ`?mNucF4g{!SpfR}q($uJDr7)o8S z|3UkxVZ@v(mq#leO!3_mn?G0&CLvJ&hr6Jke_jrLTU3&4>>j9{aFC||I6`1s08V}C z|5Y6C3Q3Jkq4bF0cj(nVdHKN~B1rZ%yQz7T=%k6Onh>7vMG`;at6KaIwqwim<&*5) zg6PU$@5@H}}(MEI^HY%XKTQ0sF zwKQ>!std;8l#H`N+R<^k7FbQb1eI78x@7xrO)>?VGdbUd^)6=LT8d+_+^{{JEuMt# z(Yhvq(2svG_>uiBDuIl2-jA~GM#YB+Xs*c2MhggV)AuOOmRO_N^Bz;vmoR9mavT4b zhX4b0>4(>70~-ldlcWyWA+*{3TlLCiABCqkK6-l`!Cxo5q!p-wAN(X+kvj?Uk(GyW z0TIb$r#o<`BE1DJ0n~+8t4iBO2+t!{SdlG2cxVzBg4m~n#B(OhdEY9GQ48WM0iQU+ ztl|nZOi{@4XVB!jBJ3foaEgPZs_{zcr&;;oXzUC9lkYThaIi~Wk6_G+MY8d$^@(#j zgi(A>p(=816_!$ya}g*qVj#f4Z^TjECcAxb=joGxu1&{ER4eyuL zFa6Eda|W>>`aTkUw719TFMGWTi!U;9FFv zg74RYqC;cZ2s_$DKVOj3$|?wwu!qLa!utvP+Z0b{=UCAsj{#g-U&4S3m=i`&JeBP+O+ zBoV7R_dzjuT`J^bvULdAJQ62BQn&L76~LnKcl#p*b4Oxw<>K$I+xP8VTH7B>iu8Or z&jz#0zvc5xu+r~>dxA!~S;#=2$IrY~;f7xk9zuZql9ijZkYPc_`h3=Xvph7MQbkv6 z0GklVWq(os!8Na}@#&@8k8^XhmiF%*Vp%0xX6N%>5uAf;v8W4LKy;?$2tLHA$5y*S zZfQgJqA2Ux+7BkS?t{#(a{LE^@l;sr|92>rad>(||2bg$9eRjbQHB<4wQLO4jMsl?(DPIy5RGkADR6}6z1CgNZ zq6b3VFtBJUGtF&w5dFktt#WIaLx;^%s%tbNFebq&JMIIh{AW7nTli!8|5cSh4;K5-KXEA19!mt&SN_Si}SmCDm+30t^E`EoSlj1cp z*LO7xu5ZE`%Afy+pzgKBL`uwjbdnQ*HB7W^#yCuiobIEi^O%WBg2Awt_P)l!(dkul zD*Q%?_Qn>t$eJf+@WYE!!9R}v=tRe_m9eN~E6@^<#Gbgu99YXI+nA0=Y5uaoQGHnc zNG4LmbkR{LsU5pH#ja4S4a&Mw8de#u;e-UEKRWeqAaGw`)0C2c+j@b*?_(|eALR|a znP60<#bkX@QCMOMadIg!4Gf%c6Cv8m8wZIHZRRokb#7@=HQRqw6+ZQ}CbyJ}Y(-zJ zC=Jo*EJig8A+X4~9ib%L$HPTZw|kp%qw&VZ-cAS9&AZK0V!oW*+#}~uy{jDnNCH-# zkw-oxs1|XwO^yW+zIa;wpx4b-O0ny6 zczUhRyx(R%o9iiUV6}XKUU~>*nGw(ASlqEWteLHy1edE{d2QKdhH?0n&nEpM8_JrN zd7@&3{>5nAR+Gu)1j}i;#@c73IOT!1EaXQAsuq{*w)_^5_P^W6huf);e#`i%F1TcY z@es>7v`?z40>J6NZ21Fz3~`G4?0*?2?=#|BEG?EtYcV#4C~FWf>8eE?pBwhq8f7gg z2FJy$ZT3hPq~X4Uyk>}4>)&y{DfyROi{7Uc6XJ^$`W+#%A&q1bP*L_WF-t&-S)ehL z&WF@m!b+R}Ch5bw2m5b%UaH7r*DR+u(JI z311;13A*5@^M~?}N*}A4_<$i^-x#%&-RI#aG(!ObpOdw%mV(_`uHjgnY>OFuz4!AV zvIz-LazDpUB{7G<%kHd}eABA7C4b^>n(dKB9$%DsF7|$sMdrLw_ANAZjr3@=h zrRk&EYGrbj6P5&iN5A;rv=>-YtY|8~)3JaGcvX0af%0xLHy{`knr^)SR_>cb8s~M+ zl%tEs{3RVTEx!-~{ec zo${7l60--ukO?BQ1Is-O`*!fPt|`_w@ZVvz(U(jPjs)frrAOl2Ub;1dWMFcu>t(7)ujC*aWH926dKGTAKkbKZMZVxklsh^aI}d zp*p!=xq1LSd5syvD_W&Apr~;>7gc}VsNR3P+!fr$zM7@6poa$ zq8uV^JTfncpfN>5p^f*Zso}{(qa^k(7zT{I?!s zB)S`ql!>eGk{$uIkgjEb)s~V1{&zT#iZ6kR@eO(OVIL!wDzdENGaqh8M+<90|N9_) zZ;{dsUlBt4$p(#NCWwc*(sfiNXykT2I|j7a2>1S3)h0Xna%06iXAoN?!B#I9HQ(JP zplYmuUlg7031oS?VCPouV-og7q=Vrk=GJtXPu<*FNU>b;a~!8XOwnb z{7CObpN=Ec4jN-#BfnU7=rdP<4Tu^H*Z>j-c4zVr{CU_{A zS9_V{q8QvIV0w08oG+ejG57|Xp(?uDb&Y(hz-Ra#ICb}YXL^Sd4X)GTGY}r`^^G_9 zNA>9)ilW|LLj zwmn)N`lBX}%MRC|8uB#~(h16K!)qh8CP z^KWIS80Lz}4NGTnS9p>ZfZ2;0D|KVuV2LXPfK)Xe4}<4IhtCdk6MXGn2mjv-05c_{ zMo^SPkktTe#w8w0d$WuX|4fChPHiWY{d~EGvUo zkuYOj8ws0?Qn}DMHYd-LLF-ENWM-})7g?k%qifdvBdqq=(m`MuiOaB+WylpxTn4mk z?E!#Dghr(wCq6cKpU2R=ox=TKf=`Qso7IW-i{$(;>6*qD^L$x(88uWs`|VeDRau1W zK0MAb(FsoS00vb?NPw%`^H4o#6LWa(smyz_g_=IkpdVTvZNNp;| zrl+3MXxBW;mG24q1FS2}6(&sSv)3_>z36hjv8qv<;-yFp+Gmfx1m@+|&hLAar)X8F z@bdF=4P`82JgpFPE5W>!fG)(h@yj^c++a|hJk}ZeWY8X$?=K;MM5pPqV6orp4VHgZ zDWBQ`m}rAJ^=0z8FX9#I=#){C{{X|BCI;rmGdF`ww`!LkCXQZYa7Zq!sTp z%PJtK>?(DQVytn@*V6Pz6yO|G6qk6ef3E(xDhzK*+_kl_gEGD`w!5PCO+GPCcwn)d zvyaxnYVn`|)dP;KjPZss-L!5}V!W%(w5rVR1v%XL*`MgKJ$3xZ{x-43cdr+w;b0Mn zTd>1<8H-K1R65BtMwO|iFvb#R|78Jwt02S|Vzf=wzd6I8oU=Fgi$JgXn108Ck8GXM zVgbCjF|j{N`Ogm0#uG2s=I;u~J7kqp!V9R&G|Ct5g&2S~0d`0m(!mUk4S^n<1$_!y zNmFfw0+Qy40u(Sr05Vhz-K}@mEU*PC$GIzgyakhD^(+u7!p5bSLpilPbSa{rhE%>pS2RM&@rj|F zlVykp2*dp58x}d8M@-BFe;h^+`7gq~yMo_fZ&<@`ngB-VybXB(@V9^nt`JHR7Br&4 zo=wNo1W79z3k4nY(>&yrkin&kii8H(rV-?vEA;`LaKt1e@^0$d0T?rzY3xu5RW0iv zP$N{aP7oCM?rF86Pjrg&G{5JI?jEr*1tg~69rjgBt ziIr{5#y!|9kBW^JuJ8ThuC4}xGQ`dacQw#Xn+RLOlaVE}1;>3bQ5_Q;>N7l|&Duyi zM*T8109_tAdJ~?-aqjROFL<}~2qqmqIfr-z82aq{&)s{{fRPCb}A>}s=4ngDlVBCy;=Z7GnTRRz+i!(+Li>wOxyu4>t~ z*9hwdG6vbWOYrkfG0Q@K74rTiSGWjKWRM!I!t%1-aaO8WBW%~7@f;B4k-DD0*a!$Yg%&MD^gK7x_&9L? zR?E9-3I)|6jS(;9V8qo#NQ|KPA`)+jr~w;&8_=GbUjX+nRg^DsHzm%wHnph{Oq;en zFda2Ed@UD}P*6(&y8EHQm%0i01W_pI3?D9DG6&qRzj=yTSQYVdmbZ@|K*|LE^$p>iu4=%rWX@R^f!9d@Z<7>^FrXMMk->bHGi= zYj{dfBPn?3q0Qb_h;^W6{kt_c6O*$@0lH@Yl(sRukt`p2&5Y9ws11g> zJ0|?d#1O{i_QRcCWQgYV{ZIrit4WV!LRe`7uIbfX=Vz7SA6Swi5IF{OsnH%6haxB+ z7$(O%vrNy6ru>;DD`RGV>I9TU!{qHMO798G?hiQ8-=2mILS;L|0~1;KQG`6d7V=!0 z-w1;5V2d!4uCLdNwWhLj?ES}hjN}W9IYv9F;!P1(-e2X1Ya;42PBz8oR#W8bEh@@_ zh4svu(3Vqi_63^Fi(>nnnD6pxTDXjoA9P|6o97dXQkx{B)LfdpINp-cR-Re=9^fQj zf#n!jDJqnPO`%W+rECP;bD=9j6@*Qu zo+X%n5LW7Ie#!bdbm-Q3dxrVGP9syY0|1&Hp751vVu^$H2b7b{2sOZO1Q$l~kE-_^ zvJJ4l|8f*d<;FZ80(Q_oRUETcm&MT<0f$@5rg1VPTX^q0sc6Mf9K=BcsgVAv2Sbydu2gU}(Ajh`=Zlo?_7K`BIQ(nw~kuv^oAN7H{Mr4b%7svRnT0-l%1=?9)JZK%; z8KVD^8Nja#EZ*CsN+k#W1LFuBUPo$U?W)VWZ14uvq`3hGK_Q+qQ80^^Q~)vi9`G4f zkefnYI3yx}cp}5bRCF@A-9_|em->Y!=HR?$8VP#*CZLW7<1v@S7}wWiwYkqT9YBla zY(f9iHSu%P8KX%x)Uv)?sDH9qsC^hTRw)swXaoR7K)SzLa7j@fuEq&OMStHw334#O ze-1l@T(n^y%PHe@qf$=Jjr{9Nm72M1RH!r| zT?7zL!QoJ-O&-)AlU|O{|u8h0Kp#(0y zTJ|80U6mg5rQtyYkcyr(>C{ z8Vyrv(_3NE6;Q)^T6_2A?q6)7*j$lG)RGI1)b|{rHMoCZjeHpZrT7LU_Z7>sC8l( z2O(*FjQ7R<}ld)0}xgTeXlKvxvEqEkDF%Gf&mrT5;gtj3tO}qZp1Nb%sE!} zuKZXWnk01vX;gJ(di#7R0vIn~w1J&@;P3erK5ACbx<| z7kJyu;q6mP`@tWSWn2yf7fhizB?t85J@GW?K11h-B2*qVKOJfVd=%Mt$RspFvI0r6 zd9Z|FRXbrWFE^K2z#QJR~qzq7#I2aNtU9tZwJacPo>hSU!E@hP6<@V%`=$lN@+*e zpD-bt*}-@REewSbnoXP`YU282>IF-WeC6aL)dmOczUf@1;SB{Tsu(n&emC?kX@)J= zb-RZTg1;ttQcylBO}TZDB5P!Wef^+%Zj2;1{aZ(rn(lEovVisQl_ zw6ORq#LMtr#E^Zaf{P(6Syq)J4>kinCe87~yqtJSLU-q}PETaax`riI;%Xfh-4P zFWNCuR-8#HEd}?Dm~tGIQV_Kow2I%`Lr4*eYAdr+fr)TikGJ=e&35t`$a87~(v}NZ z5lLFp5zANW0%rLm+Nye1FFoDC)$>Djus2e2XAGN6)9PczR`Z*g{GF`Q;)yEI?`75a zRhqh#K>W7+XKXg|`Nzgw@@KY$}}a+Z9uqGr>!7Q!$@x50lN%D z;XAegGKw&@2e+_-!*;fhO5nMszmu?m7dDCeXMacK+i|^Hkm3T!Tf_qq2*=zHp}qhO zdRoD)eb&;|GkETw60%BF6Lf)R0b@3&f8-h9W8 z9EQCC0Q;fG+JI@26vHOP%3bWs@LXkQ8_E~~STxaL^qvV0GO!=(5f-XK)NGEftw>JA zKu-Ua_*uYIabi_tQgj&A&nxg&-PG3qSc9@v8GK%Z)5~MVu{YA=)P@h(shy+f3F#<) z4_>)*SVDF@P;krRHCk_ ztgSpNAhpR@%Y^HU%}d%_J){~wskKqOovPHa3?jayzoBDjZe;FivTsnXq5yTJ*1+3K z^$)j93ph&tbqZ!t{5K?31Mn1Nau>6Kp<>}NdgW;Z zFQ{0qx)KRt!_~CR#OCA3a+dx5|=5vQt5l464^^8e(iBiR&kP zCERog`fR{(a{MfP?XCz}8fi#xc$+Pr=IQ3~c}Rv2)*6+-Z(5c`*sAkkr45HK|9kf9 zWk&dO0i~kxwx-LX&yWg|Gus=zy!ETKPlzDtjJ3A}- zzi8rz9=$sm^iDGg?mW59;CLnmsr9(-Nv9ejIS)R6-#*Ez{$FilmYqv7w#T(i;`?z@d#BN08WZE$QG;E5K>Eh%D%*9vD{%6~1APhU2<0j=Tpvn@_9lVoq zC_A%UA@Fl79o6OrRNWe(MXtfXhzSb|gnRFP10?wikgAd*w3Os6j zZA_W|L^}Tjqq99N#Na%tsi>505#35gB!abtA%0o-@v7XC%YW_1$za@BO{o zPCJ#BmmJ2pHCq706TSRQRy^MPx{#&?VK}hK?dU8$(DSng$I$D(0C1sq0(SzCcPQft z!Qcg0{Y}?xBK+1QuYyx~N!Q3+?%rc@oz5g}Y&$u9h{lLUwkuh<>B-vIJjiEdf>5?+@SZ6hjMmk$`hfSlyZP{^mG<0i*6d@%{uMRJh9O6Cnf;yad2 z^DBNbhOwT*5TYYWUzdtaTA#cLb%v* zsrMJXG7hBh8FHrxr)x2)9*tTMj$-ya+ElJ*C{IT6K@E2DhO)-0PzH4jzYghUOZLWs z6N{3d&d#Aq3!#(LqN&5H=8^3#Ykz0d;v`CcUe%>w|B^y^AX?aWR#@^yf2KXs;!Nxq z{WH9ZZXL?WX2xz*D$9tlC07EW$=(vylXY0!+T0^UOZDG7N5dL2S4C5t|0PFH2PWEp z&|n|A)UFY77LVa$Uo1agHf*@=sB@r$mg-ccv?V_Px34SR^eQ<#78LXGgf1&VAWcaA zo|mh(1$kE?xAog{nA4T?`DSm`Xl{-tzd{i>YcKg$gG{2*+q7EF=|>hbHMc!9NWWXW zUlciUPy!HF5XZ)$pDUf?G|J-qCd)U+OXQ0(m!cFMUCOX-=J~YoEuKe=NYav@V7_Hj z4|9754;PS&X|+?wDx=;>Xw{%-OPM;5E~V?oGSAr_9a2ludWJ5{I1BP2#zOxGaJ%C1 z_}QTLn$U&M-Eu2F_aBYV_HI7{vIDzH)k3>na_p0r>TUAoEAPUv$Ed$&Tu#$yRJg*X zHPmU2(9YR`1oM-R3LHa5l|B5uNZ%48GOzZHQiU*R8wrhW zEIxFPUb%mZ0EjFEWPpH}lahgsSpA6qB5nHbn?38z zoNbE)ZtfIP+=h10xfhhVySA4KQP0~ia%rwc8mgMKB8d%v7q%ztlTarbe)4ktPZK1U z&f-XMpU)(bM?7Ly-u}HGXlC)p=bOTc4iFajWSF!KPlKSwfs+nV$Tp=T_LpuIzQ}+_ zBYP1_%vD}J2)sKWSstK@sU)7sf)T%21NlqMlJDzm9Gh4;Six-I)P#HJ?f7&hpyk1B zK}@CSh-y0Ct3yNj_jVUxGLE^WE`Km)uJ&??-5GD~>!9>T#u#%z0SSW-C|z7L%3;R? z6inxHStY|oN?>^#M(7kIx9)W%dAm06JS&`l^q40+G~*7>0`g_30GX-jj!bktZ<=BO zQV%Vd_~gSoYNGHI8pk8>O3mux$@P2;k29|}l2;p=R{ChR5GxU1&+d`jjA7Gxh*ZcJ z*P13N$fy4OIRLdFY=Xxll4gl3CvlEhv>ejj#j^wN?mi5gpicY4_u;p)7gQ3DtNR3K z1RU>bv6%?^B)9|W$=}B!PBsgK#4m;uUx%!-o$)fyrXFw7lPe~-$Vu+hB>ey5!DYFo?;t>Gq~In)8v;qRN8k`GEcGokNmpmW{G0@FVCHJrs?@7DC1BKkaTh(m7E15z9Hag~K`mL_o=^#~pvg=c zb1xrUBWy~=g7h)L1v*`FRp>+oj^tZ@>}PoM*3=u%#fr%~EsbT@1lV%#qR;N1dEJz4 zWl4xf^~F8GinagApdFw{^Bti!lYRv{6ngsFODEjkt`qIE_P0AFpW>(b5`f1Axov%Qon za}W%q_pR2nI&$a@`8910G~X$A`zywr+N{8ItNXB%0A{sCZZ-+}mO)AoNhk3Jy;#<^ zSb;@y7{j4{vBuMW=?lPmf+qaJA1I9+0{YUz?DWrVpkq{9;P%3%VLc!)JQB?)XN;++ zFgCTdjW|B!3tYslQXpwta%G-uYMk^0TI?l6Y$^XGz`qHNe_JLRt>56Z^( zQn|(VNi|S%Z$*3n;PD9a-gyG;+LKtEPSa_vbj5ud zt_;)5aVr6fI8Bs@3wEfQ*p#jfv$~A7{H)=0l+fS;K|p6CimzOvy2u-iEi>)NN+9^&X$$J zQ^FMG1+&p3RChK-*;_4n&V;H=gi5d0Z?IUobmL^M+lQBse(8djELy$S&T&5Ha}R(G z*e5hCgY%*V(4TM?-^1t-QIg%Bg5U3+UjRBj&P(9Op%bHRW3_(IPThW~5EMVhkiEt{ zV91olc~X_PffVIji?couSUkOul$Kn5@fr%)aL3z=bv{{_66}yacjw*W`?N;&Q#{;h z%JWpll}hzIctEZXIp>l80YEhk%SuYt6<|IlzcnNBq0-5EO?PP79a73Z!azH<>0E{ynDIDACF8=>J&14DDD<2dx80GueR}rz0T2Se46B2r|6^LQ_`EEo9MJPteX4r0K3kJe&`8Z@_jGq&X z0`&2~$qZCXf8s26as%kP#`7Lk#%|{OhLZMf^)?~Lw;8%{x6-qtX%Fl{kmDmJabJc5 zxUvg*vsDZ%bmCjcwiW!fDC)}C+)kpIUpD?(gAyc~1R zEKCbnAsxz>qQ18_J^VqG4U!b3V(6k*J)xc4U~02p0xFJoG$k2b+A`G)^@{D(=2 zrWS?m4BzT=d{qM9c80Ht51`ANDU){4B5xViSIbozake_UcJjp18#=0Aprb8?v={dt zO3O^iP;zj13^Ywk*ssO#@w>&~YN49$!$Xg~gk_{?(#IyhK#OAY&@=5_G8R>-LFY?i zaa9+cBzGp}(0eNDVl?-I9cR8ylP zYRT<{fWKkSkHvqGc^%M^Tt&VVO@rdk!IjBv8*4hNaM<~D#ih?>1W;db$3Kl|A4%KJ z9{G<6hMD59%_bLy(yy zJ0Q}&qjsL<{%n}}8MY+ZU0?^3(HCTq)%p3m^F=VL^$i}Res&W5g=V}NdNsxIGn z&n-|B1}_)Jg?vPaChCdV88IcnBV*GaM$mbF{@m;L0r zh0-tVA;%0ZME>ntMM_MWZdD~|p8ev5pg$wtfilh^chIl?!y`I5I5&pY01!VpxzeR$ z@jaEQqCY%zCZ7|^@Wx{cD{}sHQ#!iq?rw5Rf-+?c=5tK)M$=VW&LEW$tR8U3#m-GU zEZ5W~bn$;0sGX`0*_q{zex&?I@gi4doE!aznwhV|`1Wz@M@^I1+`FQdURNK6JOq!7 zNJUUo8jb=QrEg4#g||95IKL{;XP65PN2&C;`>?foy!Q=zaZON_H5rVRc4AbFN?xS2sM?G|=5Ka&uqT*prz&~njKoWti zzk+%tmhB%pvYP#qZZgQD=CV9dnzL8n zN{iaV)t#wuW>e7E(cBJn>PSBaniVzdJLd~LuiPA8YmfI#-nM>pYM~z6$m|y`HV}*N zV+KaLQ{j)*w%~J3)QSXuwgK{6!Xzup0X zIudB%D-+1Q?_$~=(M?LiSrHDU1 zC%ct)HE=dPPRhkny0-^pYm9vRHd`|`Q8Sy9;Cs6Up}{rJIobC5G!rWr*xykMrY{lz zyn6aTmvLv}QZiwBs;O-s`qWog&aexx-R(vvUuXXr&ey0G*O8Cil_5hH6UiWv;ASDj(AR*k?2ewETKfCUTwA zk#_zbS$-jr(G>yGkgPx?MxS3VBELK$cl2X#u&sShlO0&Fo(NJY~b zUMdYObKXKqI7i7P^&$qMR8gkTx^MB@cQ4Hm1I{(aK z=Z0l;jxdfg&W?#pg`|O$Tfe_MRUNo$7u3)O1kQX?0RYdK#q=>kTButtsx0o;|%E zrQd|pmAT^AqbEwMN#2fWXk#B@MN?0q4hY;40-#+QM*M#`n}I2POBgBTphCa}guCKz zC0To1+Oq+39YF7%#uGSh|FmZk)H?J}?qgRb((IV91eh>l%ed#cD(n)uM50WAn5|gR zsgvr-t?q9+zS81UKDo@LUnFw?Qq9 zISAhnQFO4O|ErxTc)k}2{faZ>BD{6ZlNF@>ZZf1JPTus4&X8Ab7Ek0+-!LDiOf^PKR^nOoI@2_bB0@-FC>p zoz{9g9>7x2!@D{ZIM7djw@ybP!ruD$TPh_1xf4sWs1 z!C{XJCx&n9IW!J#9$dz%Tg8E&0i}8Ajv=m})QSAEuJH>`yFA*C@q{eo1%Q`gVRbNN$()}7)akb_la!WZ`!*@!SITWZYp1+FV8;TSjJk2o{Htp38hYz2MZCuy z7I)X5abv637jRj7>hC;B@b+x&ehw?8==5`5h!88++?Z6@cD2>{ps%naWV6OtSwU9kd`XY zMJ&iKn5Cz@G26G#%+^QHrRIrYmN8!+JPz*tUYCI8a5J6C&qRf-t<;0~c1nJ+yk8Nd zxD!rxbFRQ+!j{%#>LN`|-2u#AdIrEe%zl z{nulB9{LZdM0-J{)8Fui(SLm-GK_rFJhHw}i|5hdKQi7T2*#vhITWuSEET*5%&P?Y z0y?+)wm3IvFh5;S01&*~)9IU-fxoUwOhdJkbg*)2p%>pzBI}-o!js)-yG~JAM+bV} zas;0$Sf-C>nw5|GLLBriX+T4T>d#assY9QhVUTfZx57e|lKu0Q*-84V;*M|*O5a7c z%I?M{>VZ88Cu}X8l%$SgHwb03$?N9kp7v`Kg*IXl7Jhsw;!0)^_P(oS;~L^O@Om`x zjoZoinH1i#`y>Ovd8{HXqBFSUVqXsbi=u_}Zd7x^=&;r5~Y_ z1*v6MudQo6WNmgRVxf=P6QKnS`jBlZK@QoGa(F z8dJ^EXc)&?g1Bpy6R!&0c31{wBD8H!C0oAh4rIoeWG@%pVEtQiFbX5ve-OPGC zPTE#=2#yuJZgZ0>{V3nRVLZqG!Kk-CDn)B-k4CrEu&0r!KbbWXk>^Ut+bF`oSji%ZpL}EjP zKPY83K-AkHNADoOlUAZP^i7C07UO?e}4b|FYoA7 zytP&Cnb1lNDKwVI*Fs5B5*hTN!sfe`#iEVdNfG<(@!yGv?NYaY zUFkFxZS7x{Yql1LU=osZKb&eVG%Vai`Yc5xA(LO>aIMIK7za90vk3Ywinbqc{|^~z zC$gb(QqLiypA1?xPpHGM8NTMlXg=oQd^BdJE6qs4vcIi>QsUxddFed27y)dr*;KxJPzF$)@%3{N9~7Xasff>SDT7-*yN85|MA zS)?&aGm4Oxo_1mZ<3cEC&Mp1ZsPwc#CF<0`p`Q&C5fS4?1a#NpKAJ7q8_*#sgEny# z&UdnO{=)6!jUk+>7~%McqcX#wKZ{C9ekP6}3n5{X#c7pD9-F*jzl5*=9B-3bD<~ot z78BXDM45YSRn6wJ@bBj)p6{gjAg2xYIAO%E$Sc89Ft&tq6|rl@WiWU~bfQaf z(=uIqMBuTf5z&8+we|vbn^$|S`8=fK!lR>88Kf^Q5K$^o5vAQU#@4JXuJv^@@WifCZ4UFv6KyZb0HH(kR=z5{R4MkhL`2~7wBWSM%@m`&*sGCCT5WHisfNC1+=tZf^K$_Zw4kUt zUq(73qM~mwHz%6(NqYsJRRTK%2m4?FN^PLlGjg`6Y|qFIVd&UlwvjmR6Og0{!M{&H z50XP5v}WvSLw4-|B362>nSkNSOZvd!YlZ~;z~e-Rh5C(UxV7b1XXYzI+qSyJ!yojk z)-G(Au)IPK!Fu7EdF*Qbd*Q0U&}orMkUKvwx?!Dljs1zrH$@!1Jhr(z10wl8)8N4FaX5jeK9ki;|nrxk2os`!LpPIo14RQGG=gub$yi?N+{kVCbK z6IHPXFhM}N(hL+w8X9fqfLB6#qLq|8)S-%iR=76jCKESJze zBoGp+1qX0EB6uD#`GXa=*k*~=65U>&T=2uBux#i&QfodSxR`=lG8zvTK;tQ{)mv9_-78)(^1iq1qy zI+9>?&onyG1A~glkF>~qzA#P+hiagP4KE4c z>!ndn?B_ z+^kQ!>t^*_LUaXBB!PSxBKx$D8-(WckO8@#F!iz9_k<$}06;M=&P7)EK#V%kDIpgB zh}J$%;3Fj}cVqUe(T(n-1VCZ>x#kUH_DvHK=ByhVShHaZGW1ZM=_y+P3Kz;45_X)M zJKW|Y|7I%H&buQ34%jo>wBz0hwwVBY;p(VzP-2#eipBF zU)^j_1ShzY?}E27g({1DK6>v^ zr^U-{C+VKqvFA?RvADutF;5CFs#m7`suu9}t3EvlBUmjXVxd0aYme$>eMOZMBT1H( zTRGN3$ChQDu9JA8M9@3z3J#l{pO?Jlq#j&nqVAfximnrH2$k%wt$>d z$b|pn-7ZIXF!^ZyLlaKcwn%1OYO6=W&ByP|j+hPxz{|=B?M;nL-?@WCuVSiM;-Dz= zo|K!xNTSaKQ)oOa<@*G-5~2+e$W{icKnY zH@B-eh`2t9>*4R2>ytJKibx(Ia0bxv?o7FOw%qXceJGS@5wWN#TEC<%Ub!xWL>@Wb zQK2cAU=&wx-8NBG9r+mbP*5gNX?06rmWSnZ`Nj7;M@{#^=j!eh6%Qup6nAa*PKGR< zBA}PBWw2-IE^bP0FSfB&lb~rM-{>_M7qZ8~1nXwI6&D7VzbJ6dTF;>H#SOc+i2EJ1 zuRVle8*?8-dtM-*<@+49UzEByY&fFcXM zJ&^u5#Qndh^Kg2z^A|Fu=GAXoG1d=CWIOfZ1xz%+mS|lqy32~#jaM3j^=~`;g~I?) zUPg^mFb>%~u9^^SWT9&38PjtK3zs-sd_2?!m4p%L^=5X_cU>@fU`0YuZ9s(Xl!;%* z7TCqI8{+3%X~3>XHs|tE2Pm%nW#vdhmq1?Je{=hI&{@i0(F;a$f0_}>Joq2w38hO8 z(Ll5ON3%TyI*}BlT`8=kX-M!2u~C5_=_))bh6^U~M(o_hW(WUS)j&yX(5# z>rP2(7jW%F(xeTXo`t+uuzp!Bn-Kui=H&<5%vP8&by2fgUj6#-HbM!a+<}<8>WmDL zk5)_mm>&Uf+okN?9CQ{4G)WxrRGu_}hYeDg)z6$*Gcl~RiUh^Pw=<}0{@%#U6Uv;YZKmTy({JdhHE^3)M4>EMm7Oz z0l8a{CwQp>bNvfg05)R>LL8I^MqQzn;@lN9${~e_@b|9lC^rY zJw;$xBB7ClXMG(E{pQh!dTc3bipyg3`h@X|WI_fc#8m!%gf2x)HyorYuIs6|WK^NP ztr7njOgh$ZS|C0%7jVZ%-VVSzi2y~@${oB1F2Sf0O@_Xm@1_60IvT*{@Gu_7h8OEv zmA&r9FS1Tcuz9Rt|0c#SG4@w8;1h2L6n-(Pj@)dE6nfk|w`Yhb-e4-^vFf#d2y@$| z{>=7CwzX7w3ifu9<*`3yx#bKz$?0hhyn8`l`)q6xu<<6{od&{sM_FwuXm}ZW&PnDY zbkdQ6`Hrk2SuI3IP?vgJxFBMZWK5!G@S1K-+=TsOJ3|JkH?5i>BLw|O|KCn zE(+omUz<}MovyK)x$@Zr%T4+nO$nzPh(fpm<1ehdg_#Fd1gqR>oqg8iY6Fgugmqsb z&4&3Q*uq4YuWUS6(vd*iQ|j+2(Jv&yuiHuCpbvn`sw{8sdp-@Q*rbPM$4ryXn?TOmjLAZN4os=EjFS;Z zR`cQK?Z-Uj*NA+BUj}uTJL10H617;Ea(FR)CAU3)Y9L|zWw%Hz|Cj@mjw))S@| zx|=^;LJLk_CIqmoDOWaeb;^QC8(o+d-2L5TlTy|6#j28ORWc|-*LhSU?L>XFaFK8{ zK|JZb`*~4}TxE*4Y01^6Y5oF8W=IEN5zs(d{)>d5E%-r-y@2{mv;H*@Irhg`)`c)A zB{-4`@wZ}_*VU&1$Mc`u?cpc6X?wH~%r-Yt+ay6e149xHYu6?AU}nW~{ zF(-yruG+dTh!uG~p4;ntBsM`~_&3KYLm>PXH|}M|o+gU?=*;W)%_V)`J6M}P_;3%X zm9o|GW6?Z3BFbDNOIS3%PRd+TJKtVm4}Cm8(zc)N3RX*dXwan9Z>0YNjZxNJp3*h{ zCi0)7@Gv$={~^+E{#1CJKm(w(8`{RBela_&`SReAOr&Y4#d1NaLdIso7Q1A=%YCsX zBH8EU!dYlKBOD+c4nhsI@@?yksOPQM3ESm{+1Jm){NN_yr&DLzM-<>?uTT+`32N1K zju-`V>+orfyDn`w=PUR1FV>Xriv7n`KuFbYGWx7O%fbefdA*x>+1B?c?AFWue*YY1 z*?92tMa6h+(VIu5c8vBWxh@AsWvXb;G%FKk2_?nNm50?To%&NHk`d1nZCr=$Az{~# zrvN-*oNWOjC4=vGJ&Wbm75GoN$zT;Wn*pte_AMdRx5d-b+Hc4$szNwAjxNDdFH;bD zhx0VX>Q+49XxgdpQSSo`T7-{Sc1m*i+s25SZ~VJc%M~@U&QZ_FxCZ|}i_^{!FOORm z!Q6%;%%&aygDC7zEskcGk$y^os8Aice4ExhJx8ySRV-ve7;c;u1E3i24Xk)H@kI27 z&noV%b|neHNVLnvhI;DbUDXXOSLwFfB2kCP?FMqKQCEFECV4uFbO#s8MAul7?rHkc zCNt;FpfCjUCfX1R^=vTI&xE?XT=zY6vgh+?tE6J=gEa=am&Pu8xARps`ebQw?TfZ_ zPG!7D`s&3Aj`?=~4_SYLO#7_p-(w$5EYT^mN1{!QrpIEX-O(&L+w8dLGHPBywfaqm zYvW@JnwLo_*SF!ojQM=H2Y{&aH7r*^3R=RN=zb)6Mit~gkw?nCt_}+_C-uV2uNrDq zT!0dW^=%Uy_jd}4@W>$Y=A8UGZH5ZS*=wWLvs85$yX<$f9=JzB8DB-Xba(u>i`0#; zY&LGui#K@Oev>Pso-JvPU0oIgFXlDDBD&wzB_~CQd9=BdzXQnIR?_;-jyj5J?an}s z@l#@h>w+F(M;VK*9wmqVkzz-qsmHzQecJHirROSieGF=>02}bPK48n~(tKN|LXeSs z?HFCx&&WhG9E@2DJdi2>x=UgA6ofEDgz;Xnv0Xcj+7VtT|G#-G_uP~&<0eW@>6mmSOm8~4KrIVg;>zZxcEb)3d)9q zwWGfrMyj8m#Y9uiB6XIP*y|I$s3ClU*l#nHSoB21@2mt10@j>7uZK*x#HdmCV01{| zx;_NagjGpKa0AX80={N!s-Da+%SjETpw+}&G7T|b@la>$GUqg&c1D|`S< zlmM5vHcj34V~DR*lsr9^FYnk{d*RN~0Hq!?pBIVINq#!iwaK10_rKXS*1|B8J4hC) zAZr9BNctb0a{-QqnShMJ@eI(XlE5hSrXnqJ=I_AwQ% z;6onK>Vv1bjJqRHm@-YnWv*&m4Y2(X!W2EMh=Nz>bp2WF+bE>mfT01qcwlNbe&oz? zOHql3tcu=&8!!YlQg}}Eu~qNy^2sIikdZ;+pdo(FN%^nwn58pY4B=plt)v?(`$SJR zZ^`X#YRP<+JW&9+qH#Mk!k65$>t7y0KS~@g?U5>5aN4Sj5{_`ahPt^y++BaiI-(@5Cpuvkoz|=-`AT-DqLW1 z8@q95+3&*s#s!B@uW?NAc3`Cq!QF2BkV4rx%ub46phevz3|gP48lIV&FjsCpCV1Jo z!rT_vo0;${6;;?`TZt2w(mY~W9KY!`^iCGPIXTDiY_SF~guu!@#D+Y|+g$(tLRYSC ztN01jbq}>i+pf;G@f~1z!Su(nA8jMp=xqCM^3B+po2)Hr%34*8<0){iFu?br+mR6> z*CTxfd_S)rzJ;#arK9|Z3+4H7cw;wD5T;8e96oi0Z&Af4B+bkKw(vj;O3E{|2hUU1 za!Pt!x z*%Y6?8EDDL`H}W@m-UgB~Li*#XmWeBZ@YWQZdbr=9cGI`HPM6ha*=dhc zSp~r7oge9J1ct}O)3VRWW(wR#r<=6qmlZng`%bLk*62#5tOU)9$yMTo@g!jjY9%_7 z9B&yaalV7zE9@zkb8|O_{%f}LLQAD!tiOsfiR%*<&k8QPZwSJGFvQf=6+fup{sqXC z{rq61Y-p_9WoGD@UJm#1=8}H<|$TQQhVK)N%85wS*xXp*y z)W93$I(eQlK5zjbz&l2bzc{VX`*-p`>g8(VL;hsP$t*dMylv-+2i}9nQuZ`susd+BC7%(l253iB`4psm2YHA=Gjja#(x(7+Y6%?jzsxt z?pSyT!TlPkbA=DDx1xJuSNE7`gJFO?>F-HYmMz_VVSvj(&j*?id~`g4(*~Udu18N> z#!&iN7rT~}ztEqt7189CDUbL$@AStLKEacO8S=&)7ovQu#d7aG$UbyVvX>&lg2M2G zdC=f!bTZA0NPIYA7i=DD-0zW_5LSL~oiq#8Fsj(>w&Ly&`uCKX3gT=XO>?%qWwKWiLoJ659g&&W=4F`Un&FNs6W~Y|JDYFi3J+b zC-)8{^BPq1I(QO20O#llI(7;-C(XU;*!6R2uzc^g;s&S1iW+=KN=jHM!$3hKC z%aA9}`6s$A^GQtT9K-EcWT(9$lirh%!)MTqzdpI2X$-jnJQWoWkF3 z{7AfD_TD76Sz_LOp7tRCHsFYx>+?Q-hc-kJWRh=Oy$!VYopPZO9oWJ&4K26C;#Rle z^{9?eb-=R3Xl?%-D-Ma#(||KV?YCJMpW5I`wChU}#3V%8Rk6|UKeB+z&0{8>`-&*i`;`!_ zkWV$iUg_O&G_@Hb`v?HWI7E%o4)FR(f|eX;jqHDY=!4%hCh$Enp?JUB3vw2|y!GHC z4JzM};!(+EqbfcYfp#C_=N_pkcq7ZCzd%Fsh`721x>om9V4J+?6oNGH;A1Ug2OG?j5Pa@lmh!b% zzth7H+2$b-F^~tLy=@1Qk}h^AR%k(~1XB|3Upmz>1!IsO zq-&op4)0>{_XfnN)d9oCYG!`$tcwwIN4Oa_xfwPa;9v%7+ok?Qh8*nsC(Be;No z!6;ryUeUsqlIK*IQ%!zR1swPMyOqkluJP_q;&ZB!JDfx|-J4|D=zUKD03SAxFpk(^ zUc&|2&4rGH(TIgNYZIzY&MVf}FF=r#-uOXc(U#x}CZ>VzWU^K?%>{u4U8R5V{3aa4 zE&Q(D14WVntbG~+{_34|i>4eYV^l?~PzR5hxk23?#Tz@0 z#Q%(KqrOQ(#rLFB9ZSs4YMhM80`mf@*31g+vU$_F-NFj961r>{SEBz9%-MPEsUDd_ zC95iLQrcGlyu}?|)`}3dRf_6jhm(T=23CU_=0Aem&f@{GmM;Qf-)^9KoYqJ|$lF-l zhwMV0w2nI$?;HLClJwx%NX*uz5M)EpUV4g38``Fd%9kKydN^VNDHA3B|kiuk|Z9Cg&d(u{;cHY+^Y(U%kKH__DG; zsQYF|IJ7Q##@Pu)XjqXr0TrKM8hg}NP@sg*yyODWb{1B$&z`E zszUc4?=wu5kP-y#pE@_{NL;Evy^>=9L6dT{nqtrH>o_B8RptZEtnBR1%9=Q3U9M_VBgZM93cA^c zk<1#OB2Q~I!|@i@qO9THFo*t@{7h~~Du;b4JmUiDTd)U}UW}RKwfweox6o*(=!lh| z`uTa^^~oi|GoTX_(<>c?QAJygF>_h2hdoVE3EnhCzc>gQRBBF5@sH&$R8X#H8etqc zUNM`Twp=j7|1acj;_g|mB3v|FjS%!%sr4>6}5BLXZ@RBmFC>&H*& z7^V+0*qDmNRJ#nlr4&yZb#J+*3BSHJyWj}t9V5y1@zGQRuSd;jIBYc{^lJN{_Sq&5CYjs7W@{8HiIW9@Qk;Er)B|E znuw{0?{e7dv#SKJECFy@czk1pyuvpuPF+A1IjUceplLu9bI%rw=WhP^!G*h8iL`;@ zmwgM>p0!E(p=)=*`+Nl{__@HTR#(-;RCtj0UoXr)p2hJ^@m}WWz6Ttz|_Q$ zGuY)4*TqbJ3}><;+*mWz*f1aXF9E5VMow>vY@h5hK!K1NksEC^qIjit9(PyOGb&x;io3E_XGjS8*9|)*WE0hvA0A+ftgh#ikBB07Jus1UW~g;ELoQZ`V^ZY zMU7bp)LF1D))b(c4TN^=VC+Lou@;aJ)gtkRF4{dlJpj)iTWd)>s9hPKdFBuA8n z8o(tfKd2Qed4Uo~D1#(nixgF?0@5~t)*f?V+j`g3UG*bWntDn?lu%Ej;k!Uh4Rt}v z(?{ZGrf*{p-UL0!<3*{}*>J>p(~K%q_>CG9I`_4hTG!F^IWn3-D<%V?G*3QV zBkC$LN$-12@d4#c6A1*4RyQ2%gw14=64|j0VtfnXw3|K~MOM!u)6-^>+_ubilkph&^G{HL#wfMz6x*nN zr)u>jDXh#qTW8Kto}3I4Zf%*b#6CK!UNP=2;Sr$Vg(ik?+mw8iFn76AO^3fcKL=me z{_!unqhu&rIcwINoIKEcT{Pnc6qLT>YO)KfIGB@h+emgm;PeVvUV2ibD+i_^A9MWV zRwKBgkZ!)9o5tQVY;VNEcdDqJ(3@RGH}91K8HtQLFV75P4! z=}R}A_)(|50`Q*5Qlg`H6zyd{!O;=ioEu>A_9Ci?H@O#CXaUAnjbF#$!L37$R+zuuZ3b=1-K=GeG& zD8|bhKpc@^|6;1OLo;FlXSn<0g|W~U&)KQd?>F3K384uozg>;3vVyBuW1d-4?&s24})8{=s*?+rKh_qTdf zE#ED9trmJ>v)0tz-zvGG`6Y~vd}1+UcnOt0ur&}O`9eLis!lu1YFwAV_V=C&-CDs= zMaT+s7nQoY<-&C5+*^g;Qr0C$9ySLZbeG3Exog*P-;kE*+GvRh$#+WzCkRd#gF3?{ zMza(IxiBZiB`rfPDUdbz+ET2wOlEPk;)`^yu7}n?7mK*|xznjI0N5d&@+OA~aGMLK zgfTd*kP;v{^1nu1;6dz|;oMTrR54^5BTOxstiYDgav$z?>r-Bi&6w?sK*$f5}c{9V$Vy}nGs36=d7kQU%#4!!BdGC5x@ z)4ixryc)P1^bsLx-cM*AVps*ExS4F%voGZJeNb~r~=P#(}4r>rM^|FxuLP33W zDZc)}y!OW%nvloFLyGdfX@9!`l=IzlBxo>xKJwF3uGCEIL})S)s#f$3m`<~Wv}Y|l zfq@rM=im1~KJR+s;;nAJo_E`cCNyi6n+9NMiTOc?%ZOy>xQGgf282R6CD%M0!G`rk zP)11a@%`+rt; za-X3+Gb$>*&<-$2#AjT?6ogA&pU@lV%c95okaayF{hJc38 z@$^lS3;PKube|ll&YNR}RXxU`-nW#}>zf*z5iCZuQJj7dgPASbu10dj^u#67@}+tr zH6dfSBcv;m*YczZ_3cr?;W6$0({bsOts*(HilVVNv=!sg&G$-?Jt|1F6`V0Yq=Fj$ zWDvR>FsG>`(yBJt&4&`+xb~%}7e!}~)(y1S5c6kx=1IYn=z3&q9#$nN&A$-TjkE+m zxj|uvCq%d1Njb#&Ky88g=J6Hbis^M^e@CNCgnw8V_;7ifjaiXf+(cmPS;~6-2*9nb zbw#-&T3&{_gZPRu5(R%HQe~G@;9EqmIf~63A-HZKkJ0O}PF>PIXtrzX1tuqCpOUz$ z`a#Oh>7owwzqMkCfCM) zPhH7LLPtQX8=|1d69veJrKDAB!(LPv6?R$YSEEzV#_nhMN+tE>g0EBGU6chbe@`fe zR7OqU^mpmk2b(c2v(q_2;S%Urz!uzq{~vHUlAebMzpf(sGGM11Ak;L>Y07qyBs$Y! zA<*OiU9oovLcL^f+dge~P1V6nNYoSW>r0qe^vr(r-g}d@X>wPP&)oqia(C}NR3JjM z(DnY-_C6=4vIznfXuZptv}vA40}pTXa{@%m`=E)R!aPtD+YE3d5(A}glNFq$9C!|f zhOl=WW=ZHCYd1D<>MUkuvO8knS5B;u9WD18NDG6|So zW_aCq?c{fo)2g|5I*~E&waYU7ziLXhn`*kHc1!Y#^m0 zda8z)T9~-!pEt?h+W)_!w`nIJdLUNxT__K%yv(4;cx!aC1?! z1;V%xLND@rk{r4XfBkxtFI6TDM9@sJ3j?8v#4185!0yN#6jyR19$h;rz(jRGv^aFy z--yLu*2rht>RpP0!9qv*da=uvLeGpvp!=!V2?U#Gb*3+UZC!M#Hsm|Djv+U4bsv*+ z5y{M{AE^80PM6Our0L)-0|%=5=rz6iTawl6cJ%Iu^j&HbE_xFPRGo-^97OLC7fMtf z5IKmrJ=2wE%}_v z#;@^JBMvEP50ct!LrGuv8Eu$F!39?Dt$C=V{l#K+G8_hEGS>ppH>O;G^oDTVHh)G0 zaeMWvLaN`~7v+x=`Um9Oodx(bVNp8@ds?93M&~(v#y~zOpnVaqMoxBq7k!eU?q5bS z@?@8dkA>N(kj8K{i`hf)tT9uD+EGz`{6&nRU)@(ubrEU&ZmoslA79&m7NiXBW!0vK zTb@8_g1I2*wNi!CX#WbVf#G z5e1EM`RnRsWaetT<|@q0Jr!crYa1la6Sa<7!-n!Fp!(4+4EHO3*y07mL3Dr@-Zd^e_r0&%9#6Sw`6X%fL%(s`MrD=rdq30?D>PbN~f4yMG_>U zN7;{`$P_?7I@*`r&Xh%LrDwGIL|v8QPM=*%#a~io)Gi9)RwUmUlK9-Vpz!W%XRroR z+<6qmts@auFC*Cv8Y9B`GDe8PD7kWSv>DPRMrfDlca@qH3M8R&Qwz0(dR|55zj1^e zZ(dVj=AO57H+a0;#`POsvXFs~?$7x2a_5}%zz?a53XZytycQYQhdPw@g(+;eciiCx zZB%0T!L@teEel1}1m#(XLk}Y98nU~c)E;!YAS~w|iwA6o+MOT+mQd9zFqex_ll?7$ z)dtS%ja;@j>YCC+Mgibi$Ysk%A>? z#l?B4acY5Mj@LB@tw`5n2E23MKpTY)gT#AYeofg{LP*>^|BH;9WNLwz;BYg#D(SsL z1+sx;lZW>uNzAs4W=nOqHdcQia*9*+6Uoz=z-h@Ou8Vq9OJ8!a?mWjcF4{RoA|Tzo zi&M#f1{8dEk3UR?h)Sne82CPRN@uGA0R#W7st9E!IBD|#)@`Y^VY@7qIA;SX1ftMb z-_N&-g$;ligdz^A&B5Y@Y?}D2degag0O-xS<$-^Wf@bHD%a%NjF^g4y=BX6>q3cf? z!6x{z?iK>pQx+PS6%5?CtZBF#2sHKJ^tsWP4Q}S()^GCE(tX1XFee*hF=1@as92BS ze?PxmqHtwX{w|IbuRMoJC1%gzsLtsIrKZnSXvj`U_4e)PTgWPfxtA*{EIA^3PBHU` zEZFBWX}f?hl~L)KFuVX)aMIq!KSk8>P^3$wwA%ojGA1;SZ^6Z_0ENW^glbm%dcECV zm!`XIy-@aZ)a#do&pYw#dvNl$fR!(68t?;-*@ii=9g`ANQ(Pj6g~)EM1Nm^&Ng?)W z3{SRo&q+k_)~HTf1@k56_$dfPVfqHCs(f*Uy4N6Dt|M#u#pSlW1XeJ+R9E-kpk2|E zQR8O&M0l_gN*5KOkE>m2Q2RKKno~gUvM~Q^HJ5uSJ8}6kJvH>o2d=itp&k}3zM>4D zNApTCFj!~OH|mdnsoVN%Ur?Bjut^&5O;FMDUXK(nvVENENBwI>IA_5QbNItMP3g*?&)$)A->;nRq(qDrJw;6 zj}d5uMITR9Ais$*TB>7SoXxX1um{jj9XvTIivh|ePwsEfMJY5;WV2m2yCgPYV7 zvU1zdj^_(xgE-XdGK$OP?prpMp{8&k=V5d-f-DJ5K*;fi3>H`3U*7>tYdKBvS?ES@ z++jiX$3Eo9Tm5A>5|fVG6c@|LYdqsjB(6pNtT||Xqig8Nfd?|+2@1)FP09p}vM0cX zuZ9>j250jdO(zaI^Wf#JAbwkw{J0cbY3$Lm+8jE11m9H&jj7Q;W`IOhj;ot6nevr~ z7-PgDXqYatdbJq%%EnL{>h_!^bnfY+e?66kVJ*7!W?b!cVt(9X&PY2$umSwep~{?# zZmlRDDh*2p;<~4uf0Z} zQrVe=;Ntn+0t`FVwaQWIZtm%GBupa&<7)fUU=IY+8N9mX zQZ=RzP&DO6$!AUcJ?p-iWW+<6v%eyx0LNrc7T%Dw(h*xl-B;Qtwfp%Hf}XN6N1*)2a}*(=m4h*CZv{^&)0Z9>_r`J?TCPYUjLIcddJ<;R{I zkKhGY4J1_rz;BH709sLj&P!~@O5K2WmEL!__Ptnik}_)&s)j%MV6s`s;C?EK6C7{( ztlR)EHa!>+ZBCx?;&PrWBAv@borjP^X0dYaUCvP*yXGKJ$%#}mRWHN%!2qC$25|EX9-^({!BT`s?qe3gVkSjD^ z%%Y7QVewayp*fBD=DWs=lr#rF$3Z29WPO;AtAyu_H7xkQ=le~o%{rMV~ zRMfgI3(E-*BNdYh(eESh6AwPGl{G|I;iaChQ8WMK+x&Sb;hf%M=0Z{We=N$1mRy%1Kr+e(@&o~iNpP6JU1y};-9LZh}Y*zCQw`HVJ;fq5rX&`7# z?%KJP&aK9ubsA62tZVJwRmUcLEC7A(i31aJD}|h>mfppOknp$)z@3wNtsQXwF1W_O z)XT~n>h1>MVI4j}QuztOqf1xO9=7c_!y$bzxOY=rKBRIzy56)Rt=4pSgak^?!$PW| zy8T(^JVvJ9L5_{gNL*X~ev=nb!2rx?FT~Dn6@w1Un?4!WQ#Z7s|49IY|b%q-j@&NQYo_^d>6Jv9hSkwsgJ$W}BFAU?t0*b{U9vc~G)Ti-61ceQ%}P682y zy*C|+P}6S{;zK29_)1zw2_%906B~MU143&^*c4EY_jv>kd=`Qxnher!jz;SAW+0Xa zcDVid1m6PJ{Wxfacu&JWXaZLoAL_CvsjwW~sa5lN$Pcf|s+3uTnYLXwy3@EHAi;8R3L$`M`E-Z+9|zlY>! zY?J4J|-fX|AM_QOQ$XjG~wclo0 znw|>qo%^qQ3ox9-P>cKKlwH`S;IkmDh}Gs{Nt=d{n&wqZNL34=NLc%RG|C=sUj-tY z@S;#ROBC254CXj>GlbO5j}jZ@Tit%_w@iB}p4a@62#Ypzp3m^~8@-7EUN}4!sR<*X zE3nc1=tncB4=I+a&4e)ZpvlKV3s;&@4iYDfLrALR=mNs0;n7vwhsbEr73t*TkyFuh zB6xCQW6C4auu_Ei`AiK#UMxqfd;nSEZe1AMK zM25R*D$NId5x<{zMNSpdf=L?C4g-r}0F+Soq z``W4B0;uhjwjc(|c-X*;(z6;dvSM5XEZEub-CM|)+H-WFhG{gcNwoEC$4>ZHIcE(w zhhw@|Q-$%rYV$_7aDmV+?&Y5v*a%HcM5UvR$M~=i>e`_b;QP9b6}4akmb+Fa)|(~Cq*ygxwm7QU z-bw~3M8R(fSEegAs4?E_S^B~g-XW0 z(KA!>@dp)R;Szqzlk9atfqcmq5rE*s*8mhaCz+o-IS^>6^-?0Ta35vO8^|^0z4yi2 zNo)1`KcX;>j`uH2$aFjF`hBjo1{?TfwAV+zNLCH)ye7)RbS|o%*oyy1kyC*wOJv*e z9CFV;iK4$nm-nz#y}sI1h92@uS&;M+a}c(pzplA--q$)**bas0(dR%?`p!W(>LYu5yHo#+fcok^FIK;L}u*|<};k4 z67~137EyvUA-#7uXtau~1~dU$R@yUj42aaZ!+jXwp2l{a9iLPjJerJ-jdmKuK$$$auxe!In54sXQ@`F0zQ7ej`Y(oqi)hQ)PB|JiNKeu5{EYnmJ z=y8E3h!eDart^JS^_uu+Te9cpcb-zum|Jje5aF3)!0p1jPB221JN z7Kx0KlR*>)_FJC>)AAbQ;Sh;YabW< zqzMgRWgIcv8tsiR{DYR3k%(r1t1=*<{0nouHwaLn|8Oe&?>j;uTl@8(D`p#|4XY!U zQ|&qUE-Wa-H563r9_7`#&exhTqM*5+D+T&c7IfpJF-s|nA~!yg4Pcb-Heg9Q1007M zEtD99t`%1`G)Sv|?|H6Moy0pjseemyjM`fMljzB5dGv~gjHaoMsd#dl-`z9(9qE$( zaC>YTFF5CkQlqExju)D(jJn5Yt|Ua-{jukpLX>PkG`(6>*R?fNf_veSnQXLfuq!8J z|GVMIeQ5pSNA%D_jkY*_SJ8ROu0l-~C59GTT=0ND`Lw5b58#mGW>M)YWOqVAKMkLK z!;7O`9|=De;~ig1**&?TXPEyZ-$4vHH=NutgilhX+;>0*w}g_6U}Ui?7c3j(?ZNBK zW?1?}rqsgaVNcCkap^bEg@xN=>R8_Yf4Ooq_JywwTz0RS5kHLxFaPM4=(vBp6blq3RHFYsp0Pdj@uma_jGH6vNnt(00I?@^zW53HI5D(h# zreU~^2fWEWV!^*pk|2M58NTpeJMGd@w>;i79VPa02sxoCb&1k#>ef9K`C`iCh9kf7 zH^mGZPTOCF%f*4x>+eNT>4*^47S+%H;WROQ(0GA9ILOSJvi@^QHeWAZti|B_Pk^lJ zt49~g!}CGw6Gq;IBYcBA4b@2zJu_2=t`b=W;gl!Yzv$UPjn?ex`39j<36F}JkqH*5 zog=9b`eg>pMj)hp8GaQ?) z4~{||b0*ysIQQ;b4g;mHU&5$I;lE*>(I_G6>IJK@_ATF;A+j_T#nEgyv&*}iZt*F( zhRhnP{E9~=;gKBdZm!yBr-3me=g_p(lKtFFL%%3m30!&W?+S>q_R@$j4&9QF#SOw2 zUGU7vl$0V#?CR;e*)T!h(DdQyXd5z=>VGUCkdjc4@Q2VeD?ATr5*m(Mj)9pC+qQ@* zwONGK4cR7=^Dq3mItJ(t2MGF`aU%hQVMMDQ}G)NDt>G z7HkEh2|JqC0R)<$p%cG7>&Q}I^Q|Ia?izv$cH4m3n2c33VDLZe2feL=!9dZ5zXc@C zx(KM~KB*TN>K3p;LZE{$UdEwkUsfLaJys{A0d3$R!DKfT z+uXGlic1Jfb(@5oiL|$odW0n{sD-(2e$~vL!N>H z9k7T&#RntnQSw`bHUxS*v5aU8rH~KEI6u7ISE2WYKAhlnrDR;#W5t4?*$ET^-s` zup{CRd`>gl=_+J`9zng3?1u8fg7eJZMQD7Z(0K}9?VTEPm77IBN`q>Kq!t5w4ARFF z>?yF3!W> z-}0}SUH2{b-@|`#lnFG-WFd8qG~i2W!$1$X6o=kp@tDkv_uN@4C&V`b`wWnNdEY%% z2h984vdEwqn8DQjj}mqYv^=Y9w_Op*Je-ikKLG5X8zk#ZT4MC`#%Z(kWY8U zl5?jDKD&jZkxG7(XJ1yiNIHiL4@q~0USiAy^Dq6z3o4@W~}(^uarHM1aF&ynY~0dA+^w7Fh3 zq0Il6Q``t-1@{LJ4-3HISV(UrfU!wkt?!mDNkW;{-~yOa#PZ@=`%I}z-fF8{d=R^x zIR-|DL_GSgj|bjzC(M?AfGu-(vd4Z1Os|ZQ`4d~uxx|w+oh{gkosNNRkbMXVaS#4n zb`vCsj0)_A{6s7WORRrfGPlS#vloF&YxVn5ps`Va zB)7boYxcke%Z@#+L6J3Y`RFj+p9-;h(x!F|XTM1_t-4Cy&~P-`44HN1tX)ey6A?iz z3vQ9_Z%%TAx1r}jkah7z37{65Ob?JZFS1k}em#SHJPx zrWszvM@jHSS%j$nZ%VePDIdjvY#6pk3lo92LDDDdK>* z7x+D&rN(Cru3#RxW0d&Fhmun$GP`05J$EGnKP-`s@n@VHh4 z0aPS)D8Bp;V=qM^$r@sknxyL;f3r+f0SGK35qzD%TEmR5Tdx6;oCvbD|H| zECDbSk;FCh^Ywt!dsTmh1*Zezz+IZ9O4ehjbdU-wP?q(>&~AWp&2f+yyywF`~7HcNi9Iw zddkP+IFcEF^x&A4#v()*`O)qvJ8yzxQ_OIhQ8r>oY?h8CpawGIc&2bpW&T8~f5 zrH$q-hgOfU-9-=z9bNX25h!>tep;5XlWl@Pj#WHQ2b$e%Y#eIh?n9A-Wo?}Sf@RXp z_9i>_2s_B~NL*Psy{I!^>u3OVClF{_M`33&^kV;S{x+qBK^9mu4{(eJpoC?!I6zG|yopypmdh{vqA70_)5SypCW3ZkBt?yH%(k8@N3*3kntlXnpPPKQEn*bWpOzlikq= zWL@;WU?NJ2j%bQeEqu^9$g-GKazHr1^P9m3_li$`p>B<;Js&gy&AnII4gAi zeyr;D{Gjc7m7wW}M5(2ARwu@g&h&VP_Tise2>u`2)KA{Rcx=LA_gJK!pW!>4j!jYzm0r$Z z=+IIaG41IncZ+l!q4GUJEIYZtrEUgiQtXAYATtF(NcfzBUe@R!L0aqO62jQapexJa zO-QLiL4hf^=pk7KLVxTcPJ+#lD=FyzT_hr_g)!RLF^D;?nd(DX19)+`{I!s7oDU&s z+4AM9anXDhja1k83VhGt6vqlZfifN~2c;Ej1K-m)^Pw|IqXn>%NsV-$2&1PME^ zEN+u; zyEetZcDVKJaYMBY*nnzjcT7@;1r`{MmZ027fSU;vMr9m8tbS?KA3p%hh&*^qlCnSd ziwFhVUe|@8ZQsquLNl=KH)p7ECOg9B2DkQ`NzGAxlY26-7`sPya?|z-OXL)r?XnC`ptp4- z!W`~TVbKgt)k2xs@7r`?H^Uf_GURHAraMVi7+$HSuvvVqc;NXt1UFas9UCzxZ9Y*k zEQi;VOt-JNiwE5O{g-GgZ=#ohnIHaJsqT=Yjx&uex~z10Ph?((;L1l;3(JJ}I>3N( z=^c3q*Hb>Lk6}j>YP}Yiq;#wEbZ1#0_kS^Ng1pX=OH{i%3+nSk%^coA&_>!^4F5QetK7I6HpC>MKWo9nvttp0m7Y4 zu#}%Wx#CJ<6uj~xS`^Q_*&#kh=2M%vp9ZjPz(`jVF7oe1CI|cLrPDF`&~FO&2%jKw zGW~YDLOnrNbovbgJRgP93HpGoA9Q!25;8u2j%8-bK4>qZX9}kXrZ47M4J#gQrwQvf z3O^1y9N`4ZjyjBe@}z;IEXiTi;$?S1^t6{BIdHwcWU)@y^Rfz@U!4%**~68 zs0CHnh#{mS3N!~W4b6`XspdJKEWyDvF;hbf|0SeX)?#I4pXDevano(59x9Th374{1C+=#MVc8Pu-(@IS;vlq!IYk1vPl zoohJBi6+m+{9zO-P*@uwofe687Xd9@_$mJX7X9s`r!_2rmd4T|?|pw`K7da7k)d`} zS}s?Gs0!17-<1qAOJHZaGHqDUOL@j?^)SmM*1WEY=>Z@ZMZ^r*RP#qvxpdsH)V!wc z_xozsJRsOpwZ{}cB(J2oiX80jxU|}r&eCX_5LvcCZ6Rc&7o%RB0qw}sHwet2h1K%P z(-KPY6F4F-78DRn9EtvBmC+iR2mfei6IHyS51O#A2Jt3_GBTq%9ngG_wGZ<8sw`{KNZfdNsxzV7a;p?9Dk4*rY{ zsIYg}p}#gwky)EBB1)YQ^l#}Zy9FE@ zn{I?1m_G@G#2^H?#hL}m{&RS^n|{&PX5Yu_dnmb&@i-qrt&>#3a(OlaiJ5TORjgLS zsa<5~?St`)NRy{m*N7mnUA_%FKhglB9dEDeROGH+7gIuaSJ}2OZ{QLHsLs|7?W0xb z@v%JUcc?HKLyXw#CjThP`FgWn1b_)F~_ zUfiqk5TNn{Ms&=qa*7kS+}n3~gsSxnCp()b&0|y#hAdxPUvp(&VKEC$jG2(t);?%8 zQfV!zNhM2@HJ2;M54XM2UIe7A(Cn1F>XTvK2_DJ+O}R^^NgUQ$A$5g-Xjl$;3lO~& z64bd~MCKY+nmD|EULDpJk4lWgPf6k@J)k5B@xjr}cQ4D&5ci4|t!lHoBT=KMrpH2B z>Z}uV4D-kq<-|;pumngDe)Gn|G2Sd@$W?gc%{{CMSL}4HqBxn8ecc>m=1yBC({N-A zc_DkF#VE?2d7cxSOt1=qtkr>+p31*u8*Fo6++@Oe6n<`2cm29c3*gQjfvB7 z8oD3%H!shE=-~7eR}5gk=Mzn$HrcZZ9uykHW0nlKz)yn1FuqF;mzC~mH5r_jH4M{hBS_FZvSLTzWiy}hW?FLVlpOCm z1PY{W*{ACohxMSLnE4`({=KH+wZsAQ7-?GXqe+(!agE*WCX%*)GP`bVy%!6uzZLu_ z&fF(alW5s|_SZ1f2_!Dzv2ADs%S4hmmYEv^Njde-v_&FFuntVigREK7ozqdg1y z)9yGh26kf);WYW^s6@>f@QQ1WXGcC(9t=eUAGX{ER0F-u+Z&w>3?r*gaDf18p?a|3Txv}OQwW!FG)1Bqa;6Ru z?@pqkp@td2fwU-Y^f-q}#uh$u@wt0oWekRa=aSE=8WzT`MzulTt9Am75#&uD3<&v}o#xEior+Z&j2mUhW-fdh) zy)|nC@O|t<4Sj@;=3OXw^Hih!sQGe;Hlxznw`bczf`-!vjNj9LyaQM99($6Z+wOlN z-NRsT4}Mf_1#7(hBai?V?~ACw?&FR}xk+`tpR9yhah>&fvmrrUmm>U&gariQ1EY51 ztoRSqD;Z~SFR{*`GU)!>hafV=WblXu;~hrE7b(&UqbR}wKUo|qC2-LLI|qZr8xY1w z$dMECB*p615kM_sOvzNjFL4gt<&is&*)t?iV2&@JV6;VhaUiM^IRc8l_x&?kH%=P>0!|CptR548)ZiUril0Jr0tyQ3GEUnqnq=HxpP? zCL-=Ga}LqG8@MQ=&MwqX9t>pr?UBYQ@b?8`j>=NFB9~Fj8*%VpVV$uHUJpuN?4Lz@Y+K)jF{|vv(zL9t6Ae z{J|bE|MF8m&qJXDRKBj7W&vE;t+e@NgUICrcxIl!JnE6TlrI79WkH9n7$9wEC)*!q zt2d!+U8yy4flVUBN&(bldZmFle1FJ{aB5y(dCeU*D!wXU?q&%CmAnVPUU!)d7<0C* z9V^iyfcAK?kvVxkOL_Li)prw%lMx~_^SVX}_0URg zx#hm1xdzkIBOILv&teyGX6Fd480X^q$weQUcPRJ;b~IT!+Tu&nDwcyT4-A{@jG$A} znH4jxUUrGLQwWD7wmv1N@JkQb>^0)~G`P1H2R%jizg0J7p-ogtwBx&g(~`^`ErVDV zzi=cB)fmr8`i={2RDJsX8I1c3-^qncEi!k-ukgZE7x2RFZ!JosbB+bipfP32Y=M`V zEX7m+W%cbN;yx@(xX&~7({~_0_s;Azow5{_*~HdqcgtklGYC9sa(;XVyLJ|_jmiKE zGj8S6N2hFjV?E4!f-f1ytt-t23B}^b#@#?JNfjkvsK4&= zDeG=?V zkG!IJ0i!ozBZl3YLX8rEU@VJW9Xf(m+({}P*-!h;D0zmE#w`w@vzZ;ka7F(9GRS#l+UxrQ zf~&WL<;IZnC8SLufYZuVcic*3RV|RbM8L*-o4aufiohcn`I6MG^e<;%3q9ZDkf2r8 z&p$-QWTyWj0_E7@)O5UmHO)dQz){YxL15s_(d9d~aa%syIGCe|@^veCAC9I}P|V#f zGA=sGku+!>NlpBe7=ZI4r|;zme>M2_Z{Y=~mcGk_dbSB<*$<2I`LG&ec(~yctaSE^ z!^s#bb13Bajhp!xdq=`+8<#4+Q{Ix!iB?7+RoS%{F=Y?!$^H_pBupPsc4~m2eBGyO zcI*pH9ST)9o0+^VjirRY>dw3N&L)UKFS#yYplBjv%Dr+9I`@ozl?!Y883@GhI6C!y zzFk5Uyl5;c1OB{>$G5seYi3o=D}NAr-EU3()zkfh`aUY&B8OCd^s{ofhxgEfR3r9r zgv!;=1rDWG`MB{ScM>#+CO%9UDYf%@U%FOXU|}7%(-b#Gbbqz;QHvz`9YALC zL((g1VimYC_NLw-jLq(yiQx9Riy}7L0%`xFDd>SR1W+d#MC{Ght+F-l&QcQ+Z zvsD@)xQlC9Q}qt+;b^2bOc0_Op2Ya&KM3uBWM95R@ zZ-1Hik&di+)yx-O%Umh^bOk%~m^nWCDJ((BQBn@)lFoeJOc$pX^q_v8Q6+^u>(9HL{F))ll|7VOmFkg#Hca#F&(vOu!s97#7-|SWFmaB}4*5T^+ zJ*jLiGp8b=)RWtOEQ$zLRqZb05z~EX`LE0F9=rCW=p-SK){MDv9Tod7hTRZu-Zw$^ zjQl(x@m&qv4@7;BX_ExBM80}-g1%i$!3ztB)2bbk{#haEav^sI!lwG{YQheyRJaBC z-Zd!1Hgt9(I@ED9_A#Aq`y-=va1*rkhK+V_Po->sI^6bv2FL{z_!<3IsszlMP@nq> z8It0<@DJNQy7%t6#P=I#0A)M3r5bDgI^Jo;$6AZt70S-{J#u$lNiVT4Ig3NmYAm>g zSoM9z^uwwpNi+0#c^`(y!so=ai47jtgi{q#`RU?o2PsG}4W>}4_N&&(v~3{}Un6_I zHzTPu|JA7WPv_MZz&|C*=y1r8gM8D1@I&I*k&A1D&dJ{SoK z+`gS<7IDZ%XkDyt2%5aB9aRaHfdtK9t14hP7t($L&R6}YnWL&7%!nKLX-r3V-r+i$ z_K8Shw)nIw3NMHO6O`nA2`>!b!-Szvsc)NG748RDea6toS^c0tlp()DTo##$@PjGC?nUtC$vOHtV7 z)8SC8oOpF#vIx_a1>M$Wf;#J0aTeyoUzZH@qBmjfL{X^tc=8qUmBFE)vUZ z8on`aoBg!z*YFbN>&ep?6mco;=3o~;MJQ9vm$-dzMQ19UL?a-uGKtBZF0E7Hg);}R zzpY+G8X=Sv-xYK=u2$)&;J}iD*t=a6*GaUQ7zK@fK1U19;OLRE0(Veml^Rky_vVTd zx(gF`mmHDrKTeahPWwtzS8dx|Dw*?}bwkMzLnp*c10X~#hrJgJlJHE7E0hX@y^!C5 z+_NO7(%%$}Wd4O6rytD5(MxQ}f_nxg(tqa-CN9 z__yLsaY~BzY@;;h1E!@UgMsltY)tB=)iR&? zRoDWIhQ+oV0B#81Z59STKf0VXI}_|_MUY4IDnVk#S;q1?QYKD{K3^$A=dR8E6ohx< zV9uuMrehL28Il```@1-`(-#oDKeKnBO##|9;!vor13?5LKJCJFE%wywiC0@~z)vxT zMT={2^fG?_GkBb5m&MdZSOd-+s>x3CwH({lNb#H5gnJ~tfbIYmtokM1 zL6Nx9EwRUny6@KwAMN?sjRxy_CRKM9rmUjN zzlE_<$-DB@glsIT*b}n*ZxHO6a*hoD_9WM3A=Q;U3tkyKe@}s=SU5tb_I-=9m2 zOCq!M^C0>yV1xHvEiYbYxsAMTf@w;HhIi%;zaWW{-2SH7O;I!kA1SHc-~{qHDZdBU z^T}8_Bz$=-0(sy}kow9B$xN1qs?IG%8)lz&**5Bf^lW(Np{RG?C6VeMRiki^H$$RG zb}!oYY7Zn%I#{=&a1`f;My7|$tJeq}xNJox)r5%f?7aut(m~MhS_#>TYqzy@ZG8^+ zSQ|eI_{fAn7wIxW?{+fe900lBuM;U^sjTzu8__QC_YL}6#bYeSZFG#{vylfQ1Im{X zx60<366y43RI31NZlI4OnB^J!8}#$6U{2bAaFYq@lO}6Ok+NG)dkB3g>!x(!^^(+g zwmlo>Q9grr9eDV;^!<(@BwzLlF$^|=8x@uehksy9$htIFy-*Ej0A(&rqwT$*)B$OG zp`mj`Ns%W1veKf$G!s6Z2?XK#y72sLk%T4{T~&?^3<(C=D0Aibbw(b|Yd&rS_N)#$ zhLGRSusl-j*0%mv*HJ0HmY{4K#I7N!6Gq$_b#c3A%EVRBNFNPhC|({@jx*pg=5}0t zHJvF9**RW|XF|_Qo~+l|0Hf$cXkieF07Prj9V&Q*i_|`#6F9w+MQqY3H_aR?!rKbn zlF#dmU1T<6soQ;LrAhg>;!$N$K_|T)JF%WA{1>3r2wd6vRCF&MO0fQ>otZFB5P<{$ zh-w%mXqRUTZJ6TypvE@1j6X0wdC}ociXMPnCJRqXHFLILadZCl;sd0PeYi@~-#r1V z1UavE9VMo1m(vzJo@!sS3Wx%-dXiMhh?iQy7xdw2taImr888RNPe?Cz_`I}h-Bp&e z`h*F-EB^a3j0ZHcM{cw+OgmUBMF#rtHXcI5DCI7@SpJkbSNF%WdD#29t6hFH+R*r4NJ4QXXh3aOzFEhBC6;oOS? zaUMG#^8vbDy4M#xdH7UrJET3X;<^t48CJ-|Hic<(#Mak^K8&+CZ@fj+`x>^jmZ1*Q zb0`AAYwv22fHt&H#T9AgJj>RxkLNLduqd86&qYq=fqiAbakoDnO zoPy`gOFze^oj#4)Ayg(;jHMlHzwSulQ+x;3HigUj1MCw{$P9D}wz&r#0l=6pRZB%+ zyNZ)DCCQA*?4sN9tsr8beDs32+!4@O6)~X)jVi*-l3Y^Q{EaRn`KoCL=zwx0J$Xh4 z-06B-%`p~&n8viNbSr<-Y>+p)F7W1EwGA#8j;2l0!Ui4nzK3nyBzi4F*?LK_xvT>e znuI8b*G|rt@{Ob5iy2WcvQd>zN=n6Xk@QDKADjjqI4UekF7q!RX=RQP@-}-u)VG+l zOZT}CuUIJXwK)IDa2%2q)~(rauF!FMB;v=nafa@xous*4BuM%Clzg!M^4Ul+S9Hek zy6i^bTZQ_?Sy_Nv_#jO~O=}mSu@m6QKWwD62rZrKMBn{=$~qYZ-2N%Mq&myg=+#ze zKGqgUv0>o1OatCR)=ip0E)xhgbYB;*Vy9xdZc%}=1wSu4U0#rXeounbh;$KciG9*>bX%rwHWr%MW^QJzu1yCgTmZ$jz} zoP!p~SuY_A(9^`dMY!Nu_zk#3tn8jIRN1Z?o!Z7LSzS-~;804=tNe2-?YH+7QE5(- zUBuD$)-#Ylz+VTH(Pk8PX{-7mHYcOpwxH32BoQg?aOmC91jMmH8xcN%0r%i?8fGLy zj?b_#6PF#(K=kH;W;$;!Lj29%`&&~nL5}mz8ZF(7smOj^cg`@E z#Epq7B2_6c6JE$I^s^as_$z9-%1avYyBt=0Z%YYSQ#p3#i8xCGzv;rIu}`YiBRZ^MiL5xxtyJyN7AfArwJ)2u!1`8?c~_&==x$|w#u9nk;D zaaq}l`DOsicL$}w{=$(~FP;#nkr4{C07QC=ES(Vu?_G(8fq)_qgRvwW0Cn=?C^+={Wtx>y%Pf4kDnEY2$QwFr z)UPrqzXn_0E!u>veoD!hjgmuq7nxsC5!N0}z-PRpSNVkQ%5cm|hKf$}N_M~qO=Zw# zPEqK@g37+a_m`YCDnr)(2f!%yVY~-sS^KsnkQZ^D1;^=CEqTyc#|nuLa+s zRWBbrIoqp?>*I7QP-6F4VV&$I=cGBBzssEjY7_vW> zS#e!gX+R_B-(BBMkBlLF=Xu-p2QumMWu=CGP~NbI+EYIp9vFSBrI9lR+s|1-d1j62 z2#<5E!%{EC*6+EgGMMBiD&B^h4zY^4$HLcz(&4J~n7|PE?2VHCpal)Z9z{*&F~KMw z=5uzxNI07vw(-eCbXyNLd;_?;MDfZMSV$}8$y=iw)BQQEocDTDi$ephT=hCxVV24w z?_>po^utqPoX*^5`W&Q%Q@&Ia#X>zOv|(w|@S9P7{It2wl|#b}sZ6l5USkjQ&6b@H z)50+wS_jFwG}Ah2y?=avA&Ai~U154cY;z!Jmax7*`W z1!1F|Hl6X>4>WB6KTEDKZ{}?&B(_AQ!eKf46KgkAWrILtQ$aYpd)?agl%Hh46~_Vs zFplJouX|v73BMN0oroQXlWgT8aRpRYuzPYeI5%XtWAG-F)Geqx_J6tz7^Ii1v9U7> z=ULpz341_~gW!~otaToNvBI@a(2f15M@)yn{&Xo2>y)igm#IGlV$qtv<6<6zMbKpz z+qQLWrNai1hGiQQ=xM+#Aws8ILB@_9_C1HA4MK&jzS+K>aCj{Q8iuLE-YnXS21%#6 zQhgY%v|AY;@GZ*cX8(=^ZL=M7V3iHA5Hy&JO7?c0>3LbQP|)+gKLpl4lwCFnWLTwA zT%#Y`ooD*Z7ClKNpm6B~>|Biat~v($wf<8$+YjgaJj1cRaqbUXj+V))ec>|@4Y#As za%(snn7m^%+GMW$K2nq^u7){f01&n&-t$}bXB)$Q+!8} z81&J^DSo_&lDh-u;!~ytqyl7LjZsnBis8P?vHqe=Q>py`Fz{psKRN>9LJ4$(jFrKT zRDsK>_5kq86K3Rvjk3UDgE#Wo!Zd4Lvp>(8r{=^HmnyV@!CK1}v3UL`ZB9He1mc6G{Vj0c2CG6G+G3)foY0I%@V!sx(i@VQq;-9 zUnoY)$|Wrz5jkrqA9ph3>7}kDGec+atZ)JkNGerHo*=G&bH9_2!onOPQOd z@HGD6r)Nt1S^ULR)=X0Y(Ts#Otb?IDfgbR>#rqJUc$U$nxp5mo$=OxnRMXXi)%d%8 z9AMm|`uRDG-I1Q+ywcSh`ps<`S)sj_x`$^#RZo$d;#+*|*-;GVq#y53)FU>zeNvrR z1XhRmix{oBFi`)32tD06ZTA4Czm2UYBc`t%5dPmRqFlL+l+a=n+yl2|T?@*HM6K;5 zx}UTWhIQQ-&F`(2IGC;nm5nl%-yo#^(_7G)ojYleS=xY->kCkbWOD2G>tP-M{6#YPZko@HGxNFDdta&P}Q|A!~)Awesm7GAk-HvRLWRZb!$^DO$IIO3Xe zWV6Xy`_U+IN_i{5n!xOWZbjjEvlz2i+O@(uTvmV2@VKq$nsV~~SNkCmb@&Ac%BK{v zHu8laT;O*^Sns^|pL{$bu_!U`$x%zFqUDbdjk~*FeB@93#4jTi74>#%l@@$!qk{)N|MC{2f;HdjI38Ex(7}f|%V9mRdxQupG`aYNmSwf!4w9pEu$j06 z8CF%s;g+3Mioe<&cdrf^G0az>n2RhaOY$}FK(`(Aze!#;Ll=A`R~=hodHqO69kSt? z&S!zQ+NGL4v)nlZ6tG{sPx*7ktPP@b!Z_=AX0s?cgn)H-jj9PZ()0O(wQ0;rJhR60 z+@PN2#y{xHr&rZba7PP@7I0o?&H^MpHyYE|2?5s;e$TRopMM$*%}?}z0^0u)3@ut2 zZRIA5(8I^bhB}Lmb*$Gmq%FqU;CkmQw`o_iU)D~4I>2O{=?1i6BE2}B>_~~e5ISS$ z!UWS1Z3{s^J9qa@4g*uXey)xg0|S$k7#;4+LLJ=OkFrU)Y4RN^H(~>9f`~A5*nx0v zTe&-K&zH?FQ^yEP;i*7f-wbZxBEU;fd7rp^+cIn(rpN1--0b9$<+8KA3(-$>aX$@! z=Tt~k<|YjYB9{4kh|*ajq6SyL@*e&*VISnoMpeEjIp#3^Jyra9S9C2h?RV$Lc$1qP7}HWy=Us|0>&7+{>4-p=h6 zENQv20Nm5Yciak-grQn&d7EFDIYKP;3R#i>W%fvp)RO>b)6+YbNoDB}y@O#C3qs)o zPy#mEQu$_iIymw~gNQC9zpt)NGedql%8@#8LL1 zN$kf$lqy0-@`9?geb_n!=y|iNU5y6f1Z0;-uR-@5V^mIl-!%f!G~mX7IF9THQIx5I zS!d+QIGB^Zv)7aD@GeVKr$#b)YAyY8sbK-(9+4l_q zeY^D~Au%&!epmR3+hK*ib{E=gU^g4pXC3LRq6j81aEEGdTMeHlv-jMJ3Gv)ol%&nh z+g=HVx=bFA8bMF*;CmJ!;EAnjkJQ5Bb<#btW%&`DiG8G}K_VYl#@WQjL@) z;x;$FL@#QGI^5gJDVqRSZ6XC^e-e(8PNb})U3OYrQhns@gPQ z%ATlJs$y9-vM^7>9rn^porr69S@gpXt?bB>q@({P?Lr-LGHTn@EsTu(9B}knJsv~$FYuc(kz(UyO`XH6{r3@CXl&Pu0g1&OJk;+XGus2w z1>y@alarj=+GWnZNDVGJU@%uc&{Kmeru1A#|9rWmFGLQS$q%fwMvU&n_$*2nqtG0b z82K7NHrS&O5JXaC_0|=u+Yvr746_hEs0%ww^=|)30o6vm1jz_h^AU}>-c1P+8N0EU z#@E&RJnbtCnEGwW&lbujVo?M--%XFXmFfreT&{^2cXMWWd8ETvtZ)D++8qsf>Wt9< zeK@fBF$QA@)FFw+Re$6&Fdp?J`t07RK<)v*?pxW`NW6E3=dn|ijot90S^P~LVAFJ* z!47+S2=>tB^_$JUqA$mhv6M;mEb|wP5_#c;1C%Jmxtz{8@ZPjUD0s&>lKe31KC@zrq}BbBGU^dqRH1B$ta1I? zId&Q+mHf{1Ii$o}_kuHQHOB{>K)=x^JU@yLPXjwsHV@9ZsvSP3$k&HXl$sAH2RO>K zc8kJL1UV+y>}1T*L`YwfM+0D}?kn7k>~o6`Sj&l>Y~q0tGPQ>PfXQL#dMzB|dT|0z zS)k)+RM~5f%iDc_!knOtx(39HG>wyUm6iXcM(RD}f3B$%@+rv5+)7lOI{la$0J2~1XEF~$Pa$G5 zq*NBy6|QZXr!CmpI-iYux9kaw=I$a{fdcu%^yqaD_9vvruB{SiHg*^5eO^Ufk;4Ad z4vA4jiWg;eWeQ|}n4Ky#=ap?M9?+yePh#u%^T%g6#~OsTjhEE~k1X7v3W0smUfeNq zvCsAXJR!BQjH~XGzA--U?i7$S(Lb>iPEhFGYU!`h@jXhX=1~d`12g+G!k+Dw@7osa zuvWKYJLQvy5>t#>BCoHPt@8WJ^VWRB<*b(Km~;qnV$+%YeS0?8|M0M^FYMDsi`EM% zg+x__QZ8l7<#f!|lAq^yaj&83{4?;yt~a)bxQ~U6ullKT;Y?B5A2S~5diNW!_%gZ= z>_EaQBzRGvlt<+b%^fGr*IO3okn)(|d|b1R@ZVLV>nRHhJEL80qXa%zU~x7EYWL#R zUnPAwJ@>N@>+Xc>%L#5JyPQT9tEY=o2$FYTK`VjgstiYPacyiM-Oo0zTD3*SB8lDJ zR#JW~kNW(yhW+0iA4~&YI>ge5N+~dM2`sl+D?Fh7L(Aq9O@`J1H}uU-;ke>zt`~vX zwf)T3uxmrOct>V@4Cw5TIC{IEfzzgHN-f4%s=tamp4D5rCO82Ox1kfpL!WU!<_m2y zcxd$)=6+m4Mf_B(;Qqp&8ycN__Z^0iy$Zs126KIsCk@W-&N~p!I^?fIc|^Gz$s&|g zN@5_r4e^9MG+2t!Os^*hj8M?AGrYtueb0_^r(<16g#k&qOgMBJ@%unp3`0ksD;fYsv;KBqD(jv2H_i zJOKlVuAm~|Lh%FffqkM6Tg|<{zRroHL#i3`-Bb>(Vj`fD<)e0HrM&ZG;45K{ItKcC zwS%DhlEE4-n3oNrn`p4opDbmK% z21|)vn^NMS4=IbF7oxp4v(EdCS%?mxD#FI0?_aF+cx4d|#lI^3hc4Mi=0a6wWfXj( zH3)rNr_Oy%3`jaz?Uulhq4!kmh{rUACRJ5@;TzH#KgCJcdHQO;l4~+>E8sM-H%p-`M))4f5s^k8$Nn6yJ1+^TGyTh7MF? zlJy?KikyIuMvZZ#Pc-zeA5Z$GOv7Q0%g@ODtK-VX{#GZ(cZ6RHM~fcP=Fp={J|=?- z0sHU~(ogUWTF-gsdNIZ}i)fP{rnL@!N z^sz(SSz5E8p@G0QAWdAplHixDM_3so_%No)WsCQqj(vuaxhDo@9X?A zsQTrXFAirU*}nBp%c&Jz=l1%-hQJs0`ur{6|Zq=R~4o8+c+DmQP z#Yh9Sob~6g&cbZJ=7d7bmaIn7Nd3;G&U`8huykLSSium&;ESHCBfIim!eKax7}V%7 zCSQSCJOdIJ$ZwteY)zIhdB)$Y&1Y&me|SsRo9RwvHVlbdA;6wpzEB$bF2KT}jC(;7 zgoev80kzndhG;rR5i@H*pINI5Ry$>=Is5I3A-6hj%}mss>D;+-fYH>0zU3yRTsESe zEa+U=;T2CJliuor5b1Di)xFWC-7khJ_;D(D05&6mBr#6Y7ET*kbcZ&N>@SqkRtlzT z*$vSp?^Mu|10QHJXe-ydx(ZI)v+kPE=Y6OiDuWuejR_D2rJ9~+Rhvz1IruO(Mv0nK#N z-OSG*oudki6dsnc(}&5aSXpuVGmEsD^MZ3T|MJA*A_#9u+L`F|ueaH1(Ux>4n>J{s7i z{1uUl2zhtS%)4Y#@Pcy&O`Ha&499`L@pWx~JErR>dq_s-@$_f1U592O3uO)ccT)Cj z?y6{!mCS>xgNH9hVD}TZL>yo#;6exRpIOUM4XFEUpb|# z)dE>l_Q?B-XB_leHN4bhI5Xvv5Ll)95H?w(d$N$9_P+V{qelL4wG+T6r@ud+Ow5oi zaoJ43dAZvr{upnzV9hTyWk|-J>4KS_urQ7Ue3dC8^nW=-1UU1)*y2i2^HXm0rbV`e z`!vfM66o@pXHTatIUD;QN~*7H-0P_qn;Ht!RE#8$@VyY#!8MlLQm-U4=n3Do0{8*< zjV02E?|zjYU8Iq|4Bc9_JirgW7yZBs*vg>`*@-`_KM!)r}FoI>!Ts)*XqMT z_kJ&dj6k}m(FCgSRI9OuB63Z}5(911O0}WF`LN5g76bc;nKv26^W+?9l`0%Wr9Hj# zeq|X_3Cc@`QgN)*jOTrGVYh`iGlW&~=D%pl>h1w2IvVRo)eJ_+;iHl64Ox(NqnKo< z#SFi#A-}bGo@4=uwf$$Aiko5=en6pof9!R)e+$VU-oSZ@_DFdrWXj(0V=b)QAIB*Q zWsS5+ECg))KoZI1$PA(e=Ew4Cj&(n)(_-5tDp2A-3l6+K^aw?J4ycIoVgk*)e#hLgOHv|of1{mw(*|#&7(rb(r zgocK&@@(|KYo~FEb& zUK>}v!4P>YUQ^D{{im))aUT}VHwEc=w)H((=m9FT_OfHw9#E_WxDHzAqqLCChU^ig zF=RePc5gg7m`Ejx>A>e~?uO3Ros`o}EvAQ0$iQ$8T=7Jg0J+q9rMu|t zOyaIbezn<|2&n)D0L@GW*KZ{&}}x2 zz0SBJK7e?Q{L?w#KTn0i9t{gIQHiO|e5YzTLCNcro;<>IA`z}bFi6AObI+&W(YNFf zOvI$a9&%NasCwxhHMeSD$qJWgB+thE@m3R>9C|h3IvBfEyw<*)_RHM&G*7bb{p~8t zSpaV`_y?|;N1~}SJ>c}JXiz_ir!3}Qc#>On5a?8QgqODFC%@!qJEw--6qtEM2~XDS zxsMwN{gx8|q>u=KDy>~^en|UD4IO-&7n`~7LDNU!*P{D+ox8JHK!E6a{Kk_5ie9vy zuSwl#0-Lb_7_x=`cW^@Rr5xbO;+_PO#+thnjJ2@0v${T212mDlJ0>Ko3VPgfpfN%0 zoafSQV{E1i?N;Ipo!#C%sxUU$v?18yNtPg4PIT(g{mUQz)a<53tNTp+n^^Mtox&Sa z#ZrL}KD=E_&NVkd>`kXkj|58^17iZcT!~mON54@h_vU_60 zBhNTV><(jv;{(4&`@8n!?9(?S6{Om7PTE;<3|bXOKXIkYowS)>ejzJhd$0i z$1@3#IvJtMsK6>a0VGZZL;{}-E|PXGgj5jjm%I;a1b|!sM_r*v3?IQ~nm-unR0B8) z4Kr0!vWIuzz6XjIG71DAk?jl3+V3>kR{rdTNBg7@fbQRWVK(Z!QPu@&r5>NwUe!44 zb~SCK2gjkv$iucMgZOyDJzgx z^1>K^O%o(Gr3*e>qczX<0!ML~(tD|hv677*!cZN3HmEAC*N^=}fwmnWdRH-ANs3eA+7h3Ww3G zZ81sY-2O1TvA{sxv6(kY(ATCefl0wKv0CY2&@h1EmUZ3EGTG8+1gqwQfOW@ibYXI3 zHi1sTDn!H&qO9csp_)s!^q4Qw-YUHc$|SgH3u7!)7=Vd1xvE>zjCbigQl@7opfla3Gd&wvX)JG_YyXXy;22*Qo1D zn$#fBV3NiVWyu(sWIjK*d8LPQvr%@qY!Lrs(iov&?3zZ2?(1U%9&5-snV7MI&i!aX z0*{unrIlr~V8ZRsF+Z>cIqB7^{HaOX^V@z1_z)HPdC^ z}WE zpd$ij=J!v!#`&6-t-X>ruy~}0(OX%(HR1(m;r6ATo*6+<)Jo$5w~i1Ydxm-~A1Rl~G)(KldkCjRh8m^O~laLYI~v%8`5hhw51k)fIA zbF=Zq71;lB#6o2*z@!XX+=weg!HFaJY_Mi0p3P7D=gpftI z;0%&cugKL#zFX@``m1>sFo@cpb7i1lhhW1KR1`gh7`Z9-6&L@v@+IO$pZaECU=z;S zyg+#PzXeSUh_aRzC-gDSMjk7uo)8h^betVKaNN~{#I4J~rSj|AG3O8GzuM6zs$LAa zyqdqS>`YRr74G`$HyoTLOZ&DTXQQ+oLFlilb!>F?tADCUr#y}qsUH4QEji(&Skoc+ zS56)4p6YiO=8ruD(owU5yxmZG=(?MByDFzzGmp-mQPtlB;2EKGh>qFqq?DA~@Hbnu zexsK&*(`l#p)C(JX>RKt75$t9+XLbB=blKc1@Piq$w<&!dCYu`wq8t+MN@&c;Y|A0ey;K9TA!4dSMf9Y+TF>;0kU)k$7`Hc{ zpAramkk=daNfS{fhEIaPPiE7u3FFbOxNnDB#fmX0B~W!A%yTS|CidyxjQIZ#$QJ*X zq!Z73B@*0$M1*J&jeGQu4Bs5mBZ;U~{c1E$ zc*3HpwYpwucgXCs8IMcU=?y07+lh+ge?`0@sKM5N9fZT``*HCY1)3D(B4P(N8bX^F zC^>)Jnz3CG@Jya~A2h+yEVUn4;I4LuymXsY`J5V&(gq$f`NtGXs^!p%a0-DxWbH$Z*U=~p=CThCxYw4RmBQuvscrYic)V#x{^8zEDQN;;{Hy{t^O z+D)39*w~a~ES@8X?l;BdoptDjm1ZlPR1agV=kZa_sq%CquNvL&D%>G5bKlqx+kZT$ zHH(RF#p|>Lz)IkvHiF^CFGZP5A+YYYlEM%_*;!Bj-ZGQaj(GDMKd-#*9_Mde#M>`~!B1&i zc-QTTD;`WUe{=RBQ6u&@?>W_Sdx3&SkXiovPzD6l9iglPpfV`N(S0i|q5Ux&KoA?UQ)E3mH?D$?pZ-Y-2)( zeZ3hu0N!myF*{SU063`cIyS9Sd1j~=(Wq`&{TM#Ly&Nw(5bYv`UL)yRB}7elK`B6* zadZF^mer}BB`IPB`dALRGQW~|`C;sY|{M4WOmqu78+r@0E0 zJ-2I)o}!Im8^Mr=Z~veGIK9F{G!93g zvl}6mJ=BLa(E)<+KBi0B*C`03*?|P>eYVE*I3yYM=(NCTiLiiiJiLa`YrNcO?yEEN zZg$*L!kx>sg_tCKFn4`Abu@BrG$mdo9$cHp`aqo{qzl3t9my&n^2i1Co>nZ&o<+v7 z=h@*0eWL&j#0W^mL-z(V%> zR@2XQ|Ej(mxdi1#2Q#~Llc-IbO!%U`-y>c`VAMn6-I(5wH5~beWpyin9(xA&DosH) zWaRi76~tfWShF<3h}ivPSvC6rfnGsTABKx9WB^>r^Y*A6+hXHa9&B&;JH_nF&UWn} zL^8JNCDb*CRx0BTVE0e0->qmY*OK0Y26`q`_ZL-ai!_I9h)Mqq_8|#4E~<9Cs)K+w z2CovJ+v$6M=>-MW{9zpOBeaLMzUWg2L>~#I$wAaxs8y`HM<>~`7J>Z6y`5t(IY|r% zxdJ0!SQ}O(-Sj!4B6sSGd=Laaj7Hv5h)k{rv3=n}4|Jcfo}G@jx1K{IQWF@JlA9RR zbjL%_F-3ZbJiD6WJAP!0K@0hp(7Enw9aX02kQA-(C@A zX(E!cG}VScxYkWkWm8=W)hgbbD&=1dc-k&gXx5sYe3*iZzqz@Otb8mQsr#N@sN!dC zg+7TKDwPQyAZbB3iu2`ME7O#fdieQGKP&Tsg9}~U^nkZqx@D#bN&lb~u(mRU9`s{~ zXofz;?^IetdIaaYBE+Ls7=VhxtWKz!clrdK*Emo!nm$_&^>x;hkD3(l=sAsm(;}&o z1}bKPN)0^KP_>^}1K1{OYNNex=`Q^ed+Vz$o<@Vvinh%#AdF9US3V*Qd`XemkhKuo z4^cO8C86~~jb_i3l%%Q=^)I_~l2c&AaW0FaLs&Qj9v@X(>8~@&(x61efbifrbFkAzsg|yz>^@(T9w}1g{`8V ztxqste3xw41kigDLmd^wsg*cJ6D`lS_v`7-PJZM5hu{s|79ElZH}Bj1s7n%S0Jh}>YlHM)Dz#7~Jy8_=1j$^KY5u2VSM>h@*nppi;YqK9M)lf^@oVbJbx|-&;*)h{z=6MNN)k+fG;{~KlZ2G(y z3k@S?dedKlx0-(Vi$=-J!Ox$-54*&#NC!&!h>5P;CHYL!zUd$(l~7h zi#$NAGYVU6OTB210w%I0wE&Hy@JJqZji7`~ZRnJ=*4nY_ftV0VPnKuG0zwX<0`Fmi zo?He45{6I;IIAKFVo&a2c@j}v3hPUAS!xKrOMXQ}*J)7=Px(#x#^(fd!$Y~{iEKBL zVppWO^kO6l^1oTkc@qlg>o+CN-u7DjE{UcKl4^k%Z=uumB(9Kp?JD_MX$tvWn!{8jqxg3YZC+E zGuaPlrAZ99Wr)%ss5$Je72wjAE85W%DV*!F7>LKF5a%UOXfVzKKFHV$@qo^HctLn^ zrUwqd9o{B1*-A`-a1sr+d4-$%Nx|Ogy4Sw<(u0V@Z>osMRHCGahf@_v3{ev7%jlTi z4c0Or2!pXp(fDtzQGH;h^D?nU9FWFH0D~Lf2}=0|!dPiZNNQ zEswaY8lSu%p@ZG^{Oh@K0qJQi`IN`7X-+PClPyM;-{t#+{Y=Uk<&y<)btpu$yoJbq zJA?|EaDq?b#|ZM>%hH&eYMuHKvOd+=Y}2W&-aJ!{jF|TPNbRACtF&YJzu{MC{ZGeV z{p#E*tOBm{otY~?wWnj(&c6PjgEE{%NeD+Xi6lZnkbtXhRz2qD+ub(!tfI3!@}ysx zqY!PuC$I@goL`rz_xmW-?rG31tj=tO!^g19%x2UN+h6T&y&Jk`o~)6iIYN)-XfCa8@Z?JBS%0lKYMU~fB`NiWHC_1d*pleKl_&x(hI_>5uIU0VOyS1<9u?jY z+S^91lj5bgA{(YA1v-}A(FJ|b$%NE24>PUaKh~cx9~Pn?HETyu;vEmR=5NaW8A*As zCF}?|2V)sIACAzts@Odja34Y=60IEvTB)*Sa_+>DeEJK3L@qcvXhMR7$V+P; zW0o*~(=w6qCTkJ^rxorCb2&72JJT1q!H0uuGPS)H6?IlHJ$QMX%?0~S#?tOa+wjl0 zE4H=f7Y!{BrurLas8~52-bUWc0MsHX$1X_6H9@i9kV_F`lngy%+?z6`cM!KYa0Vk5 z!iEgN>W#FSd=0rb)lNLeS%_*cKW%(}0x{F}+H3HLhVIj5;re|Krh#>>)YJ7e!7dD@ z+E&}`5xS^{qjo-lcG|v?ZU_lQUWLsZrb}+!*|RnCr5@&brmGpOE`gb8K)%1Dt!bmx z|6R-_O9qOJ7@ev26)yN-{9w&Ff&8IqURCL~pt2S#VK%Yn{~BXiF)Kdw^zD#RAsb6nuwsV z;zPV)#&8CW!!>uB#4vPyvUpM0dn>-YzqJ#?iVz_Kw`}&&YPf4l#&2Q;K*fhTp`YO7*IMirtksdzr#ge!B_L%J?V3G3$ zUEdE|kZ8~t=yGu*ahwQp)F`~@JUUO0F62j^vHaFy^id$ajJGS{Vb6h4cm6t zpxEL8kBu{bIXVygexf2*Z{^6DBx~j6nNoMgmDcDdY946`1B}0%YbHC6=qq-h zl(}kNhfC%cD)~RV&UhSje~H5CG!*-d&mTo^MWeOV!Z~!)|6rlC!n*IdnE5Xitp2&1 zy;?Pfllex4JFi)~aay=-1pZW4B078;!gE@c)rAqZB~>oq53`FEV;1lgrahl^urb^o zk}Qslgw7nrr-El0KfUN8zOXTj5()QL_HNLfTj8{i5dw_v-a5JPj}jqWeH#@8@k z5%bu~d40&tW8^BtZ6>;(ukBC`j-q7+n7a4nX?u{y8u+IBW#pugHLE~>Ku zkTn|JM>`AF)f;W4w$aK?wIJ0~Lsr!s*19oZom`=I`hg}-uys)%l*dh8?Kbn00 zsld8kY%{a3F4pIts-HMVsLwe-ZQ}y!lQ`|pVb@3{>xk)5tG)_98;v;2bfbuJ=;&y< z(iFbnezi4Mk3Y%m?_-Z`R|u#6dPK4FcFk$YbJ?MRFtv1s;gdwAr69>}M0oG@nBUpt z=p^J~*s0p7kT(ZZru?$W8~qB#nt+V0`wxnl>*kMgt5=x~TCc^r3%f0yH%wY#EHvFz zBu^b100?j@ea!i&B(bx1%DjAt+nNaoOJ3nN_}SxUTH+Db_N8 zJ;ga;;g{x*>Eyzccf&w4fimWtMKQbA$u3TUyCSP7hoa^cRxZg8&&S{}G@w7iDKo`m zSCD|8@vg-e^0rsB84U&c0Tn9T(p_>XE1w+nh_4F+8dYeBYnD%T)Y#nI_DmHz9UJfD zR+SM(Q@YKq?A$&Qh01dtHP!KV5Ki2(T*bG;kO%ZPS04@gnOu*dW#GxbQMVMHTWoKc zo}T40&^mvcc31cGi#dx5-$sN>32p&d5zT~Hp7$nlj$zhvt1?DR4?6OB*P7;@BQtz9 z;0ss`r)TUZmyF!;4wGjrc&S$OCs#ds9}U#raM#UaA2t~$38KGZJZZnSLkLvZe%R#M zi2r{<5%%WzJT^nHgysE?{iyOBCcV+bo)hqGT2oGJLTT!()P{5W)(9UAx?H^4 zJ@h9k!J3`zfs4L-GLV%vTz9`gT39YJyB^GaB(3mEInW$gVi)dv!%Sv_h8;LCR4Xhw zA^19*6SOg-!2 zeP&=HdU>P?42#mmMBW7S|CPJ(WHL-bRnU+hOobNCnyKrDI{uI`=$DMc6@pjcSiJ5O z*R0~I+Xg^zKU|7tKhEIlGmA%MSDUzIV4G#RE(I8nA<$DB$wvkTL>HnideUKuWp# zo0g^5|FPl|X)n(oHNpw2Pqff5i{kAIH3_NX6sBY_-IVD(El4-mV@g;22P8VF#wk98 z6*&p5qjLyqjSW8}gyHoqDrt%N8KQM_vsC2#`juYFrzp)GCFU83R5)CRpM@jjNkk@^>s~CeY&lSSr2_;$p(WfA5sRHc zHBE(98g#Z3H)?Lqj>80s*uhWt8|IZcI`?43p>u&o(-6F=3_ea>Jan_XWyu8o*ptri zPlhxU39msf6!`j$)D^XA`T~WKBBrK&2Pb$@i(4y)*@0<0gAmO+!&r>*FPZp%Au*vf zDkRDb9}Cp+nLQ57cbkJ0U7FHr$E2zrujF*LG6~j zKdY*uZg)@gxG;AH_Epj}=W{0=TD~a=Xh%BPGOA_8w09wP)v>m<@w_7>z$HQrM6VzYX_p zgz%#M-J-@_)=IZI_~%3OYLX1}z?-AGNf`TOOf+P_)HNLG(`OaN7JV$mWCB1)OStJ4 zu>sI%2z_yW`8tAvhQGRabE#FpE#gALtI9u5VMBk(>m~w+Ft$bYjHrgha{C&&#HX2g z);3Kq!{AMV&O89}&poe;r4lCPyvk8V)rda;cC=})wRatbI6U+paO#4+d|8TOl3c*8 z`k#29UreTMLS-i-$mL>}$k>tBO~+}gkVjP94M6P|LK>0TqI$q!Y~$C^Fe*=XwtZe^ zvC^aawI!=F2PUXF#h`3VPfmX7nXZ{$6S2lH=n)h^dQuaZX9kFP7@iZAmx4?owSuvf zj3!M){gw)Mp39k zha-Uf#5pETK;)#8Be*M0+*Zp(Ll1(~!yJI?CR7)J09bJpX% zi#dNHP?xy=W5*Z(CBCi&n^n!+TjT@}u&pkgKHcw7G)dFFIB-x3aEkqbOn9>wu)-U~ zScGt9n`}3RSD;s18iA-V_%57&T+FWXW!H?A3dZj~`9kry1;j2KyqfWxs;V2=v5Up5 z-fvwkD7H9&{n04$I-T%XE%7kwOWZytmu@(z-*-2S-F1)*YN@*o`yZQxk&0NeomVV| z%f^LQ*;Bp_zvIU-<|<$jj)GnXa5AnloM)YK*&TW+z8CHa<5m&vI^Vg}DwUO2T!&cg zYZR&t!2&WfAII4d2!W~gfX;g_ibqESvqBp5=>NfB%109vumzos7wePAmNR|Io?>`PN0U1j&8fyH?+W$&Rzour+*Q^_H+Z|10$wsK zyRIhScCi#)fjp}3u15U{6Ugn4Z-ww23LGj-cFTZSkpaq-OJ;d-ef^~~yCBR?1e2kX zg*P!li!S%?lJuHRMXUQcD%5>D5+VOnCEV;{D0mNT3D=SJD=t8{HR2Ho5j;LI^|Iu> zHd&cha?bCL^p6$OyX3&#rlG}S z=oM3mC%G!lr9|iFNf*;CS6mpMkw|*R%>b%sOWn>*IIULkB;c$XWlKTMw-7Ej_QuY1 z`&>#gqW;%G!XvpFHx;mnowa+ZM#bV(uG5OD@_6s1G^bbSh$C!Wx^b@6L-%x+xIO}5 zxbI^;_Kp7FS;TJO9c}m31?i0b9=gKDjGQw_sNCfYRDh0K{j5D2#w3VQ!Fj%F}pbHR>k2}+go zxQq?S9-Uf1xIcaCv%H}F@&0zpvQa?;Ig*QftnG1hzXr&E8+H!iooCWi$Gy?CFxq{b zz5ZYjqVhnZz+$jHo6w86UcDymKrpAk|% zv3cjD9pr-LL*5PMqZsQ(}6=@xTS`112gKxeD{?s1jpg4W1A;y-k8) zn-s(hgvG((jTAk_;8!%-t}C2VvJnI`+^8?xI4X=OenS3#q`vPW@{ME-s+2@LhhRq8 z;V3ihY}&%-&H$}>uZc`he65mE%WzjYNx)Nw5iz)(pPXKoL<-yWBF|CQyddFm`a{2i8|w3+L+t(?2r!yNiJFjBxdwyXK_I-p#BfDWR7wwz%Y z$&O%_wq7^>QJ{}TeZ|n|))^TIE`)Ki8{x$Hk$WbB0y`8Wpl5Qh?=Xgp$lyc3{f%-b zI`{y8V$uF3sJ2yAhl3hu4jfl@McPz(vq5K2>`)N2D6Kf;>O!#PUVOW|Gdkz1%E-V| zWds$M7e#^-CT~P_&8xQeEsjE8XW@%%7NPRYfDVa5R{yiQ8>JKhCLri!Q)d<5lh{~Lo=p?OO^*2-i-Uq6#b}`P-(YahG)_(jCpmD z$Ye$kd(fc)aW|eH#d8^6kISg%_3G`e+jqMQ79=OEKXJWmY=PAvYqJiZ+APGy#i)$$ zs*@w}^bt0H6_97Vr61?m9e^&_sdrv`-ZU3CzjNF2;VUT^Gn6;fQfWf(<-j^FQs-2T zIAD&ys2glo8nf%G4*p|Dq&4=_^zLmOiyf`8^vE}P4lctG?z`gbM-_x$w>G2>!r2-Q zHJH@=TOSFT?!0k`2$UucJ@G_74jr^~KmVUd_M^Z=sB&g(7!51yIpm+QYbg|=6k#Cy z9p6G)ed$n@di*+6iXS7d;Y*YhOAT!CYTCjEaz!e92H9SFhZYG+*Y8Y;AshM@Y#Z2G zgmxa3_UAjWA-H$0rsY^Hg&6M z*aIbLGLC1`v_)RfwZ3FXqDkmozvsNcMEYgEXS-AM2fV?PWZwaLKf?%~TD@vi7bqu~i zPBc0*^Q}Jy8e)qI`|f-nuZ3|6Fb0Mqh85`ps-Tq z$m-*1>WYV%lW5-}%Ov}BP4x%UbWQgQT4R*4lbWdQBLNyVyMR(`$OIx70Nf)4Z&jpd z1(HivwjMVi^E70>W{72|ROh4EnR-`>?cj9E6P12B*X`;ST?x#=K2^)Un{m^*yoqHdPgz)HN8T-%PI- z`McY?1dig3z2p?}AcUcZxypmL{6kj3+MmNP4JohP8^@ ztr)4^Y1grytZ_}d(h~KQOd^i2DuIFY`v^ch_5KO+hQo-23T%GQISR=1!6(Q{;Cg}g);v_ynsl~}P;|Z+f>Fm|88p{zIQan=$Yt|c@4byf1h+xA(tUiSVg$bupahx~c z^o(Z*=+wjOnoU*|oo>Q^$ai{`Cu>~RoDcg6Dkg)d^~1mKD0xueYO>@fQ{*>_J%|q_ zw>wktQGYPK_?#2Z+!w?a7^qiP zO)%%73SATWQwABr>s`@TE1k)NcNwJ9mgb(r7B3PuL&y9D#^04!w2p@~NSM9&q4-a4qHMIBCXdBmz+ zMcA+r;v5iX+O?l~;kLw-ckB$5fi%#!T048^yc+a*IB0i|>f@U&nAY#?!rM;4_w`~H z`0$m9PRlr&oX7JmyvnNP&zI%@^^g2tp9fev92rW~|I1`j!+$m)U_n%bV1-ImqZEr@ z!Zj}a6WWvUecM1K3GXV8N9knidY<`x&hb3mh5?clsy29Q$|vQBdQfrO&p&CsCaCkK z%R3&m_g@FKm@|cVFXvp6oG}`CC7Hrc|CZj$0HaM{YKj?%F)&81b7=BNUF^jaE^{l| zk#$E#$=rKz;F(sWR>h@Zt1SMzS%c6zbF96KxkDU1c*{O=@HFLA^Dup{#+}1sR|H-n ze@u-_XU)QtAb!+g;(mLde z=kVmi)rqNIKrTeya2M+S-v*Y|{aHuw-E)U1SzkTwDysK&j0Q0ec*3Ul+vt7DyI0!? zkQwVHOdZU5vLI_opByp5{MD2qB zxxKf!P~NVsKB9`qv8k}63oL&&QMnvctx&b#^M3^hMM~sA0PrC3&{`nA0$dI) z*Rn%U0U*UDOM8)K5V-A<{esa?_YompgyX>QtO=p8`kP1%ZdK6~#~bSiGCR8B&`A}b za-rC;v<^H*neaCqAPEW`$j{t#It0o;)B%_2&>v1l@5XD+AZMfks9=_a+0+27I(_LI zvCZ|r3K!En8#@*((E=TJe34e1jargQb3o@# zBX3WAkDPT5j55QpcF~HWe5Ub=-XRb@Mr%*}YP#$|Bbhdx{L&nTF8ERhKpw6K+aY@N`k)9vlM$JBYj4GXqyfWu z>3tW!VjGG|$ainAKO;_Rif|+Amhe?falSp9u3b&Znat%Hvj(`B9+Il%w76I&5jp+YrWUEXem8QxkLz+F*jQ~<_#&x?lLw($fQwR{dw=Ol$Avi5n%g`~4ShMF`bv5jg61u= z$*oF*K=UCfwmJpjVQ%!+wrMnGudXyUKDFVGCz+h|!Yi+`A`J)!>J^f7To8e~f+>`$ z;t6zAFZZ`TQ=ZMqb^6>o7pC;CL``W@z+aS8CncuZ>uKs|V0k&Tis+F*Oz8-;+umgP zQ&ocOk0T?@vF#+c?E#)gdqQcjgRBWnDsP()Xpo?6f z9QBLO&Z*a;qh3G$QIZyibu}QRTYYeh7lrH)_-$hQJwL3`LUxxAGz|r34-rp za7zbdIlchLYUIfNS2%4y*k>>^t58wI(7C!(9y*pu5P7ty^X2y~eoU;$7v)R$ldX^l zU8Vqv_G&rebbu$zTvR6h4BlWPnu+m%rt9P7h)d%fS$GYWbS*N_!@sNq4WseoXED1# z$Ylp%!lr#n$-vZ`S# zT7Pb2CA7;M0VZ9@6yPP#`NXri`-raR(5o)8C#bPzt(*l6&L%E zT5p!+tZnZt_*H7cM7&F%5Fm9n$#3+A`KvnQIG8X=yIfxsya!A@KNiPN;;ib3;<(W= zj=j?e8Kw6DbV6WAt~e2-LE8Qqn_dv1_iJN zPHZuLJdZAfLY0rzSK(0(^tTN$6hxgUZy&~Wg*bcm>Z1z`5|tN@t;+rhg=f+Xsa0J` zfRzc*Qz0$HVy@}7G}CxIyT@s_(4&*h1`n&bC0t)D)@fp3@Dr)@S`WzzqQmctwF%$a zP7M_BMlcG5;IMZ918P^dCldMnZMFCpChhKPHES`f_zbuS05kKI=vrQi+dbNn7v?D8 zGaV3RS91WH$hAyu=I1J#5-uM1S9&1C1WclUhTOzFJJQbvvCqm_8g9(G=8QCOL3T739V8RY-$57U z-nfzHZEf_7+J>49%sr7>f-k!FTUW| z7$m_lptf|1)<|IQ!khR{7`Suc#Y07cULeDk3NEZkv$#% zuifR1T9yEx(e`Ys+`lWmgz~Q#-vB z5?kQaf;L4`1JvMq+jF_#5E7&pETBH8x0|i@Ec&l~I=lMq*|e}~D=dQ+2KLT?g1xE? zR&&$GelJ8y3rXon9uy3rcT77Y@1^popntTbj3gM80~|B%o~QdgU@v^!9&(8MV2OwL&pw#6;hH0IAVR_5v?|TJ!nh~t8XZ$Jj;FWa=GNV%XW%nE^*zetZN0lh z!>&cfhLs_*mu~X|hqq}Hw?x#tG#cC*YlJKVVXrnAOafx#v8cTy(hO>J) zTsf9JALWVbZU4-ryfVmfr6gB<=?Jm;m}(=Vcob5WFA5DKb2&MOY$`rl>WM?^(P<80 z#N;QhDy<&@O*E)+vRj@@qQu~cZ6Bk&uO#5Am4u;?1?4_p0x-JZJ=ann-6n#9GN%C8 z6BmghMU0h4xd$pk!C_IkjqYikF+AVYV_()g8d?6B;hf`xA>n9)Gu?sq-&f^mvW`%7 z!WV&@wv~J%fcX}fw%K33-M{dV{x)@?L6l?M@Nd6VP{;oQq7;UQ#9pTOnI! zt}o9!2q`rq1LCno+VF^Gan4Z>G4Z4S3(voI|J*{${#sMcPTt5ykZ2*Wg9#Q75?`Xq zT@j1D=qi?4Qzx7(74Kn@ghrU19=jHfii17auLP(>j_JwYzE^HP@A*D*yW^>VV4b|H zk{0Zjm`J&ujkuy^jwCMUykmGM0!URqZ->^+jsE52Lp3s=mU~x&o+!FZy1jDL5IHy>qYKmD) zn6U9$;#%3W*D{Yr%~L_PDl>;&rB_n+kxo_Gqv|$d@K=^Nof+UHU_Fl^clqauZzC>? z(U~q15rY{O<^j6HzC++GLtC&gGipOR9@us>Pkq)fdGR<>>Cq)oKlSv6 zizH4W^f~v(*7J)qR?I2&8@l0P>@!EJl*5O38wyaG(_Fc@<6!({9~$v~{slH76E`0S zp*XHYB^$W*q@mPKhn0h6k&m&KDu9RIN8unI(MptY{1-mo!L54EZ^iOqel*I+1%5n?I>2UM^y{+Qrq9wWw`qRME6Q(gA-t@y(@?H5Bg!1oRQG z@)o|1n~g_6VLu$PL!o2W0@&o^43glM)!qL6{ni%?ZYY`A)HUO zGcA)J5o0aGqI9CV%dIh$LFS!JAB<=%7h5P!QO>;tiP4|!-}VATkmgixsOUI!>wEP2 zwQM9KK8k@bKmwVt4M7!K2KA1Dlk%NI;BTqaeOsrH_9iN0d&^N~7i(|Ie1nOPw?avt5lCDF8wJt(0)`y$C>azMA6p{5k>2Ud6(Ym47Ya}@HZuXMWbv~1-xB)E*=Qs6b{~j?!x9P0x7TXMwwa4O6cO?~&H^7R(G{LAMx||@ zL$^d+nU)T(wfsHwFm?Opx;(yLL2Hc?d;-I_UJX2Ia&NZKtdyV}}ezs%S+T z>6e+Yl%{bjiTA&b$u3TNS$D{y< z3LNe8Df0h!-wau&p+eP}pS8XnKX%i`lTn~uodht>gr!1&pmU7R9y&-xv24W8BQ_>< zQAmPHu*{30yU|4dcnN-AO0OjC3=n#C=P~pwfNQ?ozQk`9Bcx*Bqy&i-Atcs|$oMIs zo#T}H3eu}vpze8gL4Y-1kQ37_NDg%FSQN~$8J{yMyo8JJiD26N{few4G9 z^ZLk-igASj&gy)#qzB9t2F!k2Gc*O*^?tmbyF`GMv$#7ny5j3me>XeMf-N^n(e&le zNcYju^}MnTdL)L6LDZkPQX(Ex&%1`(Xs6Bm3m77-d<($!;7=PJ$qrW{JU88g<^444 zXBVj*!85Zv-i%mF1A=7}ea4F8JrDkcxXq$5CgAX8N(^tP+#K2Hl6#`x2+y;G_TJv; z#6I`2iE3SoXrAO4m72k{L$zhU#TMHCe_5?V;;;1rRsI$TC+zp;quHK}&tox*vZxFR zOrAQLD>V_uH$0iJ!*O2dYpm@N#L=FJn#owSeO>ZR2!U{iG#X6@6Q{hBHah+~6<}IL z`5c@>f*&aM0gGwbxd+U=t+m@b_Iq!mTiuRCZVyUI+Q0n024T9?s*pkykD|Q;i3WQt z0EMQj+?Gdd@f{)lFv)`tGSHh?eF%zWv797CwH&e~cUR;p|Fwz!!E*T2m+>&tR_cRd z2DtDL8~njoEy3zg;kT-)yxE3l(}wZ~7gX;vnSyQrl-+V{iAuj}h_n0G>5{?iRkM+q z+Gal2RD%1CNBr(bHvF~4Vm|BYo5oirdFk8ega7i`hYcblth2o8e(bO8b+&S}Bv4Of z?2P%4Fs3H;g-!zgAacG!gCAY;=Ny{maL-kbf9tfdLSyW@y&M{Nu z-)3hb)Nl!7QVh5;09XOM8~}{�xR*MG&C}gS_maXGTAzj~&02*Inl@_FA=zL5x79 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

    bo8k4TjQBlZ% zI@j@K67gTO$698)Sr?5bBmw~$D=O8We!M#-{eD{qn8%I+vxm+~X#%+?MD>q<|9VwC zBAX?z%nWd<(IpW1{VyDlox^e3Q1KE*!>A@1q>9&X-hRs`BuDw|vNdpC-394eUD9WP z0$#X`MtffIHG4Zvm*?*to>1xL!=raIHZg29K4Uc@@gI3HKP2aG*Lofg1CqVFYN#v7 zZ@x#_woHtknT7GdB`9aDH%mHR3V=FiQ&4X_xSy(6JeD_B4b3{F;&|;_B+IUqsd~!eg4=GR(EB`Z>bWpgk5Z`=C$r)x@ zmiVJ3L(CEN3Jo3Ok!Zh_r}(`7U?N@5FNtpDif83oFcJ}Lv}T|lCWP7LuW8`GF+LUX z53IgzOPb<^fHfP3LZ5l-bGHHF1f^4DOj3oU_NO%kg_chReVu@HwG~gT z>y8Z=kky0Wq48PqK3|W=kK*kgn@`t4-%dJvw%7(E{#pnku9!U#?lNiJrvx*Nrl=J&$g?uMA zD^HBsz#Sy*d%oXi)vvlzxybDW*BffQ6xHW^*;M8X_6Wi1=~JgWT)=g6+`?Z&Aw_=8 z1x~-`F21?P#+-hJwD8Zl#tLqX)yLGVxUsFQJUt~Qbe0C3eFKPY?Cu^g%tXXcFsKA! zCgUvAeRsC&sV7v#4lzMe{ct6-I~*+|oqLU2gSi~=9Fs$SyTB`dYmMdr|3a)#$6t;q z##NBC32RM``*pNUSvqN35jBsV2;ia`tI8~@XV{%HQ%Cxor4qk91)%{ygz2&75xtv# zrdbIz5&j<}V9}+3P1HNjwjtw7d6Wp;3~i^iYu_@VRzdb&Z{W=z%t=#Lzd@IX9oqjT zMJCvqv=*;^dUZFj(_^|D#!pkft{nO3@JW41+Ir7Y7n!&>!KB6T72VZK@%y#E#*MQ4 ze(;{;AspKG$fGCUN)ni+q|I^-3E;7znisPH9=&z)^?+Ei!(!H$K7TTM3ysI9|1_kM zzO&(7nwBrCaHnJ+crjxrfWr@skZ^3^!-o$(ZYk9sdH81+MFtd~!2;X)cv`B`7Tzc? zoGFWcMK%!qVm1A#$F7j+lBC1;mjKNJZ&YsFPF+^S+Cve^CJ1`Q^^1AygZUBB_h#uy zR5PqDjaZrF-NR`=uE+icTLMS8-xTfzoXs#$d_slHN65aI4}krX5)oH2?T=dx4f&E5 zMw{w!iu#^t%?XAQ5}a+U4P0)f!H)~(C}kA`y};)$Ah6d;ed7E}yp6uqu zdo)(MADe0}7lCu)zX3V>ywUrI>e9ZPFly7+9``ft*dkGXrP^yfY;KS7U<- zh$)?t?`c6%o2L|T^X!2=g0OsQ^3Or=#=9OJvB-hae|KhGj*l~%qqc_c<>$5Xf}~n< zf~Gmdree91zRRt)R>?aqga&+=lW1T=uYUaB-9-EeKIhlBayg}HlbNWq-m91>DJ_FD zgtdcLso2aSS4*8n0A)>bn&y&4qu=&25Z^4@OOk6WWPEXOfOXvvZdS0W=|pr~ zMNZWw6|PSEKOEUVdrK`4>htRms&B`es3NupaFPUd@$^5net2+IhCvuk*PS*^Mr`Ez zHf^Z4L2>ud<6wdU7p^|CHd?dxPY?0cEKc8Cuor>HF+b$nb{EPuMP*uq;t4X=j_j?z zHF*-(c>mw|+QYkDh`XytQgtJ+7Br@`1FPH07f)z=9F*|-0s(Mw7(QxJ&^)wzEe{00 zG@2C`;?kjl_3pZs`5&qP{cqacaTzH^7f8)PJb#n#m!-Im3fK+j0M_k@uAzP6r=k~~ z8GO4{r>|H>7=F*i?wa|E{M)yF@E6wVz@C$V)SEJ~Nl#Y%8>vQ=Y&7~hd>8Q=&R{Q7 zmN1b}xKnvGCTMv2tl)I+&T~mYJyX`JI0p*H!e*s%q3fo4jZdR66O`(?gWO0NZw@R( z5@;2TcYajbRrKm|(_Q$=YJ#mzG7dbrCk8=6s}gbJe+RidjrIvE8_h*=RvYl=<$z{J ztqvT0DU5uCXGY2`of#68k=?vmbUX1Fvn(m4P_guX!5tOMa4tSth$*W@i*3c=3jtDw zYx#(q4d0XsMy4kNS6a`Ju&MBu)S~!~8IE?j=eq=3@LM8f^}La2vdx-G`iLc2^n4eS z{sAIJY%0d)gQDw=Ym4Jk>Yk^&Cd_H!wUhH6-Fl#Vzjm9TPks*Mw5JaNSFe3$5W)~ zO5fm3FQ!Uj4T}>;$r}dTNE;LDVb&|nq|akYro4v>b4yKQjk8hZV8+qDVdbAp z;ZK$stzb;ofXVqn9TFGUKp}>oPv$xIDB3sEHDNjcc0wyj`H{L0TZ(kN! zWP@F9c;Q=AUHrUX4pc_8WR;w-G*y3>`5>G9D`HH6V6%Z%|Mf_iE}`7~Kc`eyg`mKK zw%{6(;$oGq_19UI3o`vk(`;KWy!|_r!o}BwRDbB$3lpVz=lo9Vr7B(*H#q0U=jHt? z_SxWdqy42=4Le=MvM}#kh)IQ+Kz>Ae!9ap;&XEceslGCkIu5oN`ru-Sq^_m31HbZ; zN@`)21-J#XX+gcTg{J~o^T)O>dkI1P{tqSdzP2AK=-!mdL%j102VjM`&7&uz3;>=t>gp2$d5w1TrO*Cv<-}WV&XvVUc zXoLxr0Z1A_L3?{a>Ep4d`2mQw9~C1yUUmN86V z+P%4?H|1a@0z>5n0FM7ci$Zb7wSxzul>E3KsLM_=$$>}o{_p%A007a!M;dtd^E4+z zQ*>0g;h&?V2yQPxnF|m{+-ya_Fls=&Td13uewFr^7B=tQ(y-3H(irLNAA$8b-6njCv@Jt$LZf|-xbH_FfV`&o ziwfOKU~E$T9X6XfLcdyhV~B}Y#?#}T%K4TcD?`82Bm#1k-qeGvYNBU4_z~LCPt$%Y zJnI_6cxyLM*dX?6#L|69p!KQII2DeM+yOV=1-cE4A}d%)w$#bbcx`iN3dOrWF}=o` zv-3>BRNgH{k@2Uxtx-0t>&;dORw z)s)NFv@Qb;ey60+8WGDQM8g*tRK?Aj&9b*?)9@UQOSljf+qx3!G0?<@h=iQOl&zqj z7x+tstp@GY2(|k43P}5S-*!0;WTIVch{{vjgHzr|=~RZ@81X0Ncq`Wev??_mw$`G} z!n6TRSr^!_+-;P3Bdqy^Uqh`0Kre*(SGK*1pX1|ceUFl?LqgXinq7Jio#CozU|xnE zb}+dzSWej9-V-KpKphL=-h*imqNX+Q46tdPH_@=|aZCfG!P9Pj4gC1GdJB{!c8O%~ zs{MJ=X%ZMp^DZWL-(nZzAqLBAopqds>EaD`Ah_!M?CTgSF!1vOKLoN|?R~+#1YDDc z0BTs$=j4a%rtcHpC1|zR=x+r;P!kB$EM!w9A$6m=#f7LjT4q3XPnS6MNP!?5v+>OR z`RiAf%;6eTpG=b!u`uky_FwS)_Jm+bFkQAHXim}k$H?zw-w;9ijY2NAq2R{bIZ;dn zspF-Z0A}$e!QXKWrekF+D(ZnL6^;56=H~@ox#+QDPfL6z9uwEb@~D!SGE^Aq-Icim z)GD#^+(3$4AmL$U2l03k3u?FOowhrT)YhY!c{ke$;`tJYip`On^1f_ahRxf~n&Nl5 z1(YDY*D`{xb6w@eKN#HZfb0u}?~Q97f4~}k@o0J|1%dJpTR3HM!xu4At{NnBi}SYl zhXnP7UdcE*|<^|vX8OTFP*I@*evAOL^Cqn-&ul;ARV44ceKvp_j+zVQZ` z#TevKe?*f{{;eBAALxFtxiUF@S&CTm%M^4fM~Ets!G{^^l5NT(B(V%U0PF9=AazVc z_p$bsSX+NX5RUwQZs6V+ywR{Ewe)EQf-Zdn+VCb1Z7?r(IRzWjXs(&d{HK*j12Rky zg_?d70vvAmT%1N{fYa>R#IEb{fwhlnqP`Bl23hlHZUZQz8Ipb=YRm;Buhjrgc@ubq zCCaeI+X{=?3`4m1A;!jKCNX9m+N$X6}ShvYeWahhVkrK%Pp`_7*dS~Op+8GBQqk+OrRF+?!`D6OdSSJlAG zIwtoxzX8-)3ewDp0NbJz6DbK)-@(#@LOIbGT$WZZ(KIBPW+AADIBevY;0IiUucX!f41A8r02FAMMj5 zJb|UU-z>f|JKoEXj!kH)xGpOcR;Il2(YdHF0k2-gYKPK#?a|UO+0JVD*6MwGg123O z{xh~*cyELMTs1M>*9_7N{eGc9B7DO>CH}lVkpY^{T7+o|4_W`*{)r@fZpOignkg!Z zS5N|zHg*BaM(HVJ*cv#0D}QdD!O+XRv5t@tsKpBE`#y0!{PIaBn`l*v7&S<>thxAl z{vdAxM&^+4JxgZxVQ+F+aR_a8I#4m*cCU96alupN(50t=Kbh-zvj($UTaK2|)qhKreek_o<+suAKs#&IV2IIccAlOB|cDRwF z2yP`3*}%H4L7u`=aWztr;T`l>Y#1;nxHgYeNa!NTGGnP!hDz4sX{IvH?!)4#_E|tY zr^Zq5Q#Fag?w-5Zc|F;LYw07ruy$)~1g=eZm{BrarBKj~Cy&3-6)sYHl+gxj4)W2! z-2G!3$ch0n9I(?~u?>aR?KR~)ZU^CK;2 zFaV|S#ukMK#?FJlBeR%koi(%hj;Gt;RkSsQ+>4tl)qBm*;$I3?|YDb+pt)K=fL$)ub)D@=@SmER8 ztH)i_hg-bZy?R%%eh9Nx4?J06t4K&LapGvgC_;CVW1-{@qnl!K)=Oc%OZeT2d(#pm z1e2{^I}yK)NwFgMh+DqAo2T__05*dIe>5}+HqEd3vilU&w7^~I!oAb39EmGK(>oHJ zGXcozD6D>>F{-!Y-2u631_zg1U?XX@eh`kGF>o2VDH<_8j9us>(B++9=Kcfant(Gw z9$!$27T|HMsQgBDow4YSlX8$l(?b1P;_J9{QF(zRI*mPNasU9*(tA`qWmw6D)yj*~ z!;u+;;8^5`R^!|%HNgQ;zFG_*u{u^#4JHIBaBPcRjdFNf$xDo2GE zse;^OFTvuMueaOTf+7}CbrWWbBy)xWB?~%U9Z7Ny(5nBo`=Z+Q@xwk7AXDoB0FA+} zR*7vzoyH;7dD)WM1F$aao-N_4qKi8&4-x{=p33__RhW*WpnEZ!W5g7b_L;t=Q2`*s zXX-kiRZVY@2l=@ei(!L#OM>nevKdtV|I%hm35$z#TzI`6Yht|i4*;zr4gqNseD>Sw zYDbvAU^|Tjy17zeEXhi(shj^v4xI>0Z#o1OotTcqDgA~PFI|2_JA;+TSV||`Ed@YN z=f{KQP!_yaJkS{rDe4yl{7~@rIz5k->>L=4Gt0nB@m4bTa(Zy;se>IIOkRYJrFExN zJX0jIkP`dFsSi!wt9_-J!)vU{rPjQ;Ty6z6e91HQ6o!l$z6n>vemwv=fcCL-xws4u zsbsT#=`4HGM*R=BL*jA3Ny;dK1!>H(L=QLGYDQ(P1a>bt zP#E~+-#L$0j&9d(DjvAIv_JPDkCTx}*I^1g$yhUkO!*5=&H&ax^lD(_untET{bM&L zS;=CbADJ0)QFGG%<{MavCT4bnM67!g=XnsD) z&HAB7NfWa(hN@LS$)#;i*Yo4NS&J${rD1E+u}R@g6`wu>+)R=!y~7&etatYAM?c;B zpJ{uWH4iQfOO}S!-iixkW4EOcPjpc zE+`F%ckp>v*pF^dx8x1eH8C3>pIU+W?^08Lr)Qp&53KRlOvAWw(pjbJ2|wH5#k%2a z^;>i`M26%#@}Y++A&Qq3^aEF~)mVltEszA6hYr#7r}$xN@1H>VuI(=}l@cw?OlP7T z4%%uUll(NMpZ~`vn?rN9hpi2l`XXNRXhn8N=;sgyjkPMl97h%=)%<@( zfrD;e_)sq6>J{1lC?FsGKm++g*F(dvSJDL{hA|XZx)ZAh?5ABK0Nl?8`{-{QyYb-- zqYhmmrkj!B(YbV!4+SQhrl_`IQc(secGYcaLyDA~84*e3(J~siJM^%p!TWu&W%_$Iehh!hz-vjP&)OZ-F z#y3r+_N);DGq6zm6=F*2c18W|)*RiutyKi{5POwN4Zm>Yv=#kUif+PwI7<}~ zJK=Xq;1JB(1)ZEJvWq9G2+n`Tz1?Z7JZ9yx1J}&qdc|ttUqL3JwxW8lVq%~{m>Hu8 zjjY&JI%bql;*$*QsDN5|1dr+ThVn@8UWDShJs|kYsops4BJ^hOUGdbfkYy&un7U1x zd-({jRVvnp&%5sIc$=bw-B#QQB{lK_47T@farI|{#A#I~iz(_6Am@~(;A+gpZr(pw zdy3Nk$>TpFW%YU-YWkoG&2rV0%sqePn&6_VZ+fK7KpqcWRUC|jRKrjD&%^ZPrWg&S zpJdJ^?VSdX$y0K8o~BK)_!ulxID}Z5*0w7C2mB%^s2iZf%7=#9w74*Gh~0-yuZS>^((2?HIJ@o{^ji^Xy?DUd6^T@*0~k+o!un+!7BTEqrE zd}%J|RCy4z1!b|$!j0kD)LE&p?x1Rb^-J7XH>pLmKAtM%uTXU#AlL(HbGPCHHr(=K zr-CbiRIQD9j)#~l$LN-?NUrA;U?8Y>P)P=YHhW48=Sv96PB0&M3rOOO@@ zWaG+4^t%0Ul$6x;p+%*vRon#%gjRR4E*wElfoS%%AJ~_aSbApBVQ2umP7bT|V*Utk zNT?L598YR3wNlq5C$L%~Qu$g?j)4=uH7=ea(jIrZykc&6@Amb3&1!t@!+%$EYlyNe z7pR>-7eipvKF|woQnA3=W;L1zRVx(F%u^L_mKS8aABn#5F#y|iUVs1_)K4e$drW~- z_L7DjX<$($j@)x}OHbWf7_{L>C8H7?^(PXasaEjI^pr@PWToLDx~4e{i}+O`WTRgN z5%&6Z=N)E1X1SPTRp3EqCyAz{3qVRcDVmo;G~H_zz^b_=w#2nFNCMdO)F)j2-M`{6 zMm=cvuoLuQ2o1fx;h0%h_gB-cD*(VMV$_i`ffzavMU0;w2E|7diwr(MG0iKNJY8xR zib)cT`S+-io?(>2vJo7|xP~Ca7Z|`oVcsh)yH!4&_7^PYy_dlI42mrH0cCnNP?pi@ zL6bE_S4Erycr~v761p|)52`fcanP+-pH6*d+1kQa{zoyODIl?+NS80>C!pxN%O)pT zc!HE9m+wa#PT#}(q3Oe={O8yACavOetWV>i>}U9Py8fLKc;Zk^Yf9o{FQh`IXcu(% zS+?|msPKb`^nwuUG7`T&cYVv#|e+T`L_(Kka#U z&CTVqg}orGX=$8tqAkH6dNI#I!%NQ+wgaM065&ZYuonhtI9Xxsplo(w_n2I|EU6m( zHr+I3V5a<`AS@b*KZN-_WKfRWnp0McLrK38dCBdzVW!ae#umusiuN}%9^4l|>gM{s zg~}$nmA5liLJEPV%Whyn%^FhiIG1Xj(eUkm#tFgT5Vw2kpI}(NjJM0=_4*8ah3vfy zPX(a2PH?XxWa-x(gb;*|oEt0E80^`#dr@1)%Z{TkalF(hOw9$YxK_1pLBZZucqq*l zQm?^QH5fLHUv<^pNo9AtKAz0Hj+r@0rNmwkXJVAt{Rs?TJ3kn57t`a?X0khwZ(WKK z1D?K44JBlg)i=emGWTu>?VcFs_+mV4|{$O@Adw*YjxZ$%@n~*c%qWUrH?*AE3Z`k|LUL9U34}#&3o5HxUWfzS0^@5;>B8e!vJEiDfVmO z6CqFv5E&l6!4#PoY$F44q%=tuy5k6xz6mQB7bRw2>sRpb&*>s6_Z8^#Ll~s;>P6_| zP7nwaTQ6LSiMKPRC(V>z$ry>Zb+B8Ep#b|d$oj~_Gpi!ITntey7}QtM&9UmrEKD<% ziqn-n8Dd*~UuV34 z6#|7ORu<+60Dx0wYJ1OcaQG&Qsu;`7zq@P! z2{PP07&1XuLKP>t)&TYq{Z${{P4O(xjj!Dc?c(HbE<%F!@Flk#8ZhoKCuZxEPU#MX zE6uET8{%C-N0lQ3cHkO%tU|5t%VMZ-`(O68Fj|lY<^$M(zIzP;V(@TiXxDZwSg>6J zL@^hx;axxD&`-|fQ{M}-9F(ecqKFecwPrM3R|7=^+{+|$uY)oRx6Fa4fYeL7uxO@; zIJ}<%wJo$bFW-5$0mEN?kJ!~iq=4~iTqid&+SxL*TMC5*BS?x#nk6Nz75;8YuJ9D? z7Jhd>D_1g#qSz31i6;~ZBBjV~UABOjrNxscK*LN@Oq^z-nJq)2!RkkgxXr1Z{dvnU z!FcCdJwanoXwfZ--mwxXt`2cI)ehWf%>kBxX%?{%p~Lsy4iJq(+SSPv^mCAie^<#a z&QY7oKds!&dZIE=46tX|qT&|lujJV+aCZ|2-dumFSS0ym_&p3v(e`0w1tcYm-*1L{ii(fW(_!F~ML}Mlr7}F@s(`{qi`TnJ*@LA6$)fUc8A8R5vt! z9QB9ytJR?l+)uZU$qnLd+Xh_hP5C@)ZDv^OtT@UU$^4oa3feySDN05aK8A!f$Rz(| z6}JZ@Sp380T8AAJoU=+Tl@ww0#?~9SJ>q|)BigZknpzq-D}ub`qOj+5c$%dpzCd5X^I#yeU|aY_D?3b{YUM4~Y1iL`i5(wiXl@|2NZ)5xoW8HQWNA58fAe8> z{!bY2lsZO?=u_aqUM3*HDoqI(S!U!nCn_uXNx#3QQ2#0oYCLhL%qxpCB}pn1H1_FT zbhpzQw`U)C=%p}PIu*weVl)_jps_!0P)5%1c3nFNM|{k7lR;v4X-l&5{pmyY4I(t} zQEqs~%mm!i1iBuAG*d>f?9h;p-Qi%pqiY%)3kBH;`_q}}QTQ=wONc~ai-Es59dqiA z@J3p#7ZIQsUO0_BP`W2;UcD8(t@~J{4fd!@@!?Uara8 z6L-~QfyYLka%A2r8D&>SZLcOipu#+D=!s~AbCn6Ruj;g@C*Rq)C3NIu+t|m5ygdD^ zD!%T5Gm?;N47H|-4tD06A2wgr?UEz#uWLQmJ%f4YpWn-380Mc6;X$BMl1T03Sm1JY z9z*7iF>y-BE?P+9Ny#rjNB*ozE3Le)LJM5L{G-$~0%6Ye%AlWHblb2qs9-y&C(|Ce z;TCvJ|5XcvX9}B&(t%Ew3dQHv7u1Xd%QoCn;0%Y%hkCy~gmr8C7iZ}}7IkpgMq21F>X_5s@}!|o z?{6|by(>!ocs>-IAt{4N3ocE)~k-{39vy3h{w!a z0#kRnWWLT@lstm$3$KTS7LNBe-W-ku0#nc~fT??PrUP%dFI;|72_^#zXgAuWZyAOH zKgYfQHZNBP)&m;-VF22ANk}=5efT>R#pGeQRoH7EUv+}1tmJrpQ(#D|g(k1;vTBQi zoEykBz(!fa_ikH?ROpy!mL;}K);f3^rDV;F+wiEv@I0y~mS z0g_m4z(dqOyt;g^)%)6jfq}%;Bza1GEQT`I4gHXbs#3K|hy~9!iTe?V$AG$AgtXm z##iIuU%jU0>baH2(D8vsj_ndToXE^fl~a=p%j-mEZ?Gsjdm~-XBT6O?i~}_Lx`?V zC)&9>8tMHIRb({Mn>k&zdYwaXAj&Q1BNnt%R-!c|h!+TOy&ldW2>PLEE(FE7-@a3x zeZgkc?0B}a`?p`aiQkh!|8CcN3HWfQL35lP7cm{&vI@A-L-hha>A4HE*6SchIx?@HT1V8s6N%h!Sfx z6m#gk19YNPCL-hxt;pXxZg|rmkThWDBv)fE)=mZq6KYeL`;b|IzUyazEhAJPyxJ`bm{2TzP;kgu2}6shnSNQeF?#v+rK#f#Nh?t!yA*sII%J%9yPR_1jPK;p}0 z<#u|g^0K}qa~@CC5FY@v=B|d?uJ}bS&qcr{sde&>{61t>+7*i?BFER|cGFJ)$8BOq zu#?5F+bj_hE8!mBebs+~X1EL#GhFZ_-tG_*&9?t+*-=%)(fi_FxztE@z@}o>zB*A# z``N;=@ebv`UBLuzWRN*C!_Dg4m)RL^O|azGqdHAf7iq^77^iq$=~x@%?dDqPn4@!o zE(RFotyg{+r1${Cfl>0drL-J(4-Dp%`%E#C3hs|14ygMAcUw~2p`89-F5-RaE z_|hCzS1dgs61(zljrK$mcMQZ_x5IyZmQe*WKV74 zpZPQMMQ346G-z65#}_V4Sq375XPgj#EsaZvW`x*{>KyYxfF|dbdSR?h%Z>sTnp5&P z4$uXqVa#V1v?X%RI38an|CU|ym-IXJi8J&~gg>RU-}ZbuHQUbjlv{YOFvOmQ6v&Ij zX9qW{b3XA1a7aj|gJ)f15(U>pk;AjD06n!Gx?LcDl7~a=94*l{VYH3-*fc=sVd2`9 zU^I=pHe49+ij?7K)#0*N3v>HIQBwqvb`{{ZDM$fzIc69?-o#7h=*$}&nbRpRM~eJp z@+ofLEP;fc_IR^SW4jH=DkRaQ(UXCEgja>M%l&lx(I`uSe{6>Zf8ik|8PqX4{3x*) z59z%i_9hiu_p2sAf256V|H+C_jFV;2XEeHXL8w)pla~y;SDX7^vi@GJvIuH-#>7#? z1JEN%P1#%!V~@1-qfEqP$T=m&rF$Hhb&pK^VM4K|oJ?G5e(QIlc!MPZU?O{@e zy)SfRBFa?s=g{>A3rYS7KAB%ZH^t#<#}yA;LdX1&CbKRHr+rwH*NjFxq;3q_UXY8ag01PG;KYgL7au%6~;f+ON0^ht>#*nF5qs@IIR zdjY?KF59Q{Qw#uU@`n5wYVv(a7L8NVfJ@6%{yc>m)GRJyFS$HBWC8smI z*f;#o%|RZrXTTEUiBAV%r?q-&UN4zO;>VEI735)YWI0^xXOLipOf4|KJUc7ozeA;K z(|WMH%pQfcn_%Hey1B(+C5~jDhMq!0^73a?Ho*F1xr5ocLzXi4wDIUv&IHM^yv}d! zg`xEXi&eBW7k{D-E){PZNIwCET`lBsCpz;Bl@6WNva%jce2Na*W}0peKB)VOX>sOq zXC_fQzr2aYSet|+a~kxTCkej z;cra9Z!y7csQugP=in#&Sc#Zgx`nF4+K55hI2oB2MH%JkzNtsDaMvJOGxAqHkG8ao zmYA*44IXIGr`=4%BZ7HE3-fU+p_fuZR~%)u+QSPkqDeNuTW5q^_|U7}(k*ylYodLue?GVY$kERvf1J16n4JB>bGqE4Q)$&d zlhdX1O$a#|Xv?2Ptg_;?Yfq)poC(qtCyJ=27mby6^13J7^SwTyuYt{)_iRK@8JvS| z8$hrwqa7D`yD++c214Pu)RF=n_H5?4Pj(rZc85`<_ER-VOoP9;v1Z50x915 z9cSC|;r?)Kmj_KG=)o0o-d+j0ot~|rQT(uA0|-)>{O@>|ZeCebMUqoQv*DVIi0TYvsIa%J8@B}Qp5@lOaTq`#nDUJNJ+MSzAxET7sVgIB?)ms! z8;yGGOU+=&5I4BG`$>f;?`jsih59*%(2;OcJ4k)XM`<%}k5`%1+464q3bq~pK2@-n zB&nIK{UW0RxqG2cV3-28D65loBEFu60GK3hD>+6u zebZRppXd$wJnv>>Tk7MwV}y0P=++QR1OB9Jma&^L5xaxNgWrCl`<^kX*qUqbJ2=* zv@RMhrpAA*GCv2!rJTcO8E_gpUJ5}(N2Wk6LyGc-9vP?c>cyP1n zMO?R%4)Ov|*2kgaEOpKfsU+&x{32IO@K(&1EJeqk_C@q}bzW;I1cW9;ge~3q?ifOT z^?OFbYbP|v3wab%SFFzA52ppau|%4@f6h1V+&hj{;Y4;!NvC&0S15_r)$(xVy-P7i zU_txy_ZSzK_Qc`1QEtgxY&(C2OwDb)^v0*WZ4TCt8Xx}#t(dY3p$)ivcBvO^44i_t z_MwhS;8r|Ge22Sq-*}TgX7vmxg6$}oY0qMY<4M?n=rjk-K8!duPfD)UJ18qxw6vfS z2s{tD7^RhORa6YpWqIq;M{+9RTJ4d|zO_S))c}X*dE4oV%kpu-^E=N4*TtseSox+9 zba1yBXbJ^8s`rwb+afY>i&5a%G9KxUPD?<=Yyc7C^xMTcVjhcWxj!kqksZ4vy+> zRQ_V)31QcdY=`u=_VFGn@jt7^eXnBhhQ?>uB5+u6aF<`>nJRre3I1dKv6wWCaZWNLUPC1b3MDgn+hgVwH20k1 z`-4=hYSx-2b>&$}F=0wZeLN~)1dyKO8=4OE%;$ABT9Z4yG+tY!SAI6yKzri~&0tS6 z3xpOyt<%xx=H9*T+flCBz6bS?6c5wOfU(`_%?rMqXWryMdBNg^mosd!e-`dc4({uD zgD&l_Y7@7*eX{V!8Ec0EXW@4B9Od+XV}Hz$lQk8EX-`B}Qf_(nDm}cf}!R zszF8v!+ZelaeCeF@%tZI*!Q*u%45TNX?9Y;NBBJ}+5vv??v(GyL3vp8eB!(X?)a1NciV&eykyjALU?-N2QVNE%Yn5s15T z-L!6Kq*Hp}-)#=f>ac{pix&2DpH<@O)0((EMw^WE51;R0lk zS#mB!5KOkb1*1an!#?SESLD&1KM1h6{Yb*E#|xFzMgM{;l+o6C2Fwh|kZlxAYCT?w7U5 zEOLJE9VMiB)y+T2Wio^x9bxSK}y+O?|BRqV{fas*ADht&qXF?xa%~eYF_Au`KMp5EU zp-xPI4l^%Nbdva~4S4jT;W~XQuz)KFG|ekP)spKmQ9_OLILOX31-8WJO3S=c=+?Hz zg;qtow=m=K@6{#W^#stXFCbCy=1qIY8pCw(+?*{H1Zb&4!l_>nv>_syT3WjQTTj{I zB;Kc}Sia*QwJ__6Il>Rf(gByd=?T}WR$L5Nf`LBwLDKa2zPRzQ2&5{kuO&*?1^gucvkMm)h^+p(WTr=9Q>ne5YvgV8+c}C z4!XlmDUDuitDuZ)hVu$giST-_zat{3f|LZ++WywonB(~ zPPt{`2yBJ!i5^jXMW>sRhgog@-69Ra4aG`2SOqVM>NW8*pboGnFJS3^`?A3c~#Y)sdzRCG##Tu3cUIZ1c zp2ks{JFt^lW^p0sSCB&fy$;=sPvLf(`K-|GKVj0^-4|)iU8~F{`%w2F6Wa65=qo>q z%9&jW_b9WmXsC5&`l@})tp&GJGnS}`1bZF$1dI2ip-s!;ts&ueE$@Jc{x-D7EnilG zT>CJ6U)=b}6&U8j;_*L#@@;We(vj#C&ZJcscFET*BTvxvE8E?8_N8C=7q zqoOW=g)||AIhfUOGPj&;NT*~p)NGxEb06gXD^PU}s@JZRUkBxR+&I7WH)%fucybZ2 z+jT^ASbg|(%6SG?V=E3@9sZcEk}d0V&;qc#oUF!es~pp;<^f_VZ3vH+h;zSBqRgAo!ZoXS{+`1F z-}!=P`!Z>C_wJB6g0XvQ!PH#C1xSTL5oQ})e@OKmj|gB6Mwt6}_9IjT`I+4~(q7>t z#t>ZjhJz$2D@CzL>@qY@wJBHiaUolD0-GGJXADJ*aC@x#-Ozh0nb{h&XY4?-xu{Lu zNPJNZ$9cU*(uFTD5Ysq(2d9%buSmm<>!OolrNiz-EM^WYd->iwu-Pz*OSGZrS;gPZ zo9!E)T$;#Po~%IMOqgyZE?g_&229!sd{uA5T+7?UaTf5j+O<(fKsA69d%iZ9je60|8XntHn0Bd|<3T}w&v zTy_Z0%fF)UXky2Sy>S+*vT+mt6WV|z(G)>~w%EecV8c~%mFNstjROoCc5@QtYrenY zv_hHX57&*z@xuf(4t7b2zOF+d=f!8R9JA((3D>3qLd4{n4@)JB<6<-T*BM8 z0z+yZ$V>iU=2Zs*6?n2vG;?1!vGdX0w~KN&NatuLAYR3wACW(mSe3cy*CJ{;jM8nz zS~3Kex#_t>^a2>rCw4I2v3-pWKBqJp=BZ*jKwnY;Y?@f=UCty_tknbuYv8#~ZI`4+ zg}~2eV(|;B@3IA5SGaERezmltgyhfnaPhckYr` zUxzjO+}qM@#3pJAA_fEeZF(bf_j4>CKnBgt&+Bsd&wmARzJ~&ApYIjQZXL;NXg8Zr z6V_PB#67Z-5qyX_^T5ae|7sF1vqM>-ADH(S$XZs^-9qsxzo+5h6&k>N1od_3eJ$^b zV;^`YOGog00(dC|orHg7ty;ZZuQM`tCY?gXM?9}i4O_RpK_MY?U1Nn@sD5GC{foSB zrTi|gx?N`9#upgLbOp7(_%K7V8ux* zQ%wTG;L$OR_4lpXqheI*FWon)5!)KhodfxANj3&}U03)E$YaxUTdSxv{WvFbkYD_n zx8zaLuJtZgK(*_kIbrcnIcob}sy#JY%i-RN{JV75^n5n;${ic9%M{Evbzor0+yS3cx8z0pZeeMi1-YB<|F4hjy3*n|YxY?$`gxx@1VB{Z|cUC#s^^VU+2yQWY7BHB;xf?2ukWHs+e#PIpm3 z&*dZ_psbr;Gs;xHfB=H&MMy}NUu64DT#e6*R%G5;BeuA}KazXJB$`g-Q=8{KArQ^y#yg1M2;W+A4qr?2}!6qmVkB zG2KdP=O|1@Q8EWMANOy44O3NB>y;os!YtrmCv=>~q|~rINGZ(5Zyb0HF1RCW*iAai ze|Qilf?j%!ppu`p$S2m$JQ^}gyn3Jue7hYI?O{_leh8KMHnQ;vckpXyTvv8PYhKhN z#lNl#_SmVbm^b4V`1?DdOeOE(`|q3HMG5cml^O9;FhhYb)KJni(PKJ|;Kq zYL^vF;Bl`09t%be?n+IWUJh>>*bVtQ%rjchwQ_j5O@$1dG?twYJJT z(L0G+ye1MTgb#mdS#U7TC1GMq2LCu4Ae*Nqn>j|3eGS1B;fo@LEbm*U(Fa!36P2qG zWBmKPD#OJ={@^V_A)!e(hIyp?f7hE81-v-`h=+k{-x`;(CdgKWY*M0XP(tYJ2Vw~# zHPN>bHwsP?#QfxyY)XAEiSlu;YqLpOt89aJy8g)d>y!bruo@CwxKcz#PwLjXDAWNT z=$B9ug0Q7)gyJ%lB1~&TYK|!Awvn^n4Zq9p=OW%SU-msM6VX`S7bQR_dLP`shkUbn z_eJ!X2cnubq>SE0Z|R{_S>HC&(3@4sZ@FF$%{`8agX#HQjK$`ZhZ|fhbga_tr&?(! zD_-$Gd(y`jYTek+@-G)K&%CV*qefoQ5(L$MECq=ZqQdBCvzE81rOeCu1Y%d{U&_dI z=|M#}CKl(EH=1ZN+?3GzYW1pyc;)FwjmFTLdxJk>lOoqX>eX<1-0|f2=p|c)wnWz4l&C87(Y*?>A2Le*Sq?n8=E9a)OWO zJ_4{mk||D-NDutw{JR_txF%6y<%_>NRTd-ZZ3V1@=<9B$Pkri2H)l_)E)2T&Rzbro zhATipJP2Ank)Op6N~V7$F|*RYN&FJc993ygQQ8Zwi0StpA53=>3rb@mJaZiE3jL40 z*Xi44B%czy`~<5liCVGyXc66zN`cTG8ITqo6Qs}Kq8SP{{P>>4+(Mps`tWx{8e^M*=hzzM2DdrP51|YrEN7Ni-;M`kZ1Ij zNbzS>Mcc)S)(}fpT5BbA(~jd_+o(sx+c#D=JF5*uMK+k@d5vnK%{D19I*z_aIFk?= z&1L(-b0`A!w_0l3yL=m-_%YEubaNMzVI9^aQit?#s{NfSnv7tw>cSfXmQ~fAiJPG> zASJrhzn?ko3&wIQPl5l!e<6y<^X}`4>G2c1X<=skv7;y z?||#>1iE1#t*L&pKR@=6aQ!I_du!`q&^{n4sdlVl5_K%t0Eo)l7H#YTbWW*yVB z8ViEv<}3yH#PjO~BiT5R4N;Sxve$@0qKs4U=3(#^QFk&m?Y`&FcG}a2hcSqqo!>g-KA!SWqT`Flr6I- z>qv>NkUF>1gKVP#xS$=3K`ibGril*+AX@~9V-U2Zq_u`yv4c7r({Xeqo+l1}0IRHb z#jO%xn!L<#@Z8yOB}oV&g@4h%^W9<4Oqv3nDbR2Q86X=XX8@Sucu$sUJ6^;6mMK}y z88d?>;|yI|C?mZ4t>OmDmx`|PIDu{fI)!Rm38MdDyr~r32|`JUg&vs(m&EMswmABxkqdSTui7+XT_RCV#EyC) zT49#zLD(JFYEk7-RWTqFdh!%|PeFI^ls0zMek?1Tk?8HAUo=EXNcMzNg5(HA5UJZ~ z*SFgqKg}ZNR;fd-Np4mSVuCnC8hM-~i0I1m95{#=hbUw|Y8qlxY&q)o)*I$)EeS5^ z5^p$UL1}9QCR?^&tC5g?9I;Few3^M7n>RlXFV0fPta7q0twwbOEfB}`ze^E+w|06ep=I^h6-vo5=T5H*{NV~p1r~9A zg~>(v=*93k%$^I@qx(gKyRFs zj~)_z0#s-~&q_)qX}_e zBTJz`9MHb^87yn%mnSEzeM!z|Zi13XDGbu?4@?!N<9^aJOvCop_=hF4>}bRt0Q<_5 zqkuAHVW)^j@;Dj}Hj^kD(zYn-vw*6>iMXc|GnQkbUa9f-ZGuO9J^-$%gu$+V|1p+} z3>8|61840$i?bBCcqh0v^%U4*E65!87 z@Y}E;r+mZ-Y*@YCYK9u2bDBL!hN!8k+!XZ@AkwBq!4ofK3srdE{0dFw4u&7brxh&6itq<+Fy z2p!eIwYk&-M}FDuS-g1tYfpTO7$e!1NgVGu3cf^i<|IG&iKzWP>6jmAc#ud;wXa?M z!!5bScUzWPyp}&!NDQH&O$Va^OUqlBTwI}FRm@NU=C6%l!_D;FW9V*}hS;~g0Nxxc zQ)rK)$vK3Xp0&-d&->Gvl!fORbsGbqzogNYmzu(bIH2X0n}V0T0s%kY69Hvsc*BNv zi#y}UQfGRC)+;6Fo>s+}Onn94wZ)r|3d6F+{fPl1tx>Vf)(BTpcL2AwZ?>*NOaU?I zGsI-Fv##m00tZYlBzw3&56Z|^<+z8R(%b#jTmOuhho<7h&nTd>hkA?7@aBH?QW9_!a+_4v$uA&4ZdENWR>L_1Qw8Yv!7Gn#D*7zsd=BJp+D_ zjddI{bpm$*Npw+M-@sFQHC}!jyiArZ(o*npCMqWWHYx~`&&#ZR(nMi$a~knEQ2;L^szj^YeES9}DrNwfs11I&I zt|0S;Lr{StSb#V4IA{63O-nmay1IfwlSHYa$+4lEWz=yku4#qhL7(aca#d9Y`N^eu zmh(U*Ka!MWj16j#c6vDnk1IM~z%mMPgNV6k5Ok&SX||o?S6gr})J_e@9Sk9X(1DtM z8j!&4>7jX^pEQO=0Z$7y^l{3Dwih%yh;93el)K&7g6JX;n z)8=Br=XkRYU!gqxxzgbZ?8Uo`S}QO41iHH&`vK#n>m)^6#7W<^xKAY0RFc^$w_|~m zV~}W9zt~H;Wj2#XoEYL;@W??)5I(e0Nkef&a1znh=ZW8nANSc&EhG7g^8cxV=Vqlw zmL6#oQ;H>>?`m&sI8$lM5U2}*=&SC)ebPf#R7MX(K?KO~-z#$Lfv`2nsJF46fCdr# znEvL&fdYdMY+)LA7?SVs`SHU?gvkR0Y!ohrLw)dr08T|jN$j{`#JD5jTiAEe86^`D zZBP${T>Cx&95@SHR!N=GZrn)Gyl+}TpvwWI-Y+GcBRY{Wx?c?V4a6fq>iE5vlhRDD zg3@%N?Yn>hXbXouD*b<(K_1>=?n;dIj;zTvfJ4(mi;Y1{`5bFY-W}M)uUjwfv&(>0 z$3PNE((x9LT9r8cXjl}Y<+@v|;=TTK9q0QZD&eH9cbV(IApa5WWZ$(HT{rjkfwBJc z@=$PRp507}QJkT#3U4*f2~<%U*`Lk@BMUky!j|i6=RqgEfaI$)k4qm;mcYxk_#2M(EVw?~pt4yI6_UT-b`EDNK}zp+MPAit0Mdcvl!es$%uA78 zKhfF!m+t%Bpy4b<d2Nt-$LRf7_?d82iaU@|Z z=F9o7@c~GUjsG&B72ySA68}|7>n{IlNaN&5bt&*N0p`A5*a^a8``#&aoaD~`y2J7I zsF>IX%vWoAyN=?-L@!N-L6%`CMC?f8)H1qY8>L;|A;9^%)Qb6A(4PtE%l6em>12J! zHHZvJI7L|T`!?;P0)gqJ{5N<_9rgriu({f}2Dc&z_O#mv=+0iv`9}v1Uf_im31uy% zhl9#~Ddn&5<5~x{NCQ$ICsSteF(aJ|el`1vA`ko&5Ao)QV$fVzUuV{m+@2)1^TbYs zf6*bFhgs0NWtQv9MY2-}3MjO3pZC}uQ*paGZNSx<>kQ4*($S^X@+a?(e9)YeJICT4 zsm1DnfdbE(SqfgCgAP%SSVw0eY=I3C4^}l$+nuz08&TbH^N?7viFpffu87oxi~5-<1;c$UYO zZ^LC4;ZFU)Y-mk``VIY6E2~$N#Dg|nV0NxLVQdmHZ$<-zGeb|z zI|2(5MMgQ@hQ;WiqlEHH>Xx@I@u{!|L}nViyXV7DWHwDsN(pyl@Qb3aN)FMwbn)lj zx7>B*xa%^*fnG9B0i8;JQv5c%8B6UEJS`9uG<^LX?{dYrUlI#?9?_{+J2|Fb)hd

    Z7%?l`=d{3JvgiJjc%7v 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%0A2?|d_Sy86Z`6)Di(RMvi2N*kLQ>b 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 z�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 z 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

    I31bp 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 literal 0 HcmV?d00001 diff --git a/dockerfiles/ci/xfail_tests/7.0.list b/dockerfiles/ci/xfail_tests/7.0.list index 82bcf86cc3..7947c6b2aa 100644 --- a/dockerfiles/ci/xfail_tests/7.0.list +++ b/dockerfiles/ci/xfail_tests/7.0.list @@ -159,7 +159,9 @@ ext/phar/tests/031.phpt ext/phar/tests/032.phpt ext/phar/tests/bug69720.phpt ext/phar/tests/bug70433.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_oo_002.phpt diff --git a/dockerfiles/ci/xfail_tests/7.1.list b/dockerfiles/ci/xfail_tests/7.1.list index f483e6af8f..ff77854962 100644 --- a/dockerfiles/ci/xfail_tests/7.1.list +++ b/dockerfiles/ci/xfail_tests/7.1.list @@ -171,7 +171,9 @@ ext/phar/tests/031.phpt ext/phar/tests/032.phpt ext/phar/tests/bug69720.phpt ext/phar/tests/bug70433.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_oo_002.phpt diff --git a/dockerfiles/ci/xfail_tests/7.2.list b/dockerfiles/ci/xfail_tests/7.2.list index a0f370f1e4..157761dd23 100644 --- a/dockerfiles/ci/xfail_tests/7.2.list +++ b/dockerfiles/ci/xfail_tests/7.2.list @@ -104,6 +104,7 @@ ext/date/tests/test-parse-from-format.phpt ext/fileinfo/tests/finfo_open_error.phpt ext/ftp/tests ext/gd/tests/bug70976.phpt +ext/intl/tests/bug60192-sort.phpt ext/json/tests/bug45791.phpt ext/json/tests/bug77843.phpt ext/json/tests/pass001.phpt @@ -156,7 +157,9 @@ ext/phar/tests/031.phpt ext/phar/tests/032.phpt ext/phar/tests/bug69720.phpt ext/phar/tests/bug70433.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_oo_002.phpt diff --git a/dockerfiles/ci/xfail_tests/7.3.list b/dockerfiles/ci/xfail_tests/7.3.list index 2bd57ab419..d5bb3e8e3d 100644 --- a/dockerfiles/ci/xfail_tests/7.3.list +++ b/dockerfiles/ci/xfail_tests/7.3.list @@ -52,6 +52,7 @@ Zend/tests/exception_during_property_assign_op.phpt Zend/tests/gc_037.phpt Zend/tests/gc_041.phpt Zend/tests/gc_042.phpt +Zend/tests/generators/exception_during_shutdown.phpt Zend/tests/generators/gc_with_yield_from.phpt Zend/tests/generators/generator_closure_with_this.phpt Zend/tests/generators/generator_with_nonscalar_keys.phpt @@ -111,6 +112,7 @@ ext/date/tests/test-parse-from-format.phpt ext/fileinfo/tests/finfo_open_error.phpt ext/ftp/tests ext/gd/tests/bug70976.phpt +ext/intl/tests/bug60192-sort.phpt ext/json/tests/bug45791.phpt ext/json/tests/bug77843.phpt ext/json/tests/json_decode_exceptions.phpt @@ -167,7 +169,9 @@ ext/phar/tests/031.phpt ext/phar/tests/032.phpt ext/phar/tests/bug69720.phpt ext/phar/tests/bug70433.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_oo_002.phpt diff --git a/dockerfiles/ci/xfail_tests/7.4.list b/dockerfiles/ci/xfail_tests/7.4.list index 8a77be4dee..436febec59 100644 --- a/dockerfiles/ci/xfail_tests/7.4.list +++ b/dockerfiles/ci/xfail_tests/7.4.list @@ -67,6 +67,7 @@ Zend/tests/gc_031.phpt Zend/tests/gc_037.phpt Zend/tests/gc_041.phpt Zend/tests/gc_042.phpt +Zend/tests/generators/exception_during_shutdown.phpt Zend/tests/generators/gc_with_yield_from.phpt Zend/tests/generators/generator_closure_with_this.phpt Zend/tests/generators/generator_with_nonscalar_keys.phpt @@ -160,9 +161,11 @@ ext/date/tests/date_diff1.phpt ext/date/tests/date_time_fractions_serialize.phpt ext/date/tests/date_timestamp_set_nullparam2.phpt ext/date/tests/test-parse-from-format.phpt +ext/dom/tests/dom003.phpt ext/fileinfo/tests/finfo_open_error.phpt ext/ftp/tests ext/gd/tests/bug70976.phpt +ext/intl/tests/bug60192-sort.phpt ext/json/tests/bug45791.phpt ext/json/tests/bug77843.phpt ext/json/tests/json_decode_exceptions.phpt @@ -224,7 +227,9 @@ ext/phar/tests/031.phpt ext/phar/tests/032.phpt ext/phar/tests/bug69720.phpt ext/phar/tests/bug70433.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_oo_002.phpt diff --git a/dockerfiles/ci/xfail_tests/8.0.list b/dockerfiles/ci/xfail_tests/8.0.list index 2b8f11168a..5f1266cd62 100644 --- a/dockerfiles/ci/xfail_tests/8.0.list +++ b/dockerfiles/ci/xfail_tests/8.0.list @@ -82,6 +82,7 @@ Zend/tests/gc_037.phpt Zend/tests/gc_041.phpt Zend/tests/gc_042.phpt Zend/tests/gc_043.phpt +Zend/tests/generators/exception_during_shutdown.phpt Zend/tests/generators/gc_with_yield_from.phpt Zend/tests/generators/generator_closure_with_this.phpt Zend/tests/generators/generator_with_nonscalar_keys.phpt @@ -200,6 +201,8 @@ ext/date/tests/date_time_fractions_serialize.phpt ext/date/tests/date_timestamp_set_nullparam2.phpt ext/date/tests/test-parse-from-format.phpt ext/dom/tests/bug55700.phpt +ext/dom/tests/dom003.phpt +ext/dom/tests/dom_set_attr_node.phpt ext/fileinfo/tests/finfo_open_error.phpt ext/ftp/tests ext/json/tests/bug45791.phpt @@ -267,7 +270,9 @@ ext/phar/tests/031.phpt ext/phar/tests/032.phpt ext/phar/tests/bug69720.phpt ext/phar/tests/bug70433.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_metadata_write2.phpt @@ -308,6 +313,7 @@ ext/simplexml/tests/bug72971.phpt ext/simplexml/tests/bug72971_2.phpt ext/simplexml/tests/bug75245.phpt ext/simplexml/tests/simplexml_load_file.phpt +ext/soap/tests/bug77088.phpt ext/sockets/tests/socket_addrinfo_bind.phpt ext/sockets/tests/socket_addrinfo_connect.phpt ext/sockets/tests/socket_create_pair.phpt diff --git a/dockerfiles/ci/xfail_tests/8.1.list b/dockerfiles/ci/xfail_tests/8.1.list index 6ee1ddc595..2015e626a7 100644 --- a/dockerfiles/ci/xfail_tests/8.1.list +++ b/dockerfiles/ci/xfail_tests/8.1.list @@ -43,7 +43,11 @@ ext/curl/tests/bug79033.phpt ext/curl/tests/curl_int_cast.phpt ext/curl/tests/curl_setopt_CURLOPT_ACCEPT_ENCODING.phpt ext/date/tests/bug52113.phpt +ext/dom/tests/dom003.phpt +ext/dom/tests/dom_set_attr_node.phpt ext/fileinfo/tests/finfo_open_error.phpt +ext/json/tests/json_decode_exceptions.phpt +ext/json/tests/json_encode_exceptions.phpt ext/mbstring/tests/zend_multibyte-01.phpt ext/mbstring/tests/zend_multibyte-02.phpt ext/mbstring/tests/zend_multibyte-06.phpt @@ -98,7 +102,9 @@ ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt ext/pdo_sqlite/tests/pdo_sqlite_lastinsertid.phpt ext/phar/tests/031.phpt ext/phar/tests/032.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_metadata_write4.phpt @@ -108,6 +114,7 @@ ext/phar/tests/phar_oo_007.phpt ext/readline/tests/libedit_callback_handler_install_001.phpt ext/readline/tests/libedit_callback_handler_remove_001.phpt ext/simplexml/tests/bug51615.phpt +ext/soap/tests/bug77088.phpt ext/spl/tests/SplFixedArray_setSize_param_float.phpt ext/spl/tests/bug40091.phpt ext/spl/tests/bug44144.phpt diff --git a/dockerfiles/ci/xfail_tests/8.2.list b/dockerfiles/ci/xfail_tests/8.2.list index 4630cafb3a..f63e984ba5 100644 --- a/dockerfiles/ci/xfail_tests/8.2.list +++ b/dockerfiles/ci/xfail_tests/8.2.list @@ -43,8 +43,12 @@ ext/curl/tests/curl_int_cast.phpt ext/curl/tests/curl_postfields_array.phpt ext/curl/tests/curl_setopt_CURLOPT_ACCEPT_ENCODING.phpt ext/date/tests/bug52113.phpt +ext/dom/tests/dom003.phpt +ext/dom/tests/dom_set_attr_node.phpt ext/ffi/tests/gh12905.phpt ext/fileinfo/tests/finfo_open_error.phpt +ext/json/tests/json_decode_exceptions.phpt +ext/json/tests/json_encode_exceptions.phpt ext/mbstring/tests/zend_multibyte-01.phpt ext/mbstring/tests/zend_multibyte-02.phpt ext/mbstring/tests/zend_multibyte-06.phpt @@ -99,7 +103,9 @@ ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt ext/pdo_sqlite/tests/pdo_sqlite_lastinsertid.phpt ext/phar/tests/031.phpt ext/phar/tests/032.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_metadata_write4.phpt @@ -109,6 +115,7 @@ ext/phar/tests/phar_oo_007.phpt ext/readline/tests/libedit_callback_handler_install_001.phpt ext/readline/tests/libedit_callback_handler_remove_001.phpt ext/simplexml/tests/bug51615.phpt +ext/soap/tests/bug77088.phpt ext/spl/tests/bug40091.phpt ext/spl/tests/bug44144.phpt ext/spl/tests/bug48493.phpt @@ -117,6 +124,7 @@ ext/spl/tests/bug65006.phpt ext/spl/tests/bug71236.phpt ext/spl/tests/bug75049.phpt ext/spl/tests/fixedarray_003.phpt +ext/spl/tests/gh8318.phpt ext/spl/tests/gh10011.phpt ext/spl/tests/spl_autoload_002.phpt ext/spl/tests/spl_autoload_004.phpt diff --git a/dockerfiles/ci/xfail_tests/8.3.list b/dockerfiles/ci/xfail_tests/8.3.list index e4b0d92081..cf52679333 100644 --- a/dockerfiles/ci/xfail_tests/8.3.list +++ b/dockerfiles/ci/xfail_tests/8.3.list @@ -46,7 +46,11 @@ ext/curl/tests/curl_int_cast.phpt ext/curl/tests/curl_postfields_array.phpt ext/curl/tests/curl_setopt_CURLOPT_ACCEPT_ENCODING.phpt ext/date/tests/bug52113.phpt +ext/dom/tests/dom_set_attr_node.phpt +ext/dom/tests/dom003.phpt ext/fileinfo/tests/finfo_open_error.phpt +ext/json/tests/json_decode_exceptions.phpt +ext/json/tests/json_encode_exceptions.phpt ext/mbstring/tests/zend_multibyte-01.phpt ext/mbstring/tests/zend_multibyte-02.phpt ext/mbstring/tests/zend_multibyte-06.phpt @@ -101,7 +105,9 @@ ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt ext/pdo_sqlite/tests/pdo_sqlite_lastinsertid.phpt ext/phar/tests/031.phpt ext/phar/tests/032.phpt +ext/phar/tests/cache_list/frontcontroller29.phpt ext/phar/tests/fatal_error_webphar.phpt +ext/phar/tests/frontcontroller29.phpt ext/phar/tests/phar_buildfromiterator10.phpt ext/phar/tests/phar_buildfromiterator8.phpt ext/phar/tests/phar_metadata_write4.phpt @@ -111,6 +117,7 @@ ext/phar/tests/phar_oo_007.phpt ext/readline/tests/libedit_callback_handler_install_001.phpt ext/readline/tests/libedit_callback_handler_remove_001.phpt ext/simplexml/tests/bug51615.phpt +ext/soap/tests/bug77088.phpt ext/spl/tests/bug40091.phpt ext/spl/tests/bug44144.phpt ext/spl/tests/bug48493.phpt @@ -119,6 +126,7 @@ ext/spl/tests/bug65006.phpt ext/spl/tests/bug71236.phpt ext/spl/tests/bug75049.phpt ext/spl/tests/fixedarray_003.phpt +ext/spl/tests/gh8318.phpt ext/spl/tests/gh10011.phpt ext/spl/tests/spl_autoload_002.phpt ext/spl/tests/spl_autoload_004.phpt diff --git a/dockerfiles/ci/xfail_tests/README.md b/dockerfiles/ci/xfail_tests/README.md index 826bdb2fe6..af3a122cef 100644 --- a/dockerfiles/ci/xfail_tests/README.md +++ b/dockerfiles/ci/xfail_tests/README.md @@ -53,6 +53,17 @@ The following tests assert the output of `var_dump($obj)` and fail because we ad - `ext/spl/tests/gh10011.phpt` - `Zend/tests/gc_045.phpt` +## Tests related to exceptions + +- `Zend/tests/generators/exception_during_shutdown.phpt` +- `ext/dom/tests/dom003.phpt` +- `ext/dom/tests/dom_set_attr_node.phpt` +- `ext/intl/tests/bug60192-sort.phpt` +- `ext/phar/tests/frontcontroller29.phpt` +- `ext/phar/tests/cache_list/frontcontroller29.phpt` +- `ext/soap/tests/bug77088.phpt` +- `ext/spl/tests/gh8318.phpt` + --- # Specific tests @@ -85,6 +96,10 @@ Test does http request to shut down server. Distributed tracing headers are injected +## `ext/intl/tests/bug60192-sort.phpt` + +Has a refcounting bug on PHP 7.4 (which gets triggered by the tracer, but isn't caused by it). + ## `ext/pcntl/tests/pcntl_unshare_01.phpt` Disabled on versions: `7.4` (it wasn't there on [7.3-](https://github.com/php/php-src/tree/PHP-7.3/ext/pcntl/tests)). diff --git a/dockerfiles/services/request-replayer/src/index.php b/dockerfiles/services/request-replayer/src/index.php index 8363e9ed55..5afba6fb99 100644 --- a/dockerfiles/services/request-replayer/src/index.php +++ b/dockerfiles/services/request-replayer/src/index.php @@ -91,6 +91,9 @@ function logRequest($message, $data = '') } set_error_handler(function ($number, $message, $errfile, $errline) { + if (error_reporting() == 0) { + return; + } logRequest("Triggered error $number $message in $errfile on line $errline: " . (new \Exception)->getTraceAsString()); trigger_error($message, $number); }); diff --git a/ext/collect_backtrace.c b/ext/collect_backtrace.c new file mode 100644 index 0000000000..a0f7c671fc --- /dev/null +++ b/ext/collect_backtrace.c @@ -0,0 +1,568 @@ +#include "collect_backtrace.h" +#include "ddtrace.h" +#include +#include +#if PHP_VERSION_ID >= 80000 +#include +#endif +#include +#include "compatibility.h" + +// skip_args to not capture args if anyway captured via !DEBUG_BACKTRACE_IGNORE_ARGS +void ddtrace_call_get_locals(zend_execute_data *call, zval *locals_array, bool skip_args) { + zend_op_array *op_array = &call->func->op_array; + +#if PHP_VERSION_ID >= 70100 + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_SYMBOL_TABLE) +#else + if (call->symbol_table) +#endif + { + // array_dup also duplicates indirects away + ZVAL_ARR(locals_array, zend_array_dup(call->symbol_table)); + if (!skip_args) { + // But we want just locals, no args + for (uint32_t i = 0; i < op_array->num_args; ++i) { + zend_hash_del(Z_ARR_P(locals_array), op_array->vars[i]); + } + } + return; + } + + zend_array *locals = zend_new_array(op_array->last_var - op_array->num_args); + for (int i = skip_args ? (int)op_array->num_args : 0; i < op_array->last_var; ++i) { + zval *var = ZEND_CALL_VAR_NUM(call, i); + Z_TRY_ADDREF_P(var); + zend_hash_add_new(locals, op_array->vars[i], var); + } + ZVAL_ARR(locals_array, locals); +} + +/* Copy from zend_builin_functions.c */ +static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) { + uint32_t num_args = ZEND_CALL_NUM_ARGS(call); + + if (num_args) { + uint32_t i = 0; + zval *p = ZEND_CALL_ARG(call, 1); + + array_init_size(arg_array, num_args); + zend_hash_real_init_packed(Z_ARRVAL_P(arg_array)); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(arg_array)) { + if (call->func->type == ZEND_USER_FUNCTION) { + uint32_t first_extra_arg = MIN(num_args, call->func->op_array.num_args); + +#if PHP_VERSION_ID >= 70100 + if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_SYMBOL_TABLE)) +#else + if (call->symbol_table) +#endif + { + /* In case of attached symbol_table, values on stack may be invalid + * and we have to access them through symbol_table + * See: https://bugs.php.net/bug.php?id=73156 + */ + while (i < first_extra_arg) { + zend_string *arg_name = call->func->op_array.vars[i]; + zval original_arg = {0}; + zval *arg = zend_hash_find_ex_ind(call->symbol_table, arg_name, 1); + + if (arg) { + ZVAL_DEREF(arg); + ZVAL_COPY_VALUE(&original_arg, arg); + } else { + ZVAL_NULL(&original_arg); + } + +#if PHP_VERSION_ID >= 80200 + zend_attribute *attribute = zend_get_parameter_attribute_str( + call->func->common.attributes, + "sensitiveparameter", + sizeof("sensitiveparameter") - 1, + i + ); + + bool is_sensitive = attribute != NULL; + + if (is_sensitive) { + zval redacted_arg; + object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); + zend_call_method_with_1_params(Z_OBJ_P(&redacted_arg), zend_ce_sensitive_parameter_value, &zend_ce_sensitive_parameter_value->constructor, "__construct", NULL, &original_arg); + ZEND_HASH_FILL_SET(&redacted_arg); + } else +#endif + { + Z_TRY_ADDREF_P(&original_arg); + ZEND_HASH_FILL_SET(&original_arg); + } + + ZEND_HASH_FILL_NEXT(); + i++; + } + } else { + while (i < first_extra_arg) { + zval original_arg = {0}; + if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) { + zval *arg = p; + ZVAL_DEREF(arg); + ZVAL_COPY_VALUE(&original_arg, arg); + } else { + ZVAL_NULL(&original_arg); + } + +#if PHP_VERSION_ID >= 80200 + zend_attribute *attribute = zend_get_parameter_attribute_str( + call->func->common.attributes, + "sensitiveparameter", + sizeof("sensitiveparameter") - 1, + i + ); + bool is_sensitive = attribute != NULL; + + if (is_sensitive) { + zval redacted_arg; + object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); + zend_call_method_with_1_params(Z_OBJ_P(&redacted_arg), zend_ce_sensitive_parameter_value, &zend_ce_sensitive_parameter_value->constructor, "__construct", NULL, &original_arg); + ZEND_HASH_FILL_SET(&redacted_arg); + } else +#endif + { + Z_TRY_ADDREF_P(&original_arg); + ZEND_HASH_FILL_SET(&original_arg); + } + + ZEND_HASH_FILL_NEXT(); + p++; + i++; + } + } + p = ZEND_CALL_VAR_NUM(call, call->func->op_array.last_var + call->func->op_array.T); + } + + while (i < num_args) { + zval original_arg = {0}; + if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) { + zval *arg = p; + ZVAL_DEREF(arg); + ZVAL_COPY_VALUE(&original_arg, arg); + } else { + ZVAL_NULL(&original_arg); + } + +#if PHP_VERSION_ID >= 80200 + bool is_sensitive = 0; + + if (i < call->func->common.num_args || call->func->common.fn_flags & ZEND_ACC_VARIADIC) { + zend_attribute *attribute = zend_get_parameter_attribute_str( + call->func->common.attributes, + "sensitiveparameter", + sizeof("sensitiveparameter") - 1, + MIN(i, call->func->common.num_args) + ); + is_sensitive = attribute != NULL; + } + + if (is_sensitive) { + zval redacted_arg; + object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); + zend_call_method_with_1_params(Z_OBJ_P(&redacted_arg), zend_ce_sensitive_parameter_value, &zend_ce_sensitive_parameter_value->constructor, "__construct", NULL, &original_arg); + ZEND_HASH_FILL_SET(&redacted_arg); + } else +#endif + { + Z_TRY_ADDREF_P(&original_arg); + ZEND_HASH_FILL_SET(&original_arg); + } + + ZEND_HASH_FILL_NEXT(); + p++; + i++; + } + } ZEND_HASH_FILL_END(); + Z_ARRVAL_P(arg_array)->nNumOfElements = num_args; + } else { + ZVAL_EMPTY_ARRAY(arg_array); + } + +#if PHP_VERSION_ID >= 80000 + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_string *name; + zval *arg; + SEPARATE_ARRAY(arg_array); + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) { + ZVAL_DEREF(arg); + Z_TRY_ADDREF_P(arg); + zend_hash_add_new(Z_ARRVAL_P(arg_array), name, arg); + } ZEND_HASH_FOREACH_END(); + } +#endif +} + +#if PHP_VERSION_ID < 80100 +static inline zend_bool skip_internal_handler(zend_execute_data *skip) /* {{{ */ +{ + return !(skip->func && ZEND_USER_CODE(skip->func->common.type)) + && skip->prev_execute_data + && skip->prev_execute_data->func + && ZEND_USER_CODE(skip->prev_execute_data->func->common.type) + && skip->prev_execute_data->opline->opcode != ZEND_DO_FCALL + && skip->prev_execute_data->opline->opcode != ZEND_DO_ICALL + && skip->prev_execute_data->opline->opcode != ZEND_DO_UCALL + && skip->prev_execute_data->opline->opcode != ZEND_DO_FCALL_BY_NAME + && skip->prev_execute_data->opline->opcode != ZEND_INCLUDE_OR_EVAL; +} +#endif + +/* Copy of zend_fetch_debug_backtrace with ability to gather local variables */ +void ddtrace_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit) +{ + zend_execute_data *call; + zend_object *object; + bool fake_frame = 0; + int lineno, frameno = 0; + zend_function *func; + zend_string *filename; + zend_string *include_filename = NULL; + zval tmp; + HashTable *stack_frame, *prev_stack_frame = NULL; + zend_string *key_locals = NULL; + + array_init(return_value); + + call = EG(current_execute_data); + if (!call) { + return; + } + + if (options & DDTRACE_DEBUG_BACKTRACE_CAPTURE_LOCALS) { + key_locals = zend_string_init(ZEND_STRL("locals"), 0); + } + +#if PHP_VERSION_ID >= 80300 + if (EG(filename_override)) { + // Add the current execution point to the frame so we don't lose it + zend_string *filename_override = EG(filename_override); + zend_long lineno_override = EG(lineno_override); + EG(filename_override) = NULL; + EG(lineno_override) = -1; + + zend_string *filename = zend_get_executed_filename_ex(); + zend_long lineno = zend_get_executed_lineno(); + if (filename && (!zend_string_equals(filename, filename_override) || lineno != lineno_override)) { + stack_frame = zend_new_array(8); + zend_hash_real_init_mixed(stack_frame); + ZVAL_STR_COPY(&tmp, filename); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1); + ZVAL_LONG(&tmp, lineno); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1); + ZVAL_STR_COPY(&tmp, ZSTR_KNOWN(ZEND_STR_CONST_EXPR_PLACEHOLDER)); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1); + ZVAL_ARR(&tmp, stack_frame); + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); + } + + EG(filename_override) = filename_override; + EG(lineno_override) = lineno_override; + } +#endif + + if (skip_last) { + /* skip debug_backtrace() */ + call = call->prev_execute_data; + } + + while (call && (limit == 0 || frameno < limit)) { + if (UNEXPECTED(!call->func)) { + /* This is the fake frame inserted for nested generators. Normally, + * this frame is preceded by the actual generator frame and then + * replaced by zend_generator_check_placeholder_frame() below. + * However, the frame is popped before cleaning the stack frame, + * which is observable by destructors. */ + call = zend_generator_check_placeholder_frame(call); + } + + zend_execute_data *prev = call->prev_execute_data; + + if (!prev) { + /* add frame for a handler call without {main} code */ + if (EXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_TOP_FUNCTION) == 0)) { + break; + } +#if PHP_VERSION_ID < 70100 + } else if (call->func && (call->func->op_array.fn_flags & ZEND_ACC_GENERATOR)) { +#else + } else if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_GENERATOR) != 0)) { +#endif + prev = zend_generator_check_placeholder_frame(prev); + } + +#if PHP_VERSION_ID < 80100 + /* skip internal handler */ + if (prev && skip_internal_handler(prev)) { + prev = prev->prev_execute_data; + } +#endif + +#if PHP_VERSION_ID >= 80400 + /* For frameless calls we add an additional frame for the call itself. */ + if (ZEND_USER_CODE(call->func->type)) { + const zend_op *opline = call->opline; + if (!ZEND_OP_IS_FRAMELESS_ICALL(opline->opcode)) { + goto not_frameless_call; + } + int num_args = ZEND_FLF_NUM_ARGS(opline->opcode); + /* Check if any args were already freed. Skip the frame in that case. */ + if (num_args >= 1) { + zval *arg = zend_get_zval_ptr(opline, opline->op1_type, &opline->op1, call); + if (Z_TYPE_P(arg) == IS_UNDEF) goto not_frameless_call; + } + if (num_args >= 2) { + zval *arg = zend_get_zval_ptr(opline, opline->op2_type, &opline->op2, call); + if (Z_TYPE_P(arg) == IS_UNDEF) goto not_frameless_call; + } + if (num_args >= 3) { + const zend_op *op_data = opline + 1; + zval *arg = zend_get_zval_ptr(op_data, op_data->op1_type, &op_data->op1, call); + if (Z_TYPE_P(arg) == IS_UNDEF) goto not_frameless_call; + } + stack_frame = zend_new_array(8); + zend_hash_real_init_mixed(stack_frame); + zend_function *func = ZEND_FLF_FUNC(opline); + zend_string *name = func->common.function_name; + ZVAL_STRINGL(&tmp, ZSTR_VAL(name), ZSTR_LEN(name)); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1); + /* Steal file and line from the previous frame. */ + if (call->func && ZEND_USER_CODE(call->func->common.type)) { + filename = call->func->op_array.filename; + if (call->opline->opcode == ZEND_HANDLE_EXCEPTION) { + if (EG(opline_before_exception)) { + lineno = EG(opline_before_exception)->lineno; + } else { + lineno = call->func->op_array.line_end; + } + } else { + lineno = call->opline->lineno; + } + ZVAL_STR_COPY(&tmp, filename); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1); + ZVAL_LONG(&tmp, lineno); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1); + if ((options & DDTRACE_DEBUG_BACKTRACE_CAPTURE_LOCALS)) { + ddtrace_call_get_locals(call, &tmp, (options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0); + _zend_hash_append_ex(stack_frame, key_locals, &tmp, 0); + } + if (prev_stack_frame) { + zend_hash_del(prev_stack_frame, ZSTR_KNOWN(ZEND_STR_FILE)); + zend_hash_del(prev_stack_frame, ZSTR_KNOWN(ZEND_STR_LINE)); + zend_hash_del(prev_stack_frame, key_locals); + } + } + if ((options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0) { + HashTable *args = zend_new_array(8); + zend_hash_real_init_mixed(args); + if (num_args >= 1) { + zval *arg = zend_get_zval_ptr(opline, opline->op1_type, &opline->op1, call); + Z_TRY_ADDREF_P(arg); + zend_hash_next_index_insert_new(args, arg); + } + if (num_args >= 2) { + zval *arg = zend_get_zval_ptr(opline, opline->op2_type, &opline->op2, call); + Z_TRY_ADDREF_P(arg); + zend_hash_next_index_insert_new(args, arg); + } + if (num_args >= 3) { + const zend_op *op_data = opline + 1; + zval *arg = zend_get_zval_ptr(op_data, op_data->op1_type, &op_data->op1, call); + Z_TRY_ADDREF_P(arg); + zend_hash_next_index_insert_new(args, arg); + } + ZVAL_ARR(&tmp, args); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_ARGS), &tmp, 1); + } + ZVAL_ARR(&tmp, stack_frame); + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); + } + not_frameless_call: +#else + UNUSED(prev_stack_frame); +#endif + + /* We use _zend_hash_append*() and the array must be preallocated */ + stack_frame = zend_new_array(8); + zend_hash_real_init_mixed(stack_frame); + + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) { + filename = prev->func->op_array.filename; + if (prev->opline->opcode == ZEND_HANDLE_EXCEPTION) { + if (EG(opline_before_exception)) { + lineno = EG(opline_before_exception)->lineno; + } else { + lineno = prev->func->op_array.line_end; + } + } else { + lineno = prev->opline->lineno; + } + ZVAL_STR_COPY(&tmp, filename); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1); + ZVAL_LONG(&tmp, lineno); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1); + if ((options & DDTRACE_DEBUG_BACKTRACE_CAPTURE_LOCALS)) { + ddtrace_call_get_locals(prev, &tmp, (options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0); + _zend_hash_append_ex(stack_frame, key_locals, &tmp, 0); + } + + /* try to fetch args only if an FCALL was just made - elsewise we're in the middle of a function + * and debug_backtrace() might have been called by the error_handler. in this case we don't + * want to pop anything of the argument-stack */ + } else { + zend_execute_data *prev_call = prev; + + while (prev_call) { + zend_execute_data *prev; + + if (prev_call && + prev_call->func && + !ZEND_USER_CODE(prev_call->func->common.type) && + !(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + break; + } + + prev = prev_call->prev_execute_data; + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) { + ZVAL_STR_COPY(&tmp, prev->func->op_array.filename); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1); + ZVAL_LONG(&tmp, prev->opline->lineno); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1); + if ((options & DDTRACE_DEBUG_BACKTRACE_CAPTURE_LOCALS)) { + ddtrace_call_get_locals(prev, &tmp, (options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0); + _zend_hash_append_ex(stack_frame, key_locals, &tmp, 0); + } + break; + } + prev_call = prev; + } + filename = NULL; + } + + func = call->func; + if (!fake_frame && func && func->common.function_name) { + ZVAL_STR_COPY(&tmp, func->common.function_name); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1); + + if (Z_TYPE(call->This) == IS_OBJECT +#if PHP_VERSION_ID < 80000 + && Z_OBJ(call->This) +#endif + ) { + object = Z_OBJ(call->This); + /* $this may be passed into regular internal functions */ + if (func->common.scope) { + ZVAL_STR_COPY(&tmp, func->common.scope->name); +#if PHP_VERSION_ID >= 70300 + } else if (object->handlers->get_class_name == zend_std_get_class_name) { + ZVAL_STR_COPY(&tmp, object->ce->name); +#endif + } else { + ZVAL_STR(&tmp, object->handlers->get_class_name(object)); + } + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_CLASS), &tmp, 1); + if ((options & DEBUG_BACKTRACE_PROVIDE_OBJECT) != 0) { + ZVAL_OBJ_COPY(&tmp, object); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp, 1); + } + + ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_OBJECT_OPERATOR)); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_TYPE), &tmp, 1); + } else if (func->common.scope) { + ZVAL_STR_COPY(&tmp, func->common.scope->name); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_CLASS), &tmp, 1); + ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM)); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_TYPE), &tmp, 1); + } + + if ((options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0 && + func->type != ZEND_EVAL_CODE) { + + debug_backtrace_get_args(call, &tmp); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_ARGS), &tmp, 1); + } + } else { + /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */ + bool build_filename_arg = 1; + zend_string *pseudo_function_name; + uint32_t include_kind = 0; + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type) && prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + include_kind = prev->opline->extended_value; + } + + switch (include_kind) { + case ZEND_EVAL: + pseudo_function_name = ZSTR_KNOWN(ZEND_STR_EVAL); + build_filename_arg = 0; + break; + case ZEND_INCLUDE: + pseudo_function_name = ZSTR_KNOWN(ZEND_STR_INCLUDE); + break; + case ZEND_REQUIRE: + pseudo_function_name = ZSTR_KNOWN(ZEND_STR_REQUIRE); + break; + case ZEND_INCLUDE_ONCE: + pseudo_function_name = ZSTR_KNOWN(ZEND_STR_INCLUDE_ONCE); + break; + case ZEND_REQUIRE_ONCE: + pseudo_function_name = ZSTR_KNOWN(ZEND_STR_REQUIRE_ONCE); + break; + default: + /* Skip dummy frame unless it is needed to preserve filename/lineno info. */ + if (!filename) { + zend_array_destroy(stack_frame); + goto skip_frame; + } + + pseudo_function_name = ZSTR_KNOWN(ZEND_STR_UNKNOWN); + build_filename_arg = 0; + break; + } + + if (build_filename_arg && include_filename) { + zval arg_array; + + array_init(&arg_array); + + /* include_filename always points to the last filename of the last last called-function. + if we have called include in the frame above - this is the file we have included. + */ + + ZVAL_STR_COPY(&tmp, include_filename); + zend_hash_next_index_insert_new(Z_ARRVAL(arg_array), &tmp); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_ARGS), &arg_array, 1); + } + + ZVAL_INTERNED_STR(&tmp, pseudo_function_name); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1); + } + + ZVAL_ARR(&tmp, stack_frame); + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); + frameno++; + prev_stack_frame = stack_frame; + + skip_frame: + if (UNEXPECTED(ZEND_CALL_KIND(call) == ZEND_CALL_TOP_FUNCTION) + && !fake_frame + && prev + && prev->func + && ZEND_USER_CODE(prev->func->common.type) + && prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + fake_frame = 1; + } else { + fake_frame = 0; + include_filename = filename; + call = prev; + } + } + + if (key_locals) { + zend_string_release(key_locals); + } +} diff --git a/ext/collect_backtrace.h b/ext/collect_backtrace.h new file mode 100644 index 0000000000..098f233e2f --- /dev/null +++ b/ext/collect_backtrace.h @@ -0,0 +1,13 @@ +#ifndef DD_COLLECT_BACKTRACE_H +#define DD_COLLECT_BACKTRACE_H + +#include +#include + +#define DDTRACE_DEBUG_BACKTRACE_CAPTURE_LOCALS (1 << 30) + +void ddtrace_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit); +void ddtrace_call_get_locals(zend_execute_data *call, zval *locals_array, bool skip_args); + + +#endif // DD_COLLECT_BACKTRACE_H diff --git a/ext/compat_string.c b/ext/compat_string.c index c29452cac5..1b19c8a2ee 100644 --- a/ext/compat_string.c +++ b/ext/compat_string.c @@ -27,20 +27,32 @@ void ddtrace_downcase_zval(zval *src) { zend_string_release(str); } -zend_string *ddtrace_convert_to_str(zval *op) { +zend_string *ddtrace_convert_to_str(const zval *op) { try_again: switch (Z_TYPE_P(op)) { case IS_UNDEF: return zend_string_init("undef", sizeof("undef") - 1, 0); case IS_NULL: +#if PHP_VERSION_ID < 80000 return zend_string_init("null", sizeof("null") - 1, 0); +#else + return ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE); +#endif case IS_FALSE: +#if PHP_VERSION_ID < 80000 return zend_string_init("false", sizeof("false") - 1, 0); +#else + return ZSTR_KNOWN(ZEND_STR_FALSE); +#endif case IS_TRUE: +#if PHP_VERSION_ID < 80200 return zend_string_init("true", sizeof("true") - 1, 0); +#else + return ZSTR_KNOWN(ZEND_STR_TRUE); +#endif case IS_RESOURCE: return strpprintf(0, "Resource id #" ZEND_LONG_FMT, (zend_long)Z_RES_HANDLE_P(op)); diff --git a/ext/compat_string.h b/ext/compat_string.h index 6cdb75b8ec..25de9a02a3 100644 --- a/ext/compat_string.h +++ b/ext/compat_string.h @@ -20,6 +20,6 @@ size_t ddtrace_spprintf(char **message, size_t max_len, char *format, ...); * overflows due to recursion stemming from our own code as the root. **/ void ddtrace_convert_to_string(zval *dst, zval *src); -zend_string *ddtrace_convert_to_str(zval *op); +zend_string *ddtrace_convert_to_str(const zval *op); #endif // COMPAT_STRING_H diff --git a/ext/compatibility.h b/ext/compatibility.h index 3686198b03..6ed5229a53 100644 --- a/ext/compatibility.h +++ b/ext/compatibility.h @@ -95,7 +95,76 @@ static inline zend_long zval_get_long(zval *op) { #define PHP_DOUBLE_MAX_LENGTH 1080 #endif +enum { + ZEND_STR_TRACE, + ZEND_STR_LINE, + ZEND_STR_FILE, + ZEND_STR_MESSAGE, + ZEND_STR_CODE, + ZEND_STR_TYPE, + ZEND_STR_FUNCTION, + ZEND_STR_OBJECT, + ZEND_STR_CLASS, + ZEND_STR_OBJECT_OPERATOR, + ZEND_STR_PAAMAYIM_NEKUDOTAYIM, + ZEND_STR_ARGS, + ZEND_STR_UNKNOWN, + ZEND_STR_EVAL, + ZEND_STR_INCLUDE, + ZEND_STR_REQUIRE, + ZEND_STR_INCLUDE_ONCE, + ZEND_STR_REQUIRE_ONCE, + ZEND_STR_PREVIOUS, + ZEND_STR__LAST +}; +extern zend_string *ddtrace_known_strings[ZEND_STR__LAST]; +#define ZSTR_KNOWN(idx) ddtrace_known_strings[idx] + #define zend_declare_class_constant_ex(ce, name, value, access_type, doc_comment) zend_declare_class_constant(ce, ZSTR_VAL(name), ZSTR_LEN(name), value) + +// copied from PHP-7.0 source, but converting zend_long * to uint32_t * - assuming little endian +static inline void zend_property_guard_dtor(zval *el) { + efree_size(Z_PTR_P(el), sizeof(zend_ulong)); +} +static inline uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member) { + HashTable *guards; + zend_long stub, *guard; + + ZEND_ASSERT(GC_FLAGS(zobj) & IS_OBJ_USE_GUARDS); + if (GC_FLAGS(zobj) & IS_OBJ_HAS_GUARDS) { + guards = Z_PTR(zobj->properties_table[zobj->ce->default_properties_count]); + ZEND_ASSERT(guards != NULL); + if ((guard = zend_hash_find_ptr(guards, member)) != NULL) { + return (uint32_t *)(guard + 1) - 1; + } + } else { + ALLOC_HASHTABLE(guards); + zend_hash_init(guards, 8, NULL, zend_property_guard_dtor, 0); + Z_PTR(zobj->properties_table[zobj->ce->default_properties_count]) = guards; + GC_FLAGS(zobj) |= IS_OBJ_HAS_GUARDS; + } + + stub = 0; + guard = zend_hash_add_mem(guards, member, &stub, sizeof(zend_ulong)); + return (uint32_t *)(guard + 1) - 1; +} + +static inline zval *zend_read_property_ex(zend_class_entry *scope, zval *object, zend_string *name, zend_bool silent, zval *rv) { + zval property, *value; + zend_class_entry *old_scope = EG(scope); + + EG(scope) = scope; + + if (!Z_OBJ_HT_P(object)->read_property) { + zend_error_noreturn(E_CORE_ERROR, "Property %s of class %s cannot be read", ZSTR_VAL(name), ZSTR_VAL(Z_OBJCE_P(object)->name)); + } + + ZVAL_STR(&property, name); + value = Z_OBJ_HT_P(object)->read_property(object, &property, silent?BP_VAR_IS:BP_VAR_R, NULL, rv); + + EG(scope) = old_scope; + return value; +} #endif #if PHP_VERSION_ID < 70200 @@ -135,6 +204,15 @@ static inline zend_string *php_base64_encode_str(const zend_string *str) { } #define DD_PARAM_PROLOGUE(deref, separate) Z_PARAM_PROLOGUE(deref) + +#define ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(ht, _key, _val) \ + ZEND_HASH_REVERSE_FOREACH(ht, 0); \ + _key = _p->key; \ + _val = _z; + +#if PHP_VERSION_ID >= 70100 +#define ZSTR_KNOWN(idx) CG(known_strings)[idx] +#endif #else #define DD_PARAM_PROLOGUE Z_PARAM_PROLOGUE #endif @@ -156,12 +234,7 @@ static inline HashTable *zend_new_array(uint32_t nSize) { return ht; } -#define DD_ZVAL_EMPTY_ARRAY(z) do { \ - zval *__z = (z); \ - Z_ARR_P(__z) = zend_new_array(0); \ - Z_TYPE_INFO_P(__z) = IS_ARRAY; \ - } while (0) -#define ZVAL_EMPTY_ARRAY DD_ZVAL_EMPTY_ARRAY +#define ZVAL_EMPTY_ARRAY(z) ZVAL_ARR(z, zend_new_array(0)) #define GC_IS_RECURSIVE(gc) ((gc)->u.v.nApplyCount > 0) #define GC_PROTECT_RECURSION(gc) (++(gc)->u.v.nApplyCount) @@ -174,6 +247,8 @@ static inline HashTable *zend_new_array(uint32_t nSize) { #define ZEND_CLOSURE_OBJECT(op_array) \ ((zend_object*)((char*)(op_array) - sizeof(zend_object))) +#define zend_std_read_dimension std_object_handlers.read_dimension + // make ZEND_STRL work #undef zend_hash_str_update #define zend_hash_str_update(...) _zend_hash_str_update(__VA_ARGS__ ZEND_FILE_LINE_CC) @@ -184,6 +259,12 @@ static inline HashTable *zend_new_array(uint32_t nSize) { #undef zend_hash_str_add_new #define zend_hash_str_add_new(...) _zend_hash_str_add_new(__VA_ARGS__ ZEND_FILE_LINE_CC) +#define zend_hash_real_init_packed(ht) zend_hash_real_init(ht, 1) +#define zend_hash_real_init_mixed(ht) zend_hash_real_init(ht, 0) +#define _zend_hash_append_ex(ht, key, zv, known) _zend_hash_append(ht, key, zv) +#define zend_hash_find_ex(ht, key, known) zend_hash_find(ht, key) +#define zend_hash_find_ex_ind(ht, key, known) zend_hash_find_ind(ht, key) + #define smart_str_free_ex(str, persistent) smart_str_free(str) static inline zend_bool zend_ini_parse_bool(zend_string *str) { @@ -195,12 +276,42 @@ static inline zend_bool zend_ini_parse_bool(zend_string *str) { return atoi(ZSTR_VAL(str)) != 0; } } + +static inline zend_string *zend_ini_get_value(zend_string *name) { + zend_ini_entry *ini_entry; + + ini_entry = zend_hash_find_ptr(EG(ini_directives), name); + if (ini_entry) { + return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC(); + } else { + return NULL; + } +} + +#define ZVAL_DEINDIRECT(z) do { \ + if (Z_TYPE_P(z) == IS_INDIRECT) { \ + (z) = Z_INDIRECT_P(z); \ + } \ + } while (0) + #endif #if PHP_VERSION_ID < 70400 #define ZEND_THIS (&EX(This)) #define Z_PROP_FLAG_P(z) Z_EXTRA_P(z) +#define ZVAL_COPY_VALUE_PROP ZVAL_COPY_VALUE + +#define ZEND_HASH_FILL_SET(_val) do { \ + ZVAL_COPY_VALUE(&__fill_bkt->val, _val); \ + __fill_bkt->h = (__fill_idx); \ + __fill_bkt->key = NULL; \ + } while (0) + +#define ZEND_HASH_FILL_NEXT() do { \ + __fill_bkt++; \ + __fill_idx++; \ + } while (0) #define DD_PARAM_ERROR_CODE error_code #else @@ -229,6 +340,8 @@ static inline const zend_function *dd_zend_get_closure_method_def(zend_object *o } #define zend_get_closure_method_def dd_zend_get_closure_method_def +#define instanceof_function_slow instanceof_function + #define ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, classname, allow_null, default_value) ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) #define ZEND_ARG_OBJ_TYPE_MASK(pass_by_ref, name, class_name, type_mask, default_value) ZEND_ARG_INFO(pass_by_ref, name) #define zend_declare_typed_property(ce, name, default, visibility, doc_comment, type) zend_declare_property_ex(ce, name, default, visibility, doc_comment); (void)type @@ -329,6 +442,24 @@ static zend_always_inline void zend_array_release(zend_array *array) #define ZEND_ARG_SEND_MODE(arg_info) (arg_info)->pass_by_reference #define zend_value_error zend_type_error + +#define zend_update_property_ex(scope, object, name, value) do { zval _zv; ZVAL_OBJ(&_zv, object); zend_update_property_ex(scope, &_zv, name, value); } while (0) + +#define is_numeric_string_ex(str, length, lval, dval, allow_errors, oflow_info, trailing_data) is_numeric_string_ex(str, length, lval, dval, allow_errors, oflow_info) + +static zend_always_inline int zend_compare(zval *op1, zval *op2) { + zval result; + if (compare_function(&result, op1, op2) == FAILURE) { + return 1; + } + return Z_LVAL(result); +} + +// const cast +#undef Z_OBJPROP_P +#define Z_OBJPROP_P(zv) Z_OBJPROP(*(zval *)(zv)) +#undef Z_OBJDEBUG_P +#define Z_OBJDEBUG_P(zv, is_temp) Z_OBJDEBUG(*(zval *)(zv), is_temp) #endif #if PHP_VERSION_ID < 80100 @@ -360,6 +491,32 @@ static inline void smart_str_append_double(smart_str *str, double num, int preci } } +#define zend_hash_find_known_hash zend_hash_find + +static zend_always_inline bool zend_array_is_list(zend_array *array) { + zend_long expected_idx = 0; + zend_long num_idx; + zend_string* str_idx; + /* Empty arrays are lists */ + if (zend_hash_num_elements(array) == 0) { + return 1; + } + +#if PHP_VERSION_ID >= 70100 + if (HT_IS_PACKED(array) && HT_IS_WITHOUT_HOLES(array)) { + return 1; + } +#endif + + /* Check if the list could theoretically be repacked */ + ZEND_HASH_FOREACH_KEY(array, num_idx, str_idx) { + if (str_idx != NULL || num_idx != expected_idx++) { + return 0; + } + } ZEND_HASH_FOREACH_END(); + + return 1; +} #endif #if PHP_VERSION_ID < 80200 @@ -407,6 +564,8 @@ static inline zend_string *ddtrace_strpprintf(size_t max_len, const char *format #define zend_strpprintf ddtrace_strpprintf #define ZEND_HASH_ELEMENT(ht, idx) (&ht->arData[idx].val) +#define ZEND_HASH_MAP_FOREACH_PTR ZEND_HASH_FOREACH_PTR +#define ZEND_HASH_MAP_FOREACH_STR_KEY_VAL ZEND_HASH_FOREACH_STR_KEY_VAL #if PHP_VERSION_ID >= 80000 #define zend_weakrefs_hash_add zend_weakrefs_hash_add_fallback @@ -444,12 +603,24 @@ static zend_always_inline zend_result zend_call_function_with_return_value(zend_ #define Z_PARAM_ZVAL_OR_NULL(dest) Z_PARAM_ZVAL_EX(dest, 1, 0) +#define ZEND_GUARD_PROPERTY_MASK 0xf + +// strip const +#if PHP_VERSION_ID < 70300 +#undef zval_get_double +#define zval_get_double(zv) _zval_get_double((zval *)(zv)) +#else +#define zval_get_double(zv) zval_get_double((zval *)(zv)) +#endif + #endif #if PHP_VERSION_ID < 80400 #define zend_parse_arg_func(arg, dest_fci, dest_fcc, check_null, error, free_trampoline) zend_parse_arg_func(arg, dest_fci, dest_fcc, check_null, error) #undef ZEND_RAW_FENTRY #define ZEND_RAW_FENTRY(zend_name, name, arg_info, flags, ...) { zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags }, + +#define hasThis() (Z_TYPE_P(ZEND_THIS) == IS_OBJECT) #endif #endif // DD_COMPATIBILITY_H diff --git a/ext/configuration.c b/ext/configuration.c index f94763b768..240c050ec7 100644 --- a/ext/configuration.c +++ b/ext/configuration.c @@ -8,6 +8,9 @@ #include "sidecar.h" #include #include +#include "sidecar.h" + +ZEND_EXTERN_MODULE_GLOBALS(ddtrace); #define DD_TO_DATADOG_INC 5 /* "DD" expanded to "datadog" */ @@ -43,9 +46,10 @@ DD_CONFIGURATION #undef CALIAS #define CONFIG(...) #define ELEMENT(arg) 1, -#define CALIASES(...) APPLY_N(ELEMENT, ##__VA_ARGS__) +#define CALIASES(...) (APPLY_N(ELEMENT, ##__VA_ARGS__)) +#define ELEMENTS(...) __VA_ARGS__ #define CALIAS(type, name, default, aliases, ...) \ - _Static_assert(sizeof((uint8_t[]){aliases}) < ZAI_CONFIG_NAMES_COUNT_MAX, \ + _Static_assert(sizeof((uint8_t[]){ELEMENTS aliases}) < ZAI_CONFIG_NAMES_COUNT_MAX, \ #name " has more than the allowed ZAI_CONFIG_NAMES_COUNT_MAX alias names"); DD_CONFIGURATION #undef CALIAS @@ -90,22 +94,37 @@ static bool dd_parse_sampling_rules_format(zai_str value, zval *decoded_value, b return true; } +#define INI_CHANGE_DYNAMIC_CONFIG(name, config) \ + static bool ddtrace_alter_##name(zval *old_value, zval *new_value, zend_string *new_str) { \ + UNUSED(old_value, new_value); \ + if (!DDTRACE_G(remote_config_state)) { \ + return true; \ + } \ + return ddog_remote_config_alter_dynamic_config(DDTRACE_G(remote_config_state), DDOG_CHARSLICE_C(config), dd_zend_string_to_CharSlice(new_str)); \ + } + +INI_CHANGE_DYNAMIC_CONFIG(DD_TRACE_HEADER_TAGS, "datadog.trace.header_tags") +INI_CHANGE_DYNAMIC_CONFIG(DD_TRACE_SAMPLE_RATE, "datadog.trace.sample_rate") +INI_CHANGE_DYNAMIC_CONFIG(DD_TRACE_LOGS_ENABLED, "datadog.logs_injection") + #define CALIAS_EXPAND(name) {.ptr = name, .len = sizeof(name) - 1}, +#define EXPAND_FIRST(arg, ...) arg +#define EXPAND_CALL(macro, args) macro args // I hate the "traditional" MSVC preprocessor +#define EXPAND_IDENTITY(...) __VA_ARGS__ #ifndef _WIN32 // Allow for partially defined struct initialization here #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #else #define CONFIG(...) -#define CALIASES(...) {APPLY_N(CALIAS_EXPAND, ##__VA_ARGS__)} -#define CALIAS(type, name, default, aliases, ...) const zai_str dd_config_aliases_##name[] = aliases; +#define CALIASES(...) ({APPLY_N(CALIAS_EXPAND, ##__VA_ARGS__)}) +#define CALIAS(type, name, default, aliases, ...) const zai_str dd_config_aliases_##name[] = EXPAND_CALL(EXPAND_IDENTITY, EXPAND_FIRST(aliases)); DD_CONFIGURATION #undef CALIAS #undef CONFIG #endif #define CUSTOM(...) CUSTOM -#define EXPAND_CALL(macro, args) macro args // I hate the "traditional" MSVC preprocessor #define CONFIG(type, name, ...) EXPAND_CALL(ZAI_CONFIG_ENTRY, (DDTRACE_CONFIG_##name, name, type, __VA_ARGS__)), #ifndef _WIN32 #define CALIASES(...) ((zai_str[]){APPLY_N(CALIAS_EXPAND, ##__VA_ARGS__)}) @@ -145,6 +164,8 @@ static void dd_ini_env_to_ini_name(const zai_str env_name, zai_config_name *ini_ ini_name->ptr[sizeof("datadog.trace") - 1] = '.'; } else if (env_name.ptr == strstr(env_name.ptr, "DD_APPSEC_")) { ini_name->ptr[sizeof("datadog.appsec") - 1] = '.'; + } else if (env_name.ptr == strstr(env_name.ptr, "DD_DYNAMIC_INSTRUMENTATION_")) { + ini_name->ptr[sizeof("datadog.dynamic_instrumentation") - 1] = '.'; } } else { ini_name->len = 0; @@ -167,6 +188,7 @@ bool ddtrace_config_minit(int module_number) { // arduous way of accessing the decoded_value directly from zai_config_memoized_entries. zai_config_first_time_rinit(false); + ddtrace_alter_dd_trace_debug(NULL, &zai_config_memoized_entries[DDTRACE_CONFIG_DD_TRACE_DEBUG].decoded_value, NULL); ddtrace_log_ginit(); return true; } diff --git a/ext/configuration.h b/ext/configuration.h index 3962ce6ffd..9639858923 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -67,18 +67,18 @@ enum ddtrace_sampling_rules_format { #define DD_CFG_STR(str) #str #define DD_CFG_EXPSTR(str) DD_CFG_STR(str) -#define INTEGRATION_ALIAS(id, _, initial, alias) \ - CALIAS(BOOL, DD_TRACE_##id##_ENABLED, initial, CALIASES(DD_CFG_STR(alias))) +#define INTEGRATION_ALIAS(id, _, initial, ...) \ + CALIAS(BOOL, id, initial, __VA_ARGS__) #define INTEGRATION_WITH_DEFAULT(id, _, initial) \ - CONFIG(BOOL, DD_TRACE_##id##_ENABLED, initial) + CONFIG(BOOL, id, initial) #define INTEGRATION_NORMAL(id, _) \ - CONFIG(BOOL, DD_TRACE_##id##_ENABLED, "true") -#define GET_INTEGRATION_CONFIG_MACRO(_1, _2, DEFAULT, NAME, ...) NAME + CONFIG(BOOL, id, "true") +#define GET_INTEGRATION_CONFIG_MACRO(_1, _2, _3, DEFAULT, NAME, ...) NAME #if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL #define GET_INTEGRATION_CONFIG_MACRO_EXPAND(...) __VA_ARGS__ -#define INTEGRATION_CONFIG_ACTIVE(id, ...) GET_INTEGRATION_CONFIG_MACRO_EXPAND(GET_INTEGRATION_CONFIG_MACRO(__VA_ARGS__, INTEGRATION_ALIAS, INTEGRATION_WITH_DEFAULT, INTEGRATION_NORMAL))GET_INTEGRATION_CONFIG_MACRO_EXPAND((id, __VA_ARGS__)) +#define INTEGRATION_CONFIG_ACTIVE(id, ...) GET_INTEGRATION_CONFIG_MACRO_EXPAND(GET_INTEGRATION_CONFIG_MACRO(__VA_ARGS__, INTEGRATION_ALIAS, INTEGRATION_ALIAS, INTEGRATION_WITH_DEFAULT, INTEGRATION_NORMAL))GET_INTEGRATION_CONFIG_MACRO_EXPAND((DD_TRACE_##id##_ENABLED, __VA_ARGS__)) #else -#define INTEGRATION_CONFIG_ACTIVE(id, ...) GET_INTEGRATION_CONFIG_MACRO(__VA_ARGS__, INTEGRATION_ALIAS, INTEGRATION_WITH_DEFAULT, INTEGRATION_NORMAL)(id, __VA_ARGS__) +#define INTEGRATION_CONFIG_ACTIVE(id, ...) GET_INTEGRATION_CONFIG_MACRO(__VA_ARGS__, INTEGRATION_ALIAS, INTEGRATION_ALIAS, INTEGRATION_WITH_DEFAULT, INTEGRATION_NORMAL)(DD_TRACE_##id##_ENABLED, __VA_ARGS__) #endif #define INTEGRATION(id, ...) \ INTEGRATION_CONFIG_ACTIVE(id, __VA_ARGS__) \ @@ -131,6 +131,9 @@ enum ddtrace_sampling_rules_format { CONFIG(BOOL, DD_TRACE_DB_CLIENT_SPLIT_BY_INSTANCE, "false") \ CONFIG(BOOL, DD_TRACE_HTTP_CLIENT_SPLIT_BY_DOMAIN, "false") \ CONFIG(BOOL, DD_TRACE_REDIS_CLIENT_SPLIT_BY_HOST, "false") \ + CONFIG(BOOL, DD_EXCEPTION_REPLAY_ENABLED, "false") \ + CONFIG(INT, DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES, "-1") \ + CONFIG(INT, DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS, "3600") \ CONFIG(STRING, DD_TRACE_MEMORY_LIMIT, "") \ CONFIG(BOOL, DD_TRACE_REPORT_HOSTNAME, "false") \ CONFIG(BOOL, DD_TRACE_FLUSH_COLLECT_CYCLES, "false") \ @@ -147,13 +150,13 @@ enum ddtrace_sampling_rules_format { CONFIG(SET, DD_TRACE_HTTP_URL_QUERY_PARAM_ALLOWED, "*") \ CONFIG(SET, DD_TRACE_HTTP_POST_DATA_PARAM_ALLOWED, "") \ CONFIG(INT, DD_TRACE_RATE_LIMIT, "0", .ini_change = zai_config_system_ini_change) \ - CONFIG(DOUBLE, DD_TRACE_SAMPLE_RATE, "-1", \ + CONFIG(DOUBLE, DD_TRACE_SAMPLE_RATE, "-1", .ini_change = ddtrace_alter_DD_TRACE_SAMPLE_RATE, \ .env_config_fallback = ddtrace_conf_otel_sample_rate) \ CONFIG(JSON, DD_TRACE_SAMPLING_RULES, "[]") \ CONFIG(CUSTOM(INT), DD_TRACE_SAMPLING_RULES_FORMAT, "glob", .parser = dd_parse_sampling_rules_format) \ CONFIG(JSON, DD_SPAN_SAMPLING_RULES, "[]") \ CONFIG(STRING, DD_SPAN_SAMPLING_RULES_FILE, "", .ini_change = ddtrace_alter_sampling_rules_file_config) \ - CONFIG(SET_LOWERCASE, DD_TRACE_HEADER_TAGS, "") \ + CONFIG(SET_LOWERCASE, DD_TRACE_HEADER_TAGS, "", .ini_change = ddtrace_alter_DD_TRACE_HEADER_TAGS) \ CONFIG(INT, DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH, "512") \ CONFIG(MAP, DD_TRACE_PEER_SERVICE_MAPPING, "") \ CONFIG(BOOL, DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED, "false") \ @@ -227,7 +230,12 @@ enum ddtrace_sampling_rules_format { CONFIG(INT, DD_OPENAI_SPAN_CHAR_LIMIT, "128") \ CONFIG(DOUBLE, DD_OPENAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE, "1.0") \ CONFIG(DOUBLE, DD_OPENAI_LOG_PROMPT_COMPLETION_SAMPLE_RATE, "0.1") \ - CONFIG(BOOL, DD_INJECT_FORCE, "false", .ini_change = zai_config_system_ini_change) \ + CONFIG(BOOL, DD_INJECT_FORCE, "false", .ini_change = zai_config_system_ini_change) \ + CONFIG(DOUBLE, DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS, "5", .ini_change = zai_config_system_ini_change) \ + CONFIG(BOOL, DD_REMOTE_CONFIG_ENABLED, "true", .ini_change = zai_config_system_ini_change) \ + CONFIG(BOOL, DD_DYNAMIC_INSTRUMENTATION_ENABLED, "false", .ini_change = zai_config_system_ini_change) \ + CONFIG(SET, DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS, "", .ini_change = zai_config_system_ini_change) \ + CONFIG(SET, DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES, "", .ini_change = zai_config_system_ini_change) \ DD_INTEGRATIONS #ifndef _WIN32 diff --git a/ext/ddtrace.c b/ext/ddtrace.c index dd81d580f7..8a2cb02c63 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -63,6 +63,7 @@ #include "priority_sampling/priority_sampling.h" #include "random.h" #include "autoload_php_files.h" +#include "remote_config.h" #include "serializer.h" #include "sidecar.h" #ifndef _WIN32 @@ -71,6 +72,7 @@ #include "span.h" #include "startup_logging.h" #include "telemetry.h" +#include "threads.h" #include "tracer_tag_propagation/tracer_tag_propagation.h" #include "user_request.h" #include "zend_hrtime.h" @@ -84,24 +86,25 @@ // On PHP 7 we cannot declare arrays as internal values. Assign null and handle in create_object where necessary. #if PHP_VERSION_ID < 80000 +#pragma push_macro("ZVAL_EMPTY_ARRAY") #undef ZVAL_EMPTY_ARRAY #define ZVAL_EMPTY_ARRAY ZVAL_NULL #endif // CG(empty_string) is not accessible during MINIT (in ZTS at least) #if PHP_VERSION_ID < 70200 +#pragma push_macro("ZVAL_EMPTY_STRING") #undef ZVAL_EMPTY_STRING #define ZVAL_EMPTY_STRING(z) ZVAL_NEW_STR(z, zend_string_init("", 0, 1)) #endif #include "ddtrace_arginfo.h" #include "distributed_tracing_headers.h" +#include "live_debugger.h" #if PHP_VERSION_ID < 70200 -#undef ZVAL_EMPTY_STRING -#define ZVAL_EMPTY_STRING(z) ZVAL_INTERNED_STR(z, ZSTR_EMPTY_ALLOC()) +#pragma pop_macro("ZVAL_EMPTY_STRING") #endif #if PHP_VERSION_ID < 80000 -#undef ZVAL_EMPTY_ARRAY -#define ZVAL_EMPTY_ARRAY DD_ZVAL_EMPTY_ARRAY +#pragma pop_macro("ZVAL_EMPTY_ARRAY") #endif // For manual ZPP @@ -358,8 +361,9 @@ bool dd_save_sampling_rules_file_config(zend_string *path, int modify_type, int return altered; } -bool ddtrace_alter_sampling_rules_file_config(zval *old_value, zval *new_value) { +bool ddtrace_alter_sampling_rules_file_config(zval *old_value, zval *new_value, zend_string *new_str) { (void) old_value; + (void) new_str; if (Z_STRLEN_P(new_value) == 0) { return true; } @@ -367,8 +371,8 @@ bool ddtrace_alter_sampling_rules_file_config(zval *old_value, zval *new_value) return dd_save_sampling_rules_file_config(Z_STR_P(new_value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME); } -static inline bool dd_alter_prop(size_t prop_offset, zval *old_value, zval *new_value) { - UNUSED(old_value); +static inline bool dd_alter_prop(size_t prop_offset, zval *old_value, zval *new_value, zend_string *new_str) { + UNUSED(old_value, new_str); ddtrace_span_properties *pspan = ddtrace_active_span_props(); while (pspan) { @@ -381,14 +385,14 @@ static inline bool dd_alter_prop(size_t prop_offset, zval *old_value, zval *new_ return true; } -bool ddtrace_alter_dd_service(zval *old_value, zval *new_value) { - return dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_service), old_value, new_value); +bool ddtrace_alter_dd_service(zval *old_value, zval *new_value, zend_string *new_str) { + return dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_service), old_value, new_value, new_str); } -bool ddtrace_alter_dd_env(zval *old_value, zval *new_value) { - return dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_env), old_value, new_value); +bool ddtrace_alter_dd_env(zval *old_value, zval *new_value, zend_string *new_str) { + return dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_env), old_value, new_value, new_str); } -bool ddtrace_alter_dd_version(zval *old_value, zval *new_value) { - return dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_version), old_value, new_value); +bool ddtrace_alter_dd_version(zval *old_value, zval *new_value, zend_string *new_str) { + return dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_version), old_value, new_value, new_str); } static void dd_activate_once(void) { @@ -416,10 +420,10 @@ static void dd_activate_once(void) { if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED() || get_global_DD_TRACE_SIDECAR_TRACE_SENDER() || appsec_module) #endif { - bool modules_activated = PG(modules_activated); - PG(modules_activated) = false; + bool request_startup = PG(during_request_startup); + PG(during_request_startup) = false; ddtrace_sidecar_setup(); - PG(modules_activated) = modules_activated; + PG(during_request_startup) = request_startup; } #ifndef _WIN32 if (!get_global_DD_TRACE_SIDECAR_TRACE_SENDER()) { @@ -471,6 +475,8 @@ static void ddtrace_activate(void) { ddtrace_sidecar_ensure_active(); } + ddtrace_sidecar_rinit(); + zend_string *sampling_rules_file = get_DD_SPAN_SAMPLING_RULES_FILE(); if (ZSTR_LEN(sampling_rules_file) > 0 && !zend_string_equals(get_global_DD_SPAN_SAMPLING_RULES_FILE(), sampling_rules_file)) { dd_save_sampling_rules_file_config(sampling_rules_file, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); @@ -534,6 +540,12 @@ static PHP_GINIT_FUNCTION(ddtrace) { ZEND_TSRMLS_CACHE_UPDATE(); #endif php_ddtrace_init_globals(ddtrace_globals); +#if PHP_VERSION_ID < 70100 + zai_vm_interrupt = &ddtrace_globals->zai_vm_interrupt; +#endif +#if ZTS + ddtrace_thread_ginit(); +#endif zai_hook_ginit(); zend_hash_init(&ddtrace_globals->git_metadata, 8, unused, (dtor_func_t)ddtrace_git_metadata_dtor, 1); } @@ -607,8 +619,14 @@ static void dd_clean_main_thread_locals() { #endif static PHP_GSHUTDOWN_FUNCTION(ddtrace) { - if (ddtrace_globals->remote_config_reader) { - ddog_agent_remote_config_reader_drop(ddtrace_globals->remote_config_reader); +#if ZTS + ddtrace_thread_gshutdown(); +#endif + if (ddtrace_globals->agent_config_reader) { + ddog_agent_remote_config_reader_drop(ddtrace_globals->agent_config_reader); + } + if (ddtrace_globals->remote_config_state) { + ddog_shutdown_remote_config(ddtrace_globals->remote_config_state); } zai_hook_gshutdown(); if (ddtrace_globals->telemetry_buffer) { @@ -1026,6 +1044,7 @@ static zval *ddtrace_root_span_data_write(zend_object *object, zend_string *memb #endif ddtrace_root_span_data *span = ROOTSPANDATA(obj); zval zv; + bool root_span_data_changed = false; if (zend_string_equals_literal(prop_name, "parentId")) { if (Z_TYPE_P(value) == IS_LONG && Z_LVAL_P(value)) { span->parent_id = (uint64_t) Z_LVAL_P(value); @@ -1047,14 +1066,32 @@ static zval *ddtrace_root_span_data_write(zend_object *object, zend_string *memb }; value = &span->property_id; } + } else if (zend_string_equals_literal(prop_name, "service")) { + if (ddtrace_span_is_entrypoint_root(&span->span) && !zend_is_identical(&span->property_service, value)) { + root_span_data_changed = true; + } + } else if (zend_string_equals_literal(prop_name, "env")) { + if (ddtrace_span_is_entrypoint_root(&span->span) && !zend_is_identical(&span->property_env, value)) { + root_span_data_changed = true; + } + } else if (zend_string_equals_literal(prop_name, "version")) { + if (ddtrace_span_is_entrypoint_root(&span->span) && !zend_is_identical(&span->property_version, value)) { + root_span_data_changed = true; + } } else if (zend_string_equals_literal(prop_name, "samplingPriority")) { span->explicit_sampling_priority = zval_get_long(value) != DDTRACE_PRIORITY_SAMPLING_UNKNOWN; } #if PHP_VERSION_ID >= 70400 - return ddtrace_span_data_readonly(object, member, value, cache_slot); + zval *ret = ddtrace_span_data_readonly(object, member, value, cache_slot); #else ddtrace_span_data_readonly(object, member, value, cache_slot); +#endif + if (root_span_data_changed) { + ddtrace_sidecar_submit_root_span_data(); + } +#if PHP_VERSION_ID >= 70400 + return ret; #endif } @@ -1203,6 +1240,31 @@ static void dd_disable_if_incompatible_sapi_detected(void) { } } +#if PHP_VERSION_ID < 70100 +zend_string *ddtrace_known_strings[ZEND_STR__LAST]; +void ddtrace_init_known_strings(void) { + ddtrace_known_strings[ZEND_STR_TRACE] = zend_string_init_interned(ZEND_STRL("trace"), 1); + ddtrace_known_strings[ZEND_STR_LINE] = zend_string_init_interned(ZEND_STRL("line"), 1); + ddtrace_known_strings[ZEND_STR_FILE] = zend_string_init_interned(ZEND_STRL("file"), 1); + ddtrace_known_strings[ZEND_STR_MESSAGE] = zend_string_init_interned(ZEND_STRL("message"), 1); + ddtrace_known_strings[ZEND_STR_CODE] = zend_string_init_interned(ZEND_STRL("code"), 1); + ddtrace_known_strings[ZEND_STR_TYPE] = zend_string_init_interned(ZEND_STRL("type"), 1); + ddtrace_known_strings[ZEND_STR_FUNCTION] = zend_string_init_interned(ZEND_STRL("function"), 1); + ddtrace_known_strings[ZEND_STR_OBJECT] = zend_string_init_interned(ZEND_STRL("object"), 1); + ddtrace_known_strings[ZEND_STR_CLASS] = zend_string_init_interned(ZEND_STRL("class"), 1); + ddtrace_known_strings[ZEND_STR_OBJECT_OPERATOR] = zend_string_init_interned(ZEND_STRL("->"), 1); + ddtrace_known_strings[ZEND_STR_PAAMAYIM_NEKUDOTAYIM] = zend_string_init_interned(ZEND_STRL("::"), 1); + ddtrace_known_strings[ZEND_STR_ARGS] = zend_string_init_interned(ZEND_STRL("args"), 1); + ddtrace_known_strings[ZEND_STR_UNKNOWN] = zend_string_init_interned(ZEND_STRL("unknown"), 1); + ddtrace_known_strings[ZEND_STR_EVAL] = zend_string_init_interned(ZEND_STRL("eval"), 1); + ddtrace_known_strings[ZEND_STR_INCLUDE] = zend_string_init_interned(ZEND_STRL("include"), 1); + ddtrace_known_strings[ZEND_STR_REQUIRE] = zend_string_init_interned(ZEND_STRL("require"), 1); + ddtrace_known_strings[ZEND_STR_INCLUDE_ONCE] = zend_string_init_interned(ZEND_STRL("include_once"), 1); + ddtrace_known_strings[ZEND_STR_REQUIRE_ONCE] = zend_string_init_interned(ZEND_STRL("require_once"), 1); + ddtrace_known_strings[ZEND_STR_PREVIOUS] = zend_string_init_interned(ZEND_STRL("previous"), 1); +} +#endif + static PHP_MINIT_FUNCTION(ddtrace) { UNUSED(type); @@ -1233,6 +1295,10 @@ static PHP_MINIT_FUNCTION(ddtrace) { ddtrace_startup_hrtime(); #endif +#if PHP_VERSION_ID < 70100 + ddtrace_init_known_strings(); +#endif + register_ddtrace_symbols(module_number); REGISTER_INI_ENTRIES(); @@ -1316,6 +1382,9 @@ static PHP_MINIT_FUNCTION(ddtrace) { dd_ip_extraction_startup(); ddtrace_serializer_startup(); + ddtrace_live_debugger_minit(); + ddtrace_minit_remote_config(); + return SUCCESS; } @@ -1330,9 +1399,14 @@ static PHP_MSHUTDOWN_FUNCTION(ddtrace) { if (ddtrace_disable == 1) { zai_config_mshutdown(); zai_json_shutdown_bindings(); +#if ZTS + ddtrace_thread_mshutdown(); +#endif return SUCCESS; } + ddtrace_mshutdown_remote_config(); + if (DDTRACE_G(agent_rate_by_service)) { zai_json_release_persistent_array(DDTRACE_G(agent_rate_by_service)); DDTRACE_G(agent_rate_by_service) = NULL; @@ -1363,6 +1437,9 @@ static PHP_MSHUTDOWN_FUNCTION(ddtrace) { ddtrace_user_req_shutdown(); ddtrace_sidecar_shutdown(); +#if ZTS + ddtrace_thread_mshutdown(); +#endif #if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 // See dd_register_span_data_ce for explanation @@ -1399,6 +1476,7 @@ static void dd_rinit_once(void) { static pthread_once_t dd_rinit_once_control = PTHREAD_ONCE_INIT; static void dd_initialize_request(void) { + DDTRACE_G(request_initialized) = true; DDTRACE_G(distributed_trace_id) = (ddtrace_trace_id){0}; DDTRACE_G(distributed_parent_trace_id) = 0; DDTRACE_G(additional_global_tags) = zend_new_array(0); @@ -1411,18 +1489,26 @@ static void dd_initialize_request(void) { // Things that should only run on the first RINIT after each minit. pthread_once(&dd_rinit_once_control, dd_rinit_once); - if (!DDTRACE_G(remote_config_reader)) { + if (!DDTRACE_G(agent_config_reader)) { if (get_global_DD_TRACE_SIDECAR_TRACE_SENDER()) { if (ddtrace_endpoint) { - DDTRACE_G(remote_config_reader) = ddog_agent_remote_config_reader_for_endpoint(ddtrace_endpoint); + DDTRACE_G(agent_config_reader) = ddog_agent_remote_config_reader_for_endpoint(ddtrace_endpoint); } #ifndef _WIN32 } else if (ddtrace_coms_agent_config_handle) { - ddog_agent_remote_config_reader_for_anon_shm(ddtrace_coms_agent_config_handle, &DDTRACE_G(remote_config_reader)); + ddog_agent_remote_config_reader_for_anon_shm(ddtrace_coms_agent_config_handle, &DDTRACE_G(agent_config_reader)); #endif } } + if (!DDTRACE_G(remote_config_state) && ddtrace_endpoint) { + DDTRACE_G(remote_config_state) = ddog_init_remote_config_state(ddtrace_endpoint); + } + + if (DDTRACE_G(remote_config_state)) { + ddtrace_rinit_remote_config(); + } + ddtrace_internal_handlers_rinit(); ddtrace_log_rinit(PG(error_log)); @@ -1445,10 +1531,6 @@ static void dd_initialize_request(void) { ddtrace_distributed_tracing_result distributed_result = ddtrace_read_distributed_tracing_ids(ddtrace_read_zai_header, NULL); ddtrace_apply_distributed_tracing_result(&distributed_result, NULL); - if (!DDTRACE_G(telemetry_queue_id)) { - DDTRACE_G(telemetry_queue_id) = ddog_sidecar_queueId_generate(); - } - if (get_DD_TRACE_GENERATE_ROOT_SPAN()) { ddtrace_push_root_span(); } @@ -1562,10 +1644,13 @@ void dd_force_shutdown_tracing(void) { DDTRACE_G(in_shutdown) = false; } -static void dd_finalize_telemetry(void) { - if (DDTRACE_G(telemetry_queue_id)) { +static void dd_finalize_sidecar_lifecycle(void) { + if (DDTRACE_G(request_initialized)) { ddtrace_telemetry_finalize(); - DDTRACE_G(telemetry_queue_id) = 0; + if (ddtrace_sidecar) { + ddtrace_ffi_try("Failed signaling lifecycle end", + ddog_sidecar_lifecycle_end(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id))); + } } } @@ -1584,6 +1669,10 @@ static PHP_RSHUTDOWN_FUNCTION(ddtrace) { dd_shutdown_hooks_and_observer(); } + if (DDTRACE_G(remote_config_state)) { + ddtrace_rshutdown_remote_config(); + } + if (!ddtrace_disable) { ddtrace_autoload_rshutdown(); @@ -1591,8 +1680,9 @@ static PHP_RSHUTDOWN_FUNCTION(ddtrace) { DDTRACE_G(active_stack) = NULL; } - dd_finalize_telemetry(); + dd_finalize_sidecar_lifecycle(); ddtrace_telemetry_rshutdown(); + ddtrace_sidecar_rshutdown(); if (DDTRACE_G(last_flushed_root_service_name)) { zend_string_release(DDTRACE_G(last_flushed_root_service_name)); @@ -1621,6 +1711,8 @@ zend_result ddtrace_post_deactivate(void) { // zai config may be accessed indirectly via other modules RSHUTDOWN, so delay this until the last possible time zai_config_rshutdown(); + + DDTRACE_G(request_initialized) = false; return SUCCESS; } @@ -1632,7 +1724,9 @@ void ddtrace_disable_tracing_in_current_request(void) { zend_string_release(zero); } -bool ddtrace_alter_dd_trace_disabled_config(zval *old_value, zval *new_value) { +bool ddtrace_alter_dd_trace_disabled_config(zval *old_value, zval *new_value, zend_string *new_str) { + (void)new_str; + if (Z_TYPE_P(old_value) == Z_TYPE_P(new_value)) { return true; } @@ -2114,9 +2208,13 @@ void dd_internal_handle_fork(void) { ddtrace_coms_clean_background_sender_after_fork(); } #endif - if (DDTRACE_G(remote_config_reader)) { - ddog_agent_remote_config_reader_drop(DDTRACE_G(remote_config_reader)); - DDTRACE_G(remote_config_reader) = NULL; + if (DDTRACE_G(agent_config_reader)) { + ddog_agent_remote_config_reader_drop(DDTRACE_G(agent_config_reader)); + DDTRACE_G(agent_config_reader) = NULL; + } + if (DDTRACE_G(remote_config_state)) { + ddog_shutdown_remote_config(DDTRACE_G(remote_config_state)); + DDTRACE_G(remote_config_state) = NULL; } ddtrace_seed_prng(); ddtrace_generate_runtime_id(); @@ -2144,7 +2242,7 @@ void dd_internal_handle_fork(void) { ddtrace_coms_init_and_start_writer(); if (ddtrace_coms_agent_config_handle) { - ddog_agent_remote_config_reader_for_anon_shm(ddtrace_coms_agent_config_handle, &DDTRACE_G(remote_config_reader)); + ddog_agent_remote_config_reader_for_anon_shm(ddtrace_coms_agent_config_handle, &DDTRACE_G(agent_config_reader)); } } #endif @@ -2320,11 +2418,11 @@ PHP_FUNCTION(dd_trace_internal_fn) { RETVAL_FALSE; if (ZSTR_LEN(function_val) > 0) { if (FUNCTION_NAME_MATCHES("finalize_telemetry")) { - dd_finalize_telemetry(); + dd_finalize_sidecar_lifecycle(); RETVAL_TRUE; } else if (params_count == 1 && FUNCTION_NAME_MATCHES("detect_composer_installed_json")) { ddog_CharSlice path = dd_zend_string_to_CharSlice(Z_STR_P(ZVAL_VARARG_PARAM(params, 0))); - ddtrace_detect_composer_installed_json(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), path); + ddtrace_detect_composer_installed_json(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), path); RETVAL_TRUE; } else if (FUNCTION_NAME_MATCHES("dump_sidecar")) { if (!ddtrace_sidecar) { diff --git a/ext/ddtrace.h b/ext/ddtrace.h index 94f45127b6..8f1c14b2f2 100644 --- a/ext/ddtrace.h +++ b/ext/ddtrace.h @@ -59,12 +59,12 @@ bool ddtrace_tracer_is_limited(void); // prepare the tracer state to start handling a new trace void dd_prepare_for_new_trace(void); void ddtrace_disable_tracing_in_current_request(void); -bool ddtrace_alter_dd_trace_disabled_config(zval *old_value, zval *new_value); -bool ddtrace_alter_sampling_rules_file_config(zval *old_value, zval *new_value); -bool ddtrace_alter_default_propagation_style(zval *old_value, zval *new_value); -bool ddtrace_alter_dd_service(zval *old_value, zval *new_value); -bool ddtrace_alter_dd_env(zval *old_value, zval *new_value); -bool ddtrace_alter_dd_version(zval *old_value, zval *new_value); +bool ddtrace_alter_dd_trace_disabled_config(zval *old_value, zval *new_value, zend_string *new_str); +bool ddtrace_alter_sampling_rules_file_config(zval *old_value, zval *new_value, zend_string *new_str); +bool ddtrace_alter_default_propagation_style(zval *old_value, zval *new_value, zend_string *new_str); +bool ddtrace_alter_dd_service(zval *old_value, zval *new_value, zend_string *new_str); +bool ddtrace_alter_dd_env(zval *old_value, zval *new_value, zend_string *new_str); +bool ddtrace_alter_dd_version(zval *old_value, zval *new_value, zend_string *new_str); void dd_force_shutdown_tracing(void); void dd_internal_handle_fork(void); #ifdef CXA_THREAD_ATEXIT_WRAPPER @@ -109,6 +109,12 @@ ZEND_BEGIN_MODULE_GLOBALS(ddtrace) #endif zend_bool in_shutdown; +#if PHP_VERSION_ID < 70100 + bool zai_vm_interrupt; +#endif + bool reread_remote_configuration; + bool root_span_data_submitted; + zend_long default_priority_sampling; zend_long propagated_priority_sampling; ddtrace_span_stack *active_stack; // never NULL except tracer is disabled @@ -124,15 +130,27 @@ ZEND_BEGIN_MODULE_GLOBALS(ddtrace) zend_reference *curl_multi_injecting_spans; char *cgroup_file; - ddog_QueueId telemetry_queue_id; - ddog_AgentRemoteConfigReader *remote_config_reader; + ddog_QueueId sidecar_queue_id; + ddog_AgentRemoteConfigReader *agent_config_reader; + ddog_RemoteConfigState *remote_config_state; + zend_arena *debugger_capture_arena; + ddog_Vec_DebuggerPayload exception_debugger_buffer; + HashTable active_rc_hooks; HashTable *agent_rate_by_service; zend_string *last_flushed_root_service_name; zend_string *last_flushed_root_env_name; + ddog_Vec_Tag active_global_tags; + bool request_initialized; HashTable telemetry_spans_created_per_integration; ddog_SidecarActionsBuffer *telemetry_buffer; +#if PHP_VERSION_ID >= 80000 + HashTable curl_headers; + // Multi-handle API: curl_multi_*() + HashTable curl_multi_handles; +#endif + HashTable uhook_active_hooks; HashTable uhook_closure_hooks; @@ -148,7 +166,7 @@ ZEND_END_MODULE_GLOBALS(ddtrace) # define ATTR_TLS_GLOBAL_DYNAMIC # endif extern TSRM_TLS void *ATTR_TLS_GLOBAL_DYNAMIC TSRMLS_CACHE; -# define DDTRACE_G(v) TSRMG(ddtrace_globals_id, zend_ddtrace_globals *, v) +# define DDTRACE_G(v) ZEND_TSRMG(ddtrace_globals_id, zend_ddtrace_globals *, v) #else # define DDTRACE_G(v) (ddtrace_globals.v) #endif diff --git a/ext/exception_serialize.c b/ext/exception_serialize.c new file mode 100644 index 0000000000..3492185672 --- /dev/null +++ b/ext/exception_serialize.c @@ -0,0 +1,518 @@ +#include +#include +#include "ddtrace.h" +#include "configuration.h" +#include "exception_serialize.h" +#include "compat_string.h" +#include "SAPI.h" +#include "components/log/log.h" +#include "sidecar.h" +#include "live_debugger.h" +#include "ext/hash/php_hash.h" +#include +#include + +ZEND_EXTERN_MODULE_GLOBALS(ddtrace); + +static zend_result dd_exception_to_error_msg(zend_object *exception, void *context, add_tag_fn_t add_tag, enum dd_exception exception_state) { + zend_string *msg = zai_exception_message(exception); + zend_long line = zval_get_long(zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_LINE))); + zend_string *file = ddtrace_convert_to_str(zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_FILE))); + + char *error_text, *status_line = NULL; + + if (SG(sapi_headers).http_response_code >= 500) { + if (SG(sapi_headers).http_status_line) { + UNUSED(asprintf(&status_line, " (%s)", SG(sapi_headers).http_status_line)); + } else { + UNUSED(asprintf(&status_line, " (%d)", SG(sapi_headers).http_response_code)); + } + } + + const char *exception_type; + switch (exception_state) { + case DD_EXCEPTION_CAUGHT: exception_type = "Caught"; break; + case DD_EXCEPTION_UNCAUGHT: exception_type = "Uncaught"; break; + default: exception_type = "Thrown"; break; + } + + int error_len = asprintf(&error_text, "%s %s%s%s%s in %s:" ZEND_LONG_FMT, exception_type, + ZSTR_VAL(exception->ce->name), status_line ? status_line : "", ZSTR_LEN(msg) > 0 ? ": " : "", + ZSTR_VAL(msg), ZSTR_VAL(file), line); + + free(status_line); + + ddtrace_string key = DDTRACE_STRING_LITERAL("error.message"); + ddtrace_string value = {error_text, error_len}; + zend_result result = add_tag(context, key, value); + + zend_string_release(file); + free(error_text); + return result; +} + +static zend_result dd_exception_to_error_type(zend_object *exception, void *context, add_tag_fn_t add_tag) { + ddtrace_string value, key = DDTRACE_STRING_LITERAL("error.type"); + + if (instanceof_function(exception->ce, ddtrace_ce_fatal_error)) { + zval *code = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_CODE)); + const char *error_type_string = "{unknown error}"; + + if (Z_TYPE_P(code) == IS_LONG) { + switch (Z_LVAL_P(code)) { + case E_ERROR: + error_type_string = "E_ERROR"; + break; + case E_CORE_ERROR: + error_type_string = "E_CORE_ERROR"; + break; + case E_COMPILE_ERROR: + error_type_string = "E_COMPILE_ERROR"; + break; + case E_USER_ERROR: + error_type_string = "E_USER_ERROR"; + break; + default: + LOG_UNREACHABLE("Unhandled error type in DDTrace\\FatalError; is a fatal error case missing?"); + } + + } else { + LOG_UNREACHABLE("Exception was a DDTrace\\FatalError but failed to get an exception code"); + } + + value = ddtrace_string_cstring_ctor((char *)error_type_string); + } else { + zend_string *type_name = exception->ce->name; + value.ptr = ZSTR_VAL(type_name); + value.len = ZSTR_LEN(type_name); + } + + return add_tag(context, key, value); +} + +static zend_result dd_exception_trace_to_error_stack(zend_string *trace, void *context, add_tag_fn_t add_tag) { + ddtrace_string key = DDTRACE_STRING_LITERAL("error.stack"); + ddtrace_string value = {ZSTR_VAL(trace), ZSTR_LEN(trace)}; + zend_result result = add_tag(context, key, value); + zend_string_release(trace); + return result; +} + +static void ddtrace_capture_string_value(zend_string *str, struct ddog_CaptureValue *value, const ddog_CaptureConfiguration *config) { + value->type = DDOG_CHARSLICE_C("string"); + if (!value->not_captured_reason.len) { + value->value = (ddog_CharSlice) {.ptr = ZSTR_VAL(str), .len = ZSTR_LEN(str)}; + if (value->value.len > config->max_length) { + char *integer = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), 20); + int len = sprintf(integer, "%" PRIuPTR, value->value.len); + value->size = (ddog_CharSlice) {.ptr = integer, .len = len}; + value->value.len = config->max_length; + value->truncated = true; + } + } +} + +static void ddtrace_capture_long_value(zend_long num, struct ddog_CaptureValue *value) { + value->type = DDOG_CHARSLICE_C("int"); + if (!value->not_captured_reason.len) { + char *integer = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), 20); + int len = sprintf(integer, ZEND_LONG_FMT, num); + value->value = (ddog_CharSlice) {.ptr = integer, .len = len}; + } +} + +void ddtrace_create_capture_value(zval *zv, struct ddog_CaptureValue *value, const ddog_CaptureConfiguration *config, int remaining_nesting) { + ZVAL_DEREF(zv); + switch (Z_TYPE_P(zv)) { + case IS_FALSE: + value->type = DDOG_CHARSLICE_C("bool"); + if (!value->not_captured_reason.len) { + value->value = DDOG_CHARSLICE_C("false"); + } + break; + + case IS_TRUE: + value->type = DDOG_CHARSLICE_C("bool"); + if (!value->not_captured_reason.len) { + value->value = DDOG_CHARSLICE_C("true"); + } + break; + + case IS_LONG: + ddtrace_capture_long_value(Z_LVAL_P(zv), value); + break; + + case IS_DOUBLE: { + value->type = DDOG_CHARSLICE_C("float"); + if (!value->not_captured_reason.len) { + char *num = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), 20); + php_gcvt(Z_DVAL_P(zv), (int) EG(precision), '.', 'E', num); + value->value = (ddog_CharSlice) {.ptr = num, .len = strlen(num)}; + } + break; + } + + case IS_STRING: + ddtrace_capture_string_value(Z_STR_P(zv), value, config); + break; + + case IS_ARRAY: { + value->type = DDOG_CHARSLICE_C("array"); + if (value->not_captured_reason.len) { + break; + } + if (remaining_nesting == 0) { + value->not_captured_reason = DDOG_CHARSLICE_C("depth"); + break; + } + zval *val; + if (zend_array_is_list(Z_ARR_P(zv))) { + int remaining_fields = config->max_collection_size; + ZEND_HASH_FOREACH_VAL(Z_ARR_P(zv), val) { + if (remaining_fields-- == 0) { + value->not_captured_reason = DDOG_CHARSLICE_C("collectionSize"); + break; + } + + struct ddog_CaptureValue value_capture = {0}; + ddtrace_create_capture_value(val, &value_capture, config, remaining_nesting - 1); + ddog_capture_value_add_element(value, value_capture); + } ZEND_HASH_FOREACH_END(); + } else { + zend_long idx; + zend_string *key; + int remaining_fields = config->max_collection_size; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARR_P(zv), idx, key, val) { + if (remaining_fields-- == 0) { + value->not_captured_reason = DDOG_CHARSLICE_C("collectionSize"); + break; + } + + struct ddog_CaptureValue key_capture = {0}, value_capture = {0}; + if (key) { + ddtrace_capture_string_value(key, &key_capture, config); + ddtrace_snapshot_redacted_name(&value_capture, dd_zend_string_to_CharSlice(key)); + } else { + ddtrace_capture_long_value(idx, &key_capture); + } + ddtrace_create_capture_value(val, &value_capture, config, remaining_nesting - 1); + ddog_capture_value_add_entry(value, key_capture, value_capture); + } ZEND_HASH_FOREACH_END(); + } + break; + } + + case IS_OBJECT: { + zend_class_entry *ce = Z_OBJCE_P(zv); + value->type = (ddog_CharSlice){ .ptr = ZSTR_VAL(ce->name), .len = ZSTR_LEN(ce->name) }; + if (value->not_captured_reason.len) { + break; + } + if (remaining_nesting == 0) { + value->not_captured_reason = DDOG_CHARSLICE_C("depth"); + break; + } + if (ddog_snapshot_redacted_type(dd_zend_string_to_CharSlice(ce->name))) { + value->not_captured_reason = DDOG_CHARSLICE_C("redactedType"); + break; + } + zval *val; + zend_string *key; + int remaining_fields = config->max_field_count; +#if PHP_VERSION_ID < 70400 + int is_temp = 0; +#endif + // reverse to prefer child class properties first + HashTable *ht = ce->type == ZEND_INTERNAL_CLASS ? + #if PHP_VERSION_ID < 70400 + Z_OBJDEBUG_P(zv, is_temp) + #else + zend_get_properties_for(zv, ZEND_PROP_PURPOSE_DEBUG) + #endif + : Z_OBJPROP_P(zv); + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(ht, key, val) { + if (!key) { + continue; + } + + if (remaining_fields-- == 0) { + value->not_captured_reason = DDOG_CHARSLICE_C("fieldCount"); + break; + } + + struct ddog_CaptureValue value_capture = {0}; + ddog_CharSlice fieldname; + if (ZSTR_LEN(key) < 3 || ZSTR_VAL(key)[0]) { + fieldname = (ddog_CharSlice) {.ptr = ZSTR_VAL(key), .len = ZSTR_LEN(key)}; + } else if (ZSTR_VAL(key)[1] == '*') { // skip \0*\0 + fieldname = (ddog_CharSlice) {.ptr = ZSTR_VAL(key) + 3, .len = ZSTR_LEN(key) - 3}; + } else { + char *name = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), ZSTR_LEN(key)); + int classname_len = strlen(ZSTR_VAL(key) + 1); + memcpy(name, ZSTR_VAL(key) + 1, classname_len); + name[classname_len++] = ':'; + name[classname_len++] = ':'; + memcpy(name + classname_len, ZSTR_VAL(key) + classname_len, ZSTR_LEN(key) - classname_len); + fieldname = (ddog_CharSlice) {.ptr = name, .len = ZSTR_LEN(key)}; + } + ddtrace_snapshot_redacted_name(&value_capture, fieldname); + ZVAL_DEINDIRECT(val); + ddtrace_create_capture_value(val, &value_capture, config, remaining_nesting - 1); + ddog_capture_value_add_field(value, fieldname, value_capture); + } ZEND_HASH_FOREACH_END(); + if (ce->type == ZEND_INTERNAL_CLASS) { +#if PHP_VERSION_ID < 70400 + if (is_temp) { + zend_array_release(ht); + } +#else + zend_release_properties(ht); +#endif + } + break; + } + + case IS_RESOURCE: { + const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(zv)); + ddtrace_capture_long_value(Z_RES_P(zv)->handle, value); + value->type = (ddog_CharSlice){ .ptr = type_name, .len = strlen(type_name) }; + break; + } + + default: + value->type = DDOG_CHARSLICE_C("null"); + value->is_null = true; + } +} + +#define uuid_len 36 +#define hash_len 16 + +static ddog_DebuggerCapture *dd_create_frame_and_collect_locals(char *exception_id, char *exception_hash, int frame_num, ddog_CharSlice class_slice, ddog_CharSlice func_slice, zval *locals, zend_string *service_name, const ddog_CaptureConfiguration *capture_config, uint64_t time, void *context, add_tag_fn_t add_meta) { + char *snapshot_id = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), uuid_len); + ddog_snapshot_format_new_uuid((uint8_t(*)[uuid_len])snapshot_id); + + char msg[40]; + int len = sprintf(msg, "_dd.debug.error.%d.snapshot_id", frame_num); + add_meta(context, (ddtrace_string){msg, len}, (ddtrace_string){snapshot_id, 36}); + + ddog_DebuggerCapture *capture = ddog_create_exception_snapshot(&DDTRACE_G(exception_debugger_buffer), + (ddog_CharSlice){ .ptr = ZSTR_VAL(service_name), .len = ZSTR_LEN(service_name) }, + DDOG_CHARSLICE_C("php"), + (ddog_CharSlice){ .ptr = snapshot_id, .len = uuid_len }, + (ddog_CharSlice){ .ptr = exception_id, .len = uuid_len }, + (ddog_CharSlice){ .ptr = exception_hash, .len = hash_len }, + frame_num, + class_slice, + func_slice, + time); + + if (locals && Z_TYPE_P(locals) == IS_ARRAY) { + zend_string *key; + zval *val; + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARR_P(locals), key, val) { + if (!zend_string_equals_literal(key, "GLOBALS")) { + struct ddog_CaptureValue capture_value = {0}; + ddtrace_create_capture_value(val, &capture_value, capture_config, capture_config->max_reference_depth); + ddog_snapshot_add_field(capture, DDOG_FIELD_TYPE_LOCAL, (ddog_CharSlice) {.ptr = ZSTR_VAL(key), .len = ZSTR_LEN(key)}, capture_value); + } + } ZEND_HASH_FOREACH_END(); + } + + return capture; +} + +static inline void dd_extend_hash(zend_ulong *hash, zend_string *str) { + *hash = *hash * 33 * ZSTR_LEN(str) + ZSTR_HASH(str); +} + +static zend_ulong ddtrace_compute_exception_hash(zend_object *exception) { + zend_ulong hash = 0; + + zval ex, *previous = &ex; + ZVAL_OBJ(&ex, exception); + + do { + exception = Z_OBJ_P(previous); + dd_extend_hash(&hash, exception->ce->name); + + zval *frame; + zval *trace = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_TRACE)); + if (Z_TYPE_P(trace) == IS_ARRAY) { + ZEND_HASH_FOREACH_VAL(Z_ARR_P(trace), frame) { + if (Z_TYPE_P(frame) != IS_ARRAY) { + continue; + } + + zval *class_name = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_CLASS)); + if (class_name && Z_TYPE_P(class_name) == IS_STRING) { + dd_extend_hash(&hash, Z_STR_P(class_name)); + } + zval *func_name = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_FUNCTION)); + if (func_name && Z_TYPE_P(func_name) == IS_STRING) { + dd_extend_hash(&hash, Z_STR_P(func_name)); + } + } ZEND_HASH_FOREACH_END(); + } + Z_PROTECT_RECURSION_P(previous); + previous = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_PREVIOUS)); + } while (Z_TYPE_P(previous) == IS_OBJECT && !Z_IS_RECURSIVE_P(previous) && + instanceof_function(Z_OBJCE_P(previous), zend_ce_throwable)); + + return hash; +} + +static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_string *service_name, uint64_t time, void *context, add_tag_fn_t add_meta) { + if (!ddtrace_exception_debugging_is_active()) { + return; + } + + zval *trace = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_TRACE)); + if (Z_TYPE_P(trace) != IS_ARRAY) { + return; + } + + zend_string *key_locals = zend_string_init(ZEND_STRL("locals"), 0); + zval *locals = zai_exception_read_property(exception, key_locals); + + if (!DDTRACE_G(debugger_capture_arena)) { + DDTRACE_G(debugger_capture_arena) = zend_arena_create(65536); + } + + const ddog_CaptureConfiguration capture_config = ddog_capture_defaults(); + + char *exception_hash = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), hash_len); + zend_ulong exception_long_hash = ddtrace_compute_exception_hash(exception); + php_hash_bin2hex(exception_hash, (unsigned char *)&exception_long_hash, sizeof(exception_long_hash)); + + add_meta(context, DDTRACE_STRING_LITERAL("error.debug_info_captured"), DDTRACE_STRING_LITERAL("true")); + add_meta(context, DDTRACE_STRING_LITERAL("_dd.debug.error.exception_hash"), (ddtrace_string){exception_hash, hash_len}); + + if (!ddog_exception_hash_limiter_inc(ddtrace_sidecar, (uint64_t)exception_long_hash, get_DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS())) { + LOG(TRACE, "Skipping exception replay capture due to hash %.*s already recently hit", hash_len, exception_hash); + return; + } + + char *exception_id = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), uuid_len); + ddog_snapshot_format_new_uuid((uint8_t(*)[uuid_len])exception_id); + + add_meta(context, DDTRACE_STRING_LITERAL("_dd.debug.error.exception_capture_id"), (ddtrace_string){exception_id, uuid_len}); + + zval *frame; + int frame_num = 0; + ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARR_P(trace), frame_num, frame) { + if (get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() >= 0 && get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() < frame_num) { + break; + } + + if (Z_TYPE_P(frame) != IS_ARRAY) { + continue; + } + + zend_class_entry *ce = NULL; + zend_function *func = NULL; + ddog_CharSlice func_slice = DDOG_CHARSLICE_C(""); + ddog_CharSlice class_slice = DDOG_CHARSLICE_C(""); + zval *class_name = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_CLASS)); + if (class_name && Z_TYPE_P(class_name) == IS_STRING) { + ce = zai_symbol_lookup_class_global(zai_str_from_zstr(Z_STR_P(class_name))); + class_slice = dd_zend_string_to_CharSlice(Z_STR_P(class_name)); + } + zval *func_name = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_FUNCTION)); + if (func_name && Z_TYPE_P(func_name) == IS_STRING) { + zai_str wtf = zai_str_from_zstr(Z_STR_P(func_name)); + func = zai_symbol_lookup_function(ce ? ZAI_SYMBOL_SCOPE_CLASS : ZAI_SYMBOL_SCOPE_GLOBAL, ce, &wtf); + func_slice = dd_zend_string_to_CharSlice(Z_STR_P(func_name)); + } + + ddog_DebuggerCapture *capture = dd_create_frame_and_collect_locals(exception_id, exception_hash, frame_num, class_slice, func_slice, locals, service_name, &capture_config, time, context, add_meta); + locals = zend_hash_find(Z_ARR_P(frame), key_locals); + + zend_string *key; + zval *val; + zval *args = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_ARGS)); + if (args && Z_TYPE_P(args) == IS_ARRAY) { + zend_long idx; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARR_P(args), idx, key, val) { + struct ddog_CaptureValue capture_value = {0}; + ddtrace_create_capture_value(val, &capture_value, &capture_config, capture_config.max_reference_depth); + ddog_CharSlice arg_name; + if (key) { + arg_name = (ddog_CharSlice) {.ptr = ZSTR_VAL(key), .len = ZSTR_LEN(key)}; + } else if (func && idx < func->common.num_args) { + if (ZEND_USER_CODE(func->type)) { + zend_string *name = func->op_array.arg_info[idx].name; + arg_name = (ddog_CharSlice) {.ptr = ZSTR_VAL(name), .len = ZSTR_LEN(name)}; + } else { + const char *name = func->internal_function.arg_info[idx].name; + arg_name = (ddog_CharSlice) {.ptr = name, .len = strlen(name)}; + } + } else { + char *integer = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena), 23); + int len = sprintf(integer, "arg" ZEND_LONG_FMT, idx); + arg_name = (ddog_CharSlice){ .ptr = integer, .len = len }; + } + ddog_snapshot_add_field(capture, DDOG_FIELD_TYPE_ARG, arg_name, capture_value); + } ZEND_HASH_FOREACH_END(); + } + + zval *obj = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_OBJECT)); + if (obj) { + struct ddog_CaptureValue capture_value = {0}; + ddtrace_create_capture_value(obj, &capture_value, &capture_config, capture_config.max_reference_depth); + ddog_snapshot_add_field(capture, DDOG_FIELD_TYPE_ARG, DDOG_CHARSLICE_C("this"), capture_value); + } + } ZEND_HASH_FOREACH_END(); + + if (get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() < 0 || get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() > frame_num) { + if (locals && Z_TYPE_P(locals) == IS_ARRAY) { + dd_create_frame_and_collect_locals(exception_id, exception_hash, frame_num + 1, DDOG_CHARSLICE_C(""), DDOG_CHARSLICE_C(""), locals, service_name, &capture_config, time, context, add_meta); + } + } + + zend_string_release(key_locals); +} + +// Guarantees that add_tag will only be called once per tag, will stop trying to add tags if one fails. +zend_result ddtrace_exception_to_meta(zend_object *exception, zend_string *service_name, uint64_t time, void *context, add_tag_fn_t add_meta, enum dd_exception exception_state) { + zend_object *exception_root = exception; + zend_string *full_trace = zai_get_trace_without_args_from_exception(exception); + + zval *previous = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_PREVIOUS)); + while (Z_TYPE_P(previous) == IS_OBJECT && !Z_IS_RECURSIVE_P(previous) && + instanceof_function(Z_OBJCE_P(previous), zend_ce_throwable)) { + zend_string *trace_string = zai_get_trace_without_args_from_exception(Z_OBJ_P(previous)); + + zend_string *msg = zai_exception_message(exception); + zend_long line = zval_get_long(zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_LINE))); + zend_string *file = ddtrace_convert_to_str(zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_FILE))); + + zend_string *complete_trace = + zend_strpprintf(0, "%s\n\nNext %s%s%s in %s:" ZEND_LONG_FMT "\nStack trace:\n%s", ZSTR_VAL(trace_string), + ZSTR_VAL(exception->ce->name), ZSTR_LEN(msg) ? ": " : "", ZSTR_VAL(msg), ZSTR_VAL(file), + line, ZSTR_VAL(full_trace)); + zend_string_release(trace_string); + zend_string_release(full_trace); + zend_string_release(file); + full_trace = complete_trace; + + Z_PROTECT_RECURSION_P(previous); + exception = Z_OBJ_P(previous); + previous = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_PREVIOUS)); + } + + // exception is now the innermost exception, i.e. what we need + ddtrace_collect_exception_debug_data(exception, service_name, time / 1000000, context, add_meta); + + previous = zai_exception_read_property(exception_root, ZSTR_KNOWN(ZEND_STR_PREVIOUS)); + while (Z_TYPE_P(previous) == IS_OBJECT && !Z_IS_RECURSIVE_P(previous) && + instanceof_function(Z_OBJCE_P(previous), zend_ce_throwable)) { + Z_UNPROTECT_RECURSION_P(previous); + previous = zai_exception_read_property(Z_OBJ_P(previous), ZSTR_KNOWN(ZEND_STR_PREVIOUS)); + } + + bool success = dd_exception_to_error_msg(exception, context, add_meta, exception_state) == SUCCESS && + dd_exception_to_error_type(exception, context, add_meta) == SUCCESS && + dd_exception_trace_to_error_stack(full_trace, context, add_meta) == SUCCESS; + return success ? SUCCESS : FAILURE; +} diff --git a/ext/exception_serialize.h b/ext/exception_serialize.h new file mode 100644 index 0000000000..e545eadd58 --- /dev/null +++ b/ext/exception_serialize.h @@ -0,0 +1,15 @@ +#ifndef DD_EXCEPTION_REPLAY_H +#define DD_EXCEPTION_REPLAY_H +#include "serializer.h" +#include + +enum dd_exception { + DD_EXCEPTION_THROWN, + DD_EXCEPTION_CAUGHT, + DD_EXCEPTION_UNCAUGHT, +}; + +zend_result ddtrace_exception_to_meta(zend_object *exception, zend_string *service_name, uint64_t time, void *context, add_tag_fn_t add_meta, enum dd_exception exception_state); +void ddtrace_create_capture_value(zval *zv, struct ddog_CaptureValue *value, const ddog_CaptureConfiguration *config, int remaining_nesting); + +#endif // DD_EXCEPTION_REPLAY_H diff --git a/ext/handlers_curl.c b/ext/handlers_curl.c index baa5679356..366011f905 100644 --- a/ext/handlers_curl.c +++ b/ext/handlers_curl.c @@ -31,10 +31,6 @@ extern zend_class_entry *curl_multi_ce; bool dd_ext_curl_loaded = false; zend_long dd_const_curlopt_httpheader = 0; -ZEND_TLS HashTable dd_headers; - -// Multi-handle API: curl_multi_*() -ZEND_TLS HashTable dd_multi_handles; static zif_handler dd_curl_close_handler = NULL; static zif_handler dd_curl_exec_handler = NULL; @@ -67,15 +63,15 @@ static void dd_ch_store_headers(zend_object *ch, HashTable *headers) { zend_hash_init(new_headers, zend_hash_num_elements(headers), NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(new_headers, headers, (copy_ctor_func_t)zval_add_ref); - if (!zend_weakrefs_hash_add_ptr(&dd_headers, ch, new_headers)) { - zend_hash_index_update_ptr(&dd_headers, zend_object_to_weakref_key(ch), new_headers); + if (!zend_weakrefs_hash_add_ptr(&DDTRACE_G(curl_headers), ch, new_headers)) { + zend_hash_index_update_ptr(&DDTRACE_G(curl_headers), zend_object_to_weakref_key(ch), new_headers); } } -static void dd_ch_delete_headers(zend_object *ch) { zend_weakrefs_hash_del(&dd_headers, ch); } +static void dd_ch_delete_headers(zend_object *ch) { zend_weakrefs_hash_del(&DDTRACE_G(curl_headers), ch); } static void dd_ch_duplicate_headers(zend_object *ch_orig, zend_object *ch_new) { - HashTable *headers = zend_hash_index_find_ptr(&dd_headers, zend_object_to_weakref_key(ch_orig)); + HashTable *headers = zend_hash_index_find_ptr(&DDTRACE_G(curl_headers), zend_object_to_weakref_key(ch_orig)); if (headers) { dd_ch_store_headers(ch_new, headers); } @@ -84,7 +80,7 @@ static void dd_ch_duplicate_headers(zend_object *ch_orig, zend_object *ch_new) { static void dd_inject_distributed_tracing_headers(zend_object *ch) { zval headers; zend_array *dd_header_array; - if ((dd_header_array = zend_hash_index_find_ptr(&dd_headers, zend_object_to_weakref_key(ch)))) { + if ((dd_header_array = zend_hash_index_find_ptr(&DDTRACE_G(curl_headers), zend_object_to_weakref_key(ch)))) { ZVAL_ARR(&headers, zend_array_dup(dd_header_array)); } else { array_init(&headers); @@ -112,11 +108,11 @@ static void dd_inject_distributed_tracing_headers(zend_object *ch) { * headers on the first call to curl_multi_exec(). */ static void dd_multi_add_handle(zend_object *mh, zend_object *ch) { - HashTable *handles = zend_hash_index_find_ptr(&dd_multi_handles, zend_object_to_weakref_key(mh)); + HashTable *handles = zend_hash_index_find_ptr(&DDTRACE_G(curl_multi_handles), zend_object_to_weakref_key(mh)); if (!handles) { ALLOC_HASHTABLE(handles); zend_hash_init(handles, 8, NULL, ZVAL_PTR_DTOR, 0); - zend_weakrefs_hash_add_ptr(&dd_multi_handles, mh, handles); + zend_weakrefs_hash_add_ptr(&DDTRACE_G(curl_multi_handles), mh, handles); } zval tmp; @@ -127,14 +123,14 @@ static void dd_multi_add_handle(zend_object *mh, zend_object *ch) { /* Remove a curl handle from the multi-handle map when curl_multi_remove_handle() is called. */ static void dd_multi_remove_handle(zend_object *mh, zend_object *ch) { - HashTable *handles = zend_hash_index_find_ptr(&dd_multi_handles, zend_object_to_weakref_key(mh)); + HashTable *handles = zend_hash_index_find_ptr(&DDTRACE_G(curl_multi_handles), zend_object_to_weakref_key(mh)); if (handles) { zend_hash_index_del(handles, (zend_ulong)ch->handle); } } static void dd_multi_inject_headers(zend_object *mh) { - HashTable *handles = zend_hash_index_find_ptr(&dd_multi_handles, zend_object_to_weakref_key(mh)); + HashTable *handles = zend_hash_index_find_ptr(&DDTRACE_G(curl_multi_handles), zend_object_to_weakref_key(mh)); if (handles && zend_hash_num_elements(handles) > 0) { zend_object *ch; @@ -165,7 +161,7 @@ static void dd_multi_inject_headers(zend_object *mh) { DDTRACE_G(curl_multi_injecting_spans) = NULL; } - zend_weakrefs_hash_del(&dd_multi_handles, mh); + zend_weakrefs_hash_del(&DDTRACE_G(curl_multi_handles), mh); } } @@ -173,7 +169,7 @@ static void dd_multi_inject_headers(zend_object *mh) { static HashTable *ddtrace_curl_multi_get_gc(zend_object *object, zval **table, int *n) { HashTable *ret = dd_curl_multi_get_gc(object, table, n); - HashTable *handles = zend_hash_index_find_ptr(&dd_multi_handles, zend_object_to_weakref_key(object)); + HashTable *handles = zend_hash_index_find_ptr(&DDTRACE_G(curl_multi_handles), zend_object_to_weakref_key(object)); if (handles) { // extend the buffer created by curl_multi_get_gc zend_get_gc_buffer *gc_buffer = &EG(get_gc_buffer); @@ -246,7 +242,7 @@ ZEND_FUNCTION(ddtrace_curl_multi_close) { if (dd_load_curl_integration() && zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &z_mh, curl_multi_ce) == SUCCESS) { - zend_weakrefs_hash_del(&dd_multi_handles, Z_OBJ_P(z_mh)); + zend_weakrefs_hash_del(&DDTRACE_G(curl_multi_handles), Z_OBJ_P(z_mh)); } dd_curl_multi_close_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); @@ -426,8 +422,8 @@ void ddtrace_curl_handlers_startup(void) { } void ddtrace_curl_handlers_rinit(void) { - zend_hash_init(&dd_headers, 8, NULL, (dtor_func_t)dd_ht_dtor, 0); - zend_hash_init(&dd_multi_handles, 8, NULL, (dtor_func_t)dd_ht_dtor, 0); + zend_hash_init(&DDTRACE_G(curl_headers), 8, NULL, (dtor_func_t)dd_ht_dtor, 0); + zend_hash_init(&DDTRACE_G(curl_multi_handles), 8, NULL, (dtor_func_t)dd_ht_dtor, 0); } static void dd_curl_destroy_weakref_ht(HashTable *ht) { @@ -440,6 +436,6 @@ static void dd_curl_destroy_weakref_ht(HashTable *ht) { } void ddtrace_curl_handlers_rshutdown(void) { - dd_curl_destroy_weakref_ht(&dd_headers); - dd_curl_destroy_weakref_ht(&dd_multi_handles); + dd_curl_destroy_weakref_ht(&DDTRACE_G(curl_headers)); + dd_curl_destroy_weakref_ht(&DDTRACE_G(curl_multi_handles)); } diff --git a/ext/handlers_exception.c b/ext/handlers_exception.c index 37ecf3f4a1..17a4e0ec5e 100644 --- a/ext/handlers_exception.c +++ b/ext/handlers_exception.c @@ -2,6 +2,7 @@ #include #include +#include "collect_backtrace.h" #include "configuration.h" #include "engine_hooks.h" // For 'ddtrace_resource' #include "handlers_exception.h" @@ -205,10 +206,6 @@ ZEND_ARG_INFO(0, error_filename) ZEND_ARG_INFO(0, error_lineno) ZEND_END_ARG_INFO() -#if PHP_VERSION_ID < 70100 -#define ZEND_STR_PREVIOUS "previous" -#endif - #if PHP_VERSION_ID < 70200 && !defined(__clang__) // zpp API is not safe by itself, but our code is safe here. #pragma GCC diagnostic ignored "-Wclobbered" @@ -326,11 +323,11 @@ static PHP_METHOD(DDTrace_ExceptionOrErrorHandler, execute) { // If this ever turns out to be problematic, we have to store it somewhere in DDTRACE_G() // and delay attaching until serialization. if (root_span && Z_TYPE_P((zval *)&old_exception) > IS_FALSE) { - zval *previous = ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_PREVIOUS); + zval *previous = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_PREVIOUS)); while (Z_TYPE_P(previous) == IS_OBJECT && !Z_IS_RECURSIVE_P(previous) && instanceof_function(Z_OBJCE_P(previous), zend_ce_throwable)) { Z_PROTECT_RECURSION_P(previous); - previous = ZAI_EXCEPTION_PROPERTY(Z_OBJ_P(previous), ZEND_STR_PREVIOUS); + previous = zai_exception_read_property(Z_OBJ_P(previous), ZSTR_KNOWN(ZEND_STR_PREVIOUS)); } if (Z_TYPE_P(previous) > IS_FALSE) { @@ -341,10 +338,10 @@ static PHP_METHOD(DDTrace_ExceptionOrErrorHandler, execute) { ZVAL_COPY_VALUE(previous, (zval *)&old_exception); } - previous = ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_PREVIOUS); + previous = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_PREVIOUS)); while (Z_TYPE_P(previous) == IS_OBJECT && Z_IS_RECURSIVE_P(previous)) { Z_UNPROTECT_RECURSION_P(previous); - previous = ZAI_EXCEPTION_PROPERTY(Z_OBJ_P(previous), ZEND_STR_PREVIOUS); + previous = zai_exception_read_property(Z_OBJ_P(previous), ZSTR_KNOWN(ZEND_STR_PREVIOUS)); } } } @@ -388,6 +385,87 @@ void dd_exception_handler_freed(zend_object *object) { } #endif +static zend_object *ddtrace_exception_new(zend_class_entry *class_type, zend_object *(*prev)(zend_class_entry *class_type)) { + zend_execute_data *ex = EG(current_execute_data); + EG(current_execute_data) = NULL; + zend_object *object = prev(class_type); + EG(current_execute_data) = ex; + + zend_class_entry *base_ce = zai_get_exception_base(object); + + bool ignore_args = zend_string_equals_literal(class_type->name, "SodiumException"); +#if PHP_VERSION_ID >= 70400 + ignore_args = ignore_args || EG(exception_ignore_args); +#endif + + bool exception_replay = get_DD_EXCEPTION_REPLAY_ENABLED(); + + zval trace; + ddtrace_fetch_debug_backtrace(&trace, 0, (ignore_args ? DEBUG_BACKTRACE_IGNORE_ARGS : 0) | (exception_replay ? DDTRACE_DEBUG_BACKTRACE_CAPTURE_LOCALS | DEBUG_BACKTRACE_PROVIDE_OBJECT : 0), 0); + Z_SET_REFCOUNT(trace, 0); + + zval filezv, linezv; + zend_string *filename; + if ((class_type != zend_ce_parse_error +#if PHP_VERSION_ID >= 70300 + && class_type != zend_ce_compile_error +#endif + ) || !(filename = zend_get_compiled_filename())) { + ZVAL_STRING(&filezv, zend_get_executed_filename()); + ZVAL_LONG(&linezv, zend_get_executed_lineno()); + } else { + ZVAL_STR_COPY(&filezv, filename); + ZVAL_LONG(&linezv, zend_get_compiled_lineno()); + } + + EG(current_execute_data) = NULL; // zend_std_write_property will have side effects when EX(opline) points to ZEND_ASSIGN_OBJ... + zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), &trace); + zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &filezv); + zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &linezv); + zval_ptr_dtor(&filezv); + EG(current_execute_data) = ex; + + if (ex && ex->func && ZEND_USER_CODE(ex->func->type) && exception_replay) { + zval locals; + ddtrace_call_get_locals(ex, &locals, !ignore_args); + zend_string *key_locals = zend_string_init(ZEND_STRL("locals"), 0); + Z_SET_REFCOUNT(locals, 0); + zend_update_property_ex(base_ce, object, key_locals, &locals); + zend_string_release(key_locals); + } + + return object; +} + +// fast path +zend_object *(*prev_exception_default_create_object)(zend_class_entry *class_type); +static zend_object *ddtrace_default_exception_new(zend_class_entry *class_type) { + return ddtrace_exception_new(class_type, prev_exception_default_create_object); +} + +// support custom exception create handlers +HashTable ddtrace_exception_custom_create_object; +static zend_object *ddtrace_custom_exception_new(zend_class_entry *class_type) { + zend_class_entry *ce = class_type; + zend_object *(*prev)(zend_class_entry *class_type); + while (!(prev = zend_hash_index_find_ptr(&ddtrace_exception_custom_create_object, (zend_ulong)(uintptr_t)ce))) { + ce = ce->parent; + } + return ddtrace_exception_new(class_type, prev); +} + +static zend_property_info *dd_add_exception_locals_property(zend_class_entry *ce) { + zend_string *key = zend_string_init(ZEND_STRL("locals"), 1); +#if PHP_VERSION_ID >= 80000 + zend_property_info *prop = zend_declare_typed_property(ce, key, &EG(uninitialized_zval), ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); +#else + zend_declare_property_ex(ce, key, &EG(uninitialized_zval), ZEND_ACC_PRIVATE, NULL); + zend_property_info *prop = zend_hash_find_ptr(&ce->properties_info, key); +#endif + zend_string_release(key); + return prop; +} + void ddtrace_exception_handlers_startup(void) { ddtrace_exception_or_error_handler = (zend_internal_function){ .type = ZEND_INTERNAL_FUNCTION, @@ -422,9 +500,79 @@ void ddtrace_exception_handlers_startup(void) { for (size_t i = 0; i < handlers_len; ++i) { datadog_php_install_handler(handlers[i]); } + + zend_property_info *exception_prop = dd_add_exception_locals_property(zend_ce_exception); + zend_property_info *error_prop = dd_add_exception_locals_property(zend_ce_error); + + prev_exception_default_create_object = zend_ce_exception->create_object; + zend_hash_init(&ddtrace_exception_custom_create_object, 8, NULL, NULL, 1); + zend_class_entry *ce; + zend_string *locals_key = zend_string_init_interned(ZEND_STRL("locals"), 1); + ZEND_HASH_FOREACH_PTR(CG(class_table), ce) { + if ((ce->ce_flags & ZEND_ACC_INTERFACE) == 0 && instanceof_function_slow(ce, zend_ce_throwable)) { + if (ce->create_object) { + if (ce->create_object == prev_exception_default_create_object) { + ce->create_object = ddtrace_default_exception_new; + } else { + zend_hash_index_add_ptr(&ddtrace_exception_custom_create_object, (zend_long)(uintptr_t)ce, ce->create_object); + ce->create_object = ddtrace_custom_exception_new; + } + + // add locals property to all existing throwables + zend_class_entry *base_ce = NULL; + zend_property_info *parent_info; + if (ce != zend_ce_exception && instanceof_function_slow(ce, zend_ce_exception)) { + base_ce = zend_ce_exception; + parent_info = exception_prop; + } else if (ce != zend_ce_error && instanceof_function_slow(ce, zend_ce_error)) { + base_ce = zend_ce_error; + parent_info = error_prop; + } + if (base_ce) { + zval *child = zend_hash_find_known_hash(&ce->properties_info, locals_key); + if (child) { + ((zend_property_info *)Z_PTR_P(child))->flags |= ZEND_ACC_CHANGED; + } else { +#if PHP_VERSION_ID < 80100 + if (ce->type == ZEND_INTERNAL_CLASS) { + zend_property_info *property_info = parent_info; + parent_info = pemalloc(sizeof(zend_property_info), 1); + memcpy(parent_info, property_info, sizeof(zend_property_info)); + zend_string_addref(parent_info->name); + } +#endif + zend_hash_add_new_ptr(&ce->properties_info, locals_key, parent_info); + } + + zend_property_info *property_info; + ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, property_info) { + if (property_info->offset >= parent_info->offset && property_info->ce != base_ce && (property_info->flags & ZEND_ACC_STATIC) == 0) { + property_info->offset += sizeof(zval); + } + } ZEND_HASH_FOREACH_END(); + + int insert_at = zend_hash_num_elements(&base_ce->properties_info) - 1; + ce->default_properties_count++; + + ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval) * ce->default_properties_count, 1); + memmove(ce->default_properties_table + insert_at + 1, ce->default_properties_table + insert_at, sizeof(zval) * (ce->default_properties_count - insert_at - 1)); + ZVAL_COPY_VALUE_PROP(ce->default_properties_table + insert_at, base_ce->default_properties_table + insert_at); + +#if PHP_VERSION_ID >= 70400 + ce->properties_info_table = perealloc(ce->properties_info_table, sizeof(zend_property_info *) * ce->default_properties_count, 1); + memmove(ce->properties_info_table + insert_at + 1, ce->properties_info_table + insert_at, sizeof(zend_property_info *) * (ce->default_properties_count - insert_at - 1)); + ce->properties_info_table[insert_at] = parent_info; +#endif + } + } + } + } ZEND_HASH_FOREACH_END(); } -void ddtrace_exception_handlers_shutdown(void) { ddtrace_free_unregistered_class(&dd_exception_or_error_handler_ce); } +void ddtrace_exception_handlers_shutdown(void) { + ddtrace_free_unregistered_class(&dd_exception_or_error_handler_ce); + zend_hash_destroy(&ddtrace_exception_custom_create_object); +} void ddtrace_exception_handlers_rinit(void) { if (Z_TYPE(EG(user_exception_handler)) != IS_OBJECT || diff --git a/ext/hook/uhook.c b/ext/hook/uhook.c index 03a46b9813..039e60f02c 100644 --- a/ext/hook/uhook.c +++ b/ext/hook/uhook.c @@ -99,7 +99,7 @@ HashTable *dd_uhook_collect_args(zend_execute_data *execute_data) { ht->nTableSize = num_args; #endif - zend_hash_real_init(ht, 1); + zend_hash_real_init_packed(ht); ZEND_HASH_FILL_PACKED(ht) { if (EX(func)->type == ZEND_USER_FUNCTION) { uint32_t first_extra_arg = MIN(num_args, func->op_array.num_args); @@ -196,7 +196,7 @@ static bool dd_uhook_call_hook(zend_execute_data *execute_data, zend_object *clo return Z_TYPE(rv) != IS_FALSE; } -static bool dd_uhook_match_filepath(zend_string *file, zend_string *source) { +bool ddtrace_uhook_match_filepath(zend_string *file, zend_string *source) { if (ZSTR_LEN(source) == 0) { return true; // empty path is wildcard } @@ -255,7 +255,7 @@ static bool dd_uhook_begin(zend_ulong invocation, zend_execute_data *execute_dat dd_uhook_def *def = auxiliary; dd_uhook_dynamic *dyn = dynamic; - if (def->file && (!execute_data->func->op_array.filename || !dd_uhook_match_filepath(execute_data->func->op_array.filename, def->file))) { + if (def->file && (!execute_data->func->op_array.filename || !ddtrace_uhook_match_filepath(execute_data->func->op_array.filename, def->file))) { dyn->hook_data = NULL; return true; } diff --git a/ext/hook/uhook.h b/ext/hook/uhook.h index 64366c6ca6..6510bebbfc 100644 --- a/ext/hook/uhook.h +++ b/ext/hook/uhook.h @@ -7,6 +7,7 @@ HashTable *dd_uhook_collect_args(zend_execute_data *execute_data); void dd_uhook_report_sandbox_error(zend_execute_data *execute_data, zend_object *closure); void dd_uhook_log_invocation(void (*log)(const char *, ...), zend_execute_data *execute_data, const char *type, zend_object *closure); +bool ddtrace_uhook_match_filepath(zend_string *file, zend_string *source); void zai_uhook_rinit(); void zai_uhook_rshutdown(); diff --git a/ext/integrations/integrations.h b/ext/integrations/integrations.h index 75e2a4ec8b..cc03df4294 100644 --- a/ext/integrations/integrations.h +++ b/ext/integrations/integrations.h @@ -11,44 +11,44 @@ #define DDTRACE_LONGEST_INTEGRATION_NAME_LEN 16 // "symfonymessenger" FTW! -#define DD_INTEGRATIONS \ - INTEGRATION(AMQP, "amqp") \ - INTEGRATION(CAKEPHP, "cakephp") \ - INTEGRATION(CODEIGNITER, "codeigniter") \ - INTEGRATION(EXEC, "exec") \ - INTEGRATION(CURL, "curl") \ - INTEGRATION(DRUPAL, "drupal") \ - INTEGRATION(ELASTICSEARCH, "elasticsearch") \ - INTEGRATION(ELOQUENT, "eloquent") \ - INTEGRATION(FRANKENPHP, "frankenphp") \ - INTEGRATION(GUZZLE, "guzzle") \ - INTEGRATION(LAMINAS, "laminas") \ - INTEGRATION(LARAVEL, "laravel") \ - INTEGRATION(LARAVELQUEUE, "laravelqueue") \ - INTEGRATION(LOGS, "logs", "false", DD_LOGS_INJECTION) \ - INTEGRATION(LUMEN, "lumen") \ - INTEGRATION(MAGENTO, "magento") \ - INTEGRATION(MEMCACHE, "memcache") \ - INTEGRATION(MEMCACHED, "memcached") \ - INTEGRATION(MONGO, "mongo") \ - INTEGRATION(MONGODB, "mongodb") \ - INTEGRATION(MYSQLI, "mysqli") \ - INTEGRATION(NETTE, "nette") \ - INTEGRATION(OPENAI, "openai") \ - INTEGRATION(PCNTL, "pcntl") \ - INTEGRATION(PDO, "pdo") \ - INTEGRATION(PHPREDIS, "phpredis") \ - INTEGRATION(PREDIS, "predis") \ - INTEGRATION(PSR18, "psr18") \ - INTEGRATION(ROADRUNNER, "roadrunner") \ - INTEGRATION(SQLSRV, "sqlsrv") \ - INTEGRATION(SLIM, "slim") \ - INTEGRATION(SWOOLE, "swoole") \ - INTEGRATION(SYMFONY, "symfony") \ - INTEGRATION(SYMFONYMESSENGER, "symfonymessenger") \ - INTEGRATION(WEB, "web") \ - INTEGRATION(WORDPRESS, "wordpress") \ - INTEGRATION(YII, "yii") \ +#define DD_INTEGRATIONS \ + INTEGRATION(AMQP, "amqp") \ + INTEGRATION(CAKEPHP, "cakephp") \ + INTEGRATION(CODEIGNITER, "codeigniter") \ + INTEGRATION(EXEC, "exec") \ + INTEGRATION(CURL, "curl") \ + INTEGRATION(DRUPAL, "drupal") \ + INTEGRATION(ELASTICSEARCH, "elasticsearch") \ + INTEGRATION(ELOQUENT, "eloquent") \ + INTEGRATION(FRANKENPHP, "frankenphp") \ + INTEGRATION(GUZZLE, "guzzle") \ + INTEGRATION(LAMINAS, "laminas") \ + INTEGRATION(LARAVEL, "laravel") \ + INTEGRATION(LARAVELQUEUE, "laravelqueue") \ + INTEGRATION(LOGS, "logs", "false", CALIASES("DD_LOGS_INJECTION"), .ini_change = ddtrace_alter_DD_TRACE_LOGS_ENABLED) \ + INTEGRATION(LUMEN, "lumen") \ + INTEGRATION(MAGENTO, "magento") \ + INTEGRATION(MEMCACHE, "memcache") \ + INTEGRATION(MEMCACHED, "memcached") \ + INTEGRATION(MONGO, "mongo") \ + INTEGRATION(MONGODB, "mongodb") \ + INTEGRATION(MYSQLI, "mysqli") \ + INTEGRATION(NETTE, "nette") \ + INTEGRATION(OPENAI, "openai") \ + INTEGRATION(PCNTL, "pcntl") \ + INTEGRATION(PDO, "pdo") \ + INTEGRATION(PHPREDIS, "phpredis") \ + INTEGRATION(PREDIS, "predis") \ + INTEGRATION(PSR18, "psr18") \ + INTEGRATION(ROADRUNNER, "roadrunner") \ + INTEGRATION(SQLSRV, "sqlsrv") \ + INTEGRATION(SLIM, "slim") \ + INTEGRATION(SWOOLE, "swoole") \ + INTEGRATION(SYMFONY, "symfony") \ + INTEGRATION(SYMFONYMESSENGER, "symfonymessenger") \ + INTEGRATION(WEB, "web") \ + INTEGRATION(WORDPRESS, "wordpress") \ + INTEGRATION(YII, "yii") \ INTEGRATION(ZENDFRAMEWORK, "zendframework") #define INTEGRATION(id, ...) DDTRACE_INTEGRATION_##id, diff --git a/ext/live_debugger.c b/ext/live_debugger.c new file mode 100644 index 0000000000..211be6513c --- /dev/null +++ b/ext/live_debugger.c @@ -0,0 +1,1305 @@ +#include "live_debugger.h" +#include "ddtrace.h" +#include "exception_serialize.h" +#include "zai_string/string.h" +#include "span.h" +#include "hook/uhook.h" +#include "sidecar.h" +#include "hook/hook.h" +#include "serializer.h" +#include "configuration.h" +#include "compat_string.h" +#include "zend_interfaces.h" +#include "zend_hrtime.h" +#include "components-rs/common.h" + +ZEND_EXTERN_MODULE_GLOBALS(ddtrace); + +struct eval_ctx { + zend_execute_data *frame; + zend_arena *arena; + zval *retval; + const ddog_CaptureConfiguration *config; +}; + +static void clean_ctx(struct eval_ctx *ctx) { + if (ctx->arena) { + zend_arena *arena = ctx->arena; + do { + zend_arena *prev = arena->prev; + for (zval *cur = (zval *)((char *)arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))); cur < (zval *)arena->ptr; ++cur) { + zval_ptr_dtor(cur); + } + arena = prev; + } while (arena); + zend_arena_destroy(ctx->arena); + } +} + +static ddog_ConditionEvaluationResult dd_eval_condition(const ddog_ProbeCondition *condition, zval *retval) { + ddog_CaptureConfiguration config = ddog_capture_defaults(); + struct eval_ctx ctx = { + .frame = EG(current_execute_data), + .arena = NULL, + .retval = retval, + .config = &config, + }; + ddog_ConditionEvaluationResult result = ddog_evaluate_condition(condition, &ctx); + clean_ctx(&ctx); + return result; +} + +static ddog_ValueEvaluationResult dd_eval_value(const ddog_ProbeValue *value, zval *retval) { + ddog_CaptureConfiguration config = ddog_capture_defaults(); + struct eval_ctx ctx = { + .frame = EG(current_execute_data), + .arena = NULL, + .retval = retval, + .config = &config, + }; + ddog_ValueEvaluationResult result = ddog_evaluate_value(value, &ctx); + clean_ctx(&ctx); + return result; +} + +static zend_string *dd_eval_string(const ddog_DslString *string, const ddog_CaptureConfiguration *config, zval *retval, ddog_Vec_SnapshotEvaluationError **error) { + struct eval_ctx ctx = { + .frame = EG(current_execute_data), + .arena = NULL, + .retval = retval, + .config = config, + }; + ddog_VoidCollection bytes = ddog_evaluate_unmanaged_string(string, &ctx, error); + zend_string *str = zend_string_init(bytes.elements, bytes.count, 0); + bytes.free(bytes); + clean_ctx(&ctx); + return str; +} + +static zval *dd_persist_eval_arena(struct eval_ctx *eval_ctx, zval *zv) { + if (!eval_ctx->arena) { + eval_ctx->arena = zend_arena_create(4096); + } + zval *zvp = zend_arena_alloc(&eval_ctx->arena, sizeof(zval)); + ZVAL_COPY_VALUE(zvp, zv); + return zvp; +} + +static ddog_CharSlice dd_persist_str_eval_arena(struct eval_ctx *eval_ctx, zend_string *str) { + zval zv; + ZVAL_STR(&zv, str); + if (Z_REFCOUNTED(zv)) { + if (Z_REFCOUNT(zv) == 1) { + dd_persist_eval_arena(eval_ctx, &zv); + } else { + Z_DELREF(zv); + } + } + return dd_zend_string_to_CharSlice(Z_STR(zv)); +} + +typedef struct { + ddtrace_span_data *span; +} dd_span_probe_dynamic; + +typedef struct { + ddog_Probe probe; + zend_string *function; + zend_string *scope; + zend_string *file; + zend_string *probe_id; +} dd_probe_def; + +static bool dd_probe_file_mismatch(dd_probe_def *def, zend_execute_data *execute_data) { + return def->file && (!execute_data->func->op_array.filename || !ddtrace_uhook_match_filepath(execute_data->func->op_array.filename, def->file)); +} + +static void dd_probe_dtor(void *data) { + dd_probe_def *def = data; + if (def->probe.probe.tag == DDOG_PROBE_TYPE_SPAN_DECORATION) { + drop_span_decoration_probe(def->probe.probe.span_decoration); + } + if (def->file) { + zend_string_release(def->file); + } + if (def->scope) { + zend_string_release(def->scope); + } + if (def->function) { + zend_string_release(def->function); + } + zend_string_release(def->probe_id); + efree(def); +} + +static void dd_probe_resolved(void *data, bool found) { + dd_probe_def *def = data; + if (found) { + def->probe.status = DDOG_PROBE_STATUS_INSTALLED; + } else { + def->probe.status = DDOG_PROBE_STATUS_ERROR; + def->probe.status_msg = DDOG_CHARSLICE_C("Method does not exist on the given class"); + def->probe.status_exception = DDOG_CHARSLICE_C("METHOD_NOT_FOUND"); + } + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); +} + +static int64_t dd_init_live_debugger_probe(const ddog_Probe *probe, dd_probe_def *def, zai_hook_begin begin, zai_hook_end end, void (*def_dtor)(void *), size_t dynamic) { + def->probe = *probe; + def->probe_id = dd_CharSlice_to_zend_string(probe->id); + def->file = NULL; + def->function = NULL; + def->scope = NULL; + + const ddog_ProbeTarget *target = &probe->target; + if (target->type_name.len) { + if (!ddog_type_can_be_instrumented(DDTRACE_G(remote_config_state), target->type_name)) { + def->probe.status = DDOG_PROBE_STATUS_BLOCKED; + goto error; + } + + def->scope = dd_CharSlice_to_zend_string(target->type_name); + } + if (target->method_name.len) { + def->function = dd_CharSlice_to_zend_string(target->method_name); + } else if (target->source_file.len) { + def->file = dd_CharSlice_to_zend_string(target->source_file); + } else { + def->probe.status = DDOG_PROBE_STATUS_ERROR; + def->probe.status_msg = DDOG_CHARSLICE_C("Target is not supported"); + def->probe.status_exception = DDOG_CHARSLICE_C("UNSUPPORTED_TARGET"); + goto error; + } + + zai_str scope = ZAI_STR_EMPTY, function = ZAI_STR_EMPTY; + if (def->scope) { + scope = (zai_str) ZAI_STR_FROM_ZSTR(def->scope); + } + if (def->function) { + function = (zai_str) ZAI_STR_FROM_ZSTR(def->function); + } + zend_long id = zai_hook_install( + scope, + function, + begin, + end, + ZAI_HOOK_AUX_RESOLVED(def, def_dtor, dd_probe_resolved), + dynamic); + + if (id < 0) { + def->probe.status = DDOG_PROBE_STATUS_ERROR; + def->probe.status_msg = DDOG_CHARSLICE_C("Method does not exist on the given class"); + def->probe.status_exception = DDOG_CHARSLICE_C("METHOD_NOT_FOUND"); +error: + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); + def_dtor(def); + return -1; + } + + if (def->probe.status != DDOG_PROBE_STATUS_INSTALLED) { + def->probe.status = DDOG_PROBE_STATUS_RECEIVED; + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); + } + + zend_hash_index_add_new_ptr(&DDTRACE_G(active_rc_hooks), id, def); + return id; +} + +static void dd_probe_mark_active(dd_probe_def *def) { + if (def->probe.status != DDOG_PROBE_STATUS_EMITTING) { + def->probe.status = DDOG_PROBE_STATUS_EMITTING; + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); + } +} + +static bool dd_span_probe_begin(zend_ulong invocation, zend_execute_data *execute_data, void *auxiliary, void *dynamic) { + dd_probe_def *def = auxiliary; + dd_span_probe_dynamic *dyn = dynamic; + + if (dd_probe_file_mismatch(def, execute_data)) { + dyn->span = NULL; + return true; + } + + dd_probe_mark_active(def); + + dyn->span = ddtrace_alloc_execute_data_span(invocation, execute_data); + + zval garbage; + ZVAL_COPY_VALUE(&garbage, &dyn->span->property_resource); + ZVAL_COPY_VALUE(&dyn->span->property_resource, &dyn->span->property_name); + ZVAL_STRING(&dyn->span->property_name, "dd.dynamic.span"); + zval_ptr_dtor(&garbage); + + zval probe_id; + ZVAL_STR_COPY(&probe_id, def->probe_id); + zend_hash_str_update(ddtrace_property_array(&dyn->span->property_meta), ZEND_STRL("debugger.probeid"), &probe_id); + + return true; +} + +static void dd_span_probe_end(zend_ulong invocation, zend_execute_data *execute_data, zval *retval, void *auxiliary, void *dynamic) { + dd_span_probe_dynamic *dyn = dynamic; + + UNUSED(execute_data, retval, auxiliary); + + if (dyn->span) { + ddtrace_clear_execute_data_span(invocation, true); + } +} + +static int64_t dd_set_span_probe(const ddog_Probe *probe) { + dd_probe_def *def = emalloc(sizeof(*def)); + return dd_init_live_debugger_probe(probe, def, dd_span_probe_begin, dd_span_probe_end, dd_probe_dtor, sizeof(dd_span_probe_dynamic)); +} + +static void dd_submit_probe_eval_error_snapshot(const ddog_Probe *probe, ddog_Vec_SnapshotEvaluationError *error) { + zend_string *service_name = ddtrace_active_service_name(); + ddog_DebuggerPayload *snapshot = ddog_evaluation_error_snapshot(probe, + (ddog_CharSlice){ .ptr = ZSTR_VAL(service_name), .len = ZSTR_LEN(service_name) }, + DDOG_CHARSLICE_C("php"), + error, + ddtrace_nanoseconds_realtime() / 1000000); + ddtrace_sidecar_send_debugger_datum(snapshot); + zend_string_release(service_name); +} + +static void dd_span_decoration_end(zend_ulong invocation, zend_execute_data *execute_data, zval *retval, void *auxiliary, void *dynamic) { + dd_probe_def *def = auxiliary; + ddtrace_span_data *span = ddtrace_active_span(); + if (!span) { + return; + } + UNUSED(invocation, dynamic); + if (dd_probe_file_mismatch(def, execute_data)) { + return; + } + + dd_probe_mark_active(def); + + if (def->probe.probe.span_decoration.target == DDOG_SPAN_PROBE_TARGET_ROOT) { + span = &span->stack->root_span->span; + } + zend_array *meta = ddtrace_property_array(&span->property_meta); + + bool condition_result = true; + const ddog_ProbeCondition *const *condition = def->probe.probe.span_decoration.conditions; + for (uintptr_t i = 0; i < def->probe.probe.span_decoration.span_tags_num; ++i) { + const ddog_SpanProbeTag *spanTag = def->probe.probe.span_decoration.span_tags + i; + if (spanTag->next_condition) { + ddog_ConditionEvaluationResult result = dd_eval_condition(*(condition++), retval); + if (result.tag == DDOG_CONDITION_EVALUATION_RESULT_ERROR) { + dd_submit_probe_eval_error_snapshot(&def->probe, result.error); + condition_result = false; + } else { + condition_result = result.tag == DDOG_CONDITION_EVALUATION_RESULT_SUCCESS; + } + } + if (condition_result) { + zval zv; + ddog_Vec_SnapshotEvaluationError *error; + ddog_CaptureConfiguration config_defaults = ddog_capture_defaults(); + ZVAL_STR(&zv, dd_eval_string(spanTag->tag.value, &config_defaults, retval, &error)); + zend_hash_str_update(meta, spanTag->tag.name.ptr, spanTag->tag.name.len, &zv); + + zend_string *tag = zend_strpprintf(0, "_dd.di.%.*s.probe_id", (int)spanTag->tag.name.len, spanTag->tag.name.ptr); + ZVAL_STR_COPY(&zv, def->probe_id); + zend_hash_update(meta, tag, &zv); + zend_string_release(tag); + + if (error) { + ddog_CharSlice msg = ddog_evaluation_error_first_msg(error); + + tag = zend_strpprintf(0, "_dd.di.%.*s.evaluation_error", (int)spanTag->tag.name.len, spanTag->tag.name.ptr); + ZVAL_STR(&zv, dd_CharSlice_to_zend_string(msg)); + zend_hash_update(meta, tag, &zv); + zend_string_release(tag); + + ddog_evaluation_error_drop(error); + } + } + } +} + +static bool dd_span_decoration_begin(zend_ulong invocation, zend_execute_data *execute_data, void *auxiliary, void *dynamic) { + zval retval; + ZVAL_NULL(&retval); + dd_span_decoration_end(invocation, execute_data, &retval, auxiliary, dynamic); + return true; +} + +static int64_t dd_set_span_decoration(const ddog_Probe *probe) { + dd_probe_def *def = emalloc(sizeof(*def)); + + zai_hook_begin begin = NULL; + zai_hook_end end = NULL; + if (probe->target.in_body_location == DDOG_IN_BODY_LOCATION_START) { + begin = dd_span_decoration_begin; + } else { + end = dd_span_decoration_end; + } + return dd_init_live_debugger_probe(probe, def, begin, end, dd_probe_dtor, 0); +} + +typedef struct { + dd_probe_def parent; + const ddog_MaybeShmLimiter *limiter; +} dd_log_probe_def; + +typedef struct { + bool rejected; + ddog_DebuggerPayload *payload; + zend_string *service; + zend_arena *capture_arena; +} dd_log_probe_dyn; + +static bool dd_log_probe_eval_condition(dd_log_probe_def *def, zend_execute_data *execute_data, zval *retval) { + if (dd_probe_file_mismatch(&def->parent, execute_data)) { + return false; + } + + ddog_ConditionEvaluationResult condition = dd_eval_condition(def->parent.probe.probe.log.when, retval); + switch (condition.tag) { + case DDOG_CONDITION_EVALUATION_RESULT_SUCCESS: + return ddog_shm_limiter_inc(def->limiter, def->parent.probe.probe.log.sampling_snapshots_per_second) && ddog_global_log_probe_limiter_inc(DDTRACE_G(remote_config_state)); + case DDOG_CONDITION_EVALUATION_RESULT_ERROR: + dd_submit_probe_eval_error_snapshot(&def->parent.probe, condition.error); + break; + case DDOG_CONDITION_EVALUATION_RESULT_FAILURE: + break; + } + return false; +} + +static void dd_log_probe_ensure_payload(dd_log_probe_dyn *dyn, dd_log_probe_def *def, ddog_CharSlice *msg) { + if (dyn->payload) { + ddog_update_payload_message(dyn->payload, *msg); + } else { + dyn->service = ddtrace_active_service_name(); + dyn->payload = ddog_create_log_probe_snapshot(&def->parent.probe, msg, dd_zend_string_to_CharSlice(dyn->service), DDOG_CHARSLICE_C("php"), ddtrace_nanoseconds_realtime() / 1000000); + } +} + +static void dd_log_probe_capture_snapshot(ddog_DebuggerCapture *capture, dd_log_probe_def *def, zend_execute_data *execute_data) { + const ddog_CaptureConfiguration *capture_config = def->parent.probe.probe.log.capture; + if (ZEND_USER_CODE(EX(func)->type)) { + zend_array *symbol_table = zend_rebuild_symbol_table(); + zend_string *symbol; + zval *variable; + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(symbol_table, symbol, variable) { + if (symbol) { + struct ddog_CaptureValue capture_value = {0}; + ddog_CharSlice name_slice = dd_zend_string_to_CharSlice(symbol); + ddtrace_snapshot_redacted_name(&capture_value, name_slice); + ddtrace_create_capture_value(variable, &capture_value, capture_config, capture_config->max_reference_depth); + ddog_FieldType type = EX_VAR_NUM(0) <= variable && variable < EX_VAR_NUM(EX(func)->op_array.num_args) ? DDOG_FIELD_TYPE_ARG : DDOG_FIELD_TYPE_LOCAL; + ddog_snapshot_add_field(capture, type, name_slice, capture_value); + } + } ZEND_HASH_FOREACH_END(); + } else if (EX(func)->internal_function.arg_info) { + uint32_t num_args = EX(func)->internal_function.num_args; + for (uintptr_t i = 0; i < num_args; ++i) { + const char *name = EX(func)->internal_function.arg_info[i].name; + ddog_CharSlice name_slice = { .len = strlen(name), .ptr = name }; + struct ddog_CaptureValue capture_value = {0}; + ddtrace_snapshot_redacted_name(&capture_value, name_slice); + ddtrace_create_capture_value(EX_VAR_NUM(i), &capture_value, capture_config, capture_config->max_reference_depth); + ddog_snapshot_add_field(capture, DDOG_FIELD_TYPE_ARG, name_slice, capture_value); + } + } + if (hasThis()) { + struct ddog_CaptureValue capture_value = {0}; + ddtrace_create_capture_value(&EX(This), &capture_value, capture_config, capture_config->max_reference_depth); + ddog_snapshot_add_field(capture, DDOG_FIELD_TYPE_ARG, DDOG_CHARSLICE_C("this"), capture_value); + } +} + +static void dd_log_probe_end(zend_ulong invocation, zend_execute_data *execute_data, zval *retval, void *auxiliary, void *dynamic) { + dd_log_probe_dyn *dyn = dynamic; + dd_log_probe_def *def = auxiliary; + UNUSED(invocation); + + if (dyn->rejected) { + return; + } + + if (def->parent.probe.evaluate_at == DDOG_EVALUATE_AT_EXIT && !dd_log_probe_eval_condition(def, execute_data, retval)) { + return; + } + + ddog_Vec_SnapshotEvaluationError *errors; + zend_string *result = dd_eval_string(def->parent.probe.probe.log.segments, def->parent.probe.probe.log.capture, retval, &errors); + if (errors) { + dd_submit_probe_eval_error_snapshot(&def->parent.probe, errors); + } + ddog_CharSlice result_msg = dd_zend_string_to_CharSlice(result); + dd_log_probe_ensure_payload(dyn, def, &result_msg); + + if (def->parent.probe.probe.log.capture_snapshot) { + DDTRACE_G(debugger_capture_arena) = dyn->capture_arena ? dyn->capture_arena : zend_arena_create(65536); + ddog_DebuggerCapture *capture = ddog_snapshot_exit(dyn->payload); + dd_log_probe_capture_snapshot(capture, def, execute_data); + const ddog_CaptureConfiguration *capture_config = def->parent.probe.probe.log.capture; + struct ddog_CaptureValue capture_value = {0}; + ddtrace_create_capture_value(&EX(This), &capture_value, capture_config, capture_config->max_reference_depth); + ddog_snapshot_add_field(capture, DDOG_FIELD_TYPE_ARG, DDOG_CHARSLICE_C("@return"), capture_value); + } + ddtrace_sidecar_send_debugger_datum(dyn->payload); + if (DDTRACE_G(debugger_capture_arena)) { + zend_arena_destroy(DDTRACE_G(debugger_capture_arena)); + DDTRACE_G(debugger_capture_arena) = NULL; + } + zend_string_release(result); + zend_string_release(dyn->service); +} + +static bool dd_log_probe_begin(zend_ulong invocation, zend_execute_data *execute_data, void *auxiliary, void *dynamic) { + dd_log_probe_dyn *dyn = dynamic; + dd_log_probe_def *def = auxiliary; + UNUSED(invocation); + + dd_probe_mark_active(&def->parent); + + zval retval; + ZVAL_NULL(&retval); + + dyn->payload = NULL; + dyn->rejected = def->parent.probe.evaluate_at == DDOG_EVALUATE_AT_ENTRY && !dd_log_probe_eval_condition(def, execute_data, &retval); + dyn->capture_arena = NULL; + + if (!dyn->rejected && def->parent.probe.evaluate_at == DDOG_EVALUATE_AT_ENTRY) { + dd_log_probe_ensure_payload(dyn, def, NULL); + if (def->parent.probe.probe.log.capture_snapshot) { + ddog_DebuggerCapture *capture = ddog_snapshot_entry(dyn->payload); + DDTRACE_G(debugger_capture_arena) = zend_arena_create(65536); + dd_log_probe_capture_snapshot(capture, def, execute_data); + dyn->capture_arena = DDTRACE_G(debugger_capture_arena); + DDTRACE_G(debugger_capture_arena) = NULL; + } + } + + return true; +} + +static int64_t dd_set_log_probe(const ddog_Probe *probe, const ddog_MaybeShmLimiter *limiter) { + dd_log_probe_def *def = emalloc(sizeof(*def)); + def->limiter = limiter; + return dd_init_live_debugger_probe(probe, &def->parent, dd_log_probe_begin, dd_log_probe_end, dd_probe_dtor, sizeof(dd_log_probe_dyn)); +} + +static void dd_metric_probe_end(zend_ulong invocation, zend_execute_data *execute_data, zval *retval, void *auxiliary, void *dynamic) { + dd_probe_def *def = auxiliary; + UNUSED(invocation, dynamic); + if (dd_probe_file_mismatch(def, execute_data)) { + return; + } + + dd_probe_mark_active(def); + + ddog_CharSlice *name = &def->probe.probe.metric.name; + zend_string *metric_name = zend_strpprintf(0, "dynamic.instrumentation.metric.probe.%.*s", (int)name->len, name->ptr); + + ddog_ValueEvaluationResult result = dd_eval_value(def->probe.probe.metric.value, retval); + if (result.tag == DDOG_VALUE_EVALUATION_RESULT_ERROR) { + dd_submit_probe_eval_error_snapshot(&def->probe, result.error); + return; + } + + ddog_IntermediateValue value = ddog_evaluated_value_get(result.success); + double metric_value = 0; + switch (value.tag) { + case DDOG_INTERMEDIATE_VALUE_NULL: + break; + case DDOG_INTERMEDIATE_VALUE_STRING: ; + zend_string *str = dd_CharSlice_to_zend_string(value.string); + metric_value = zend_strtod(ZSTR_VAL(str), NULL); + zend_string_release(str); + break; + case DDOG_INTERMEDIATE_VALUE_NUMBER: + metric_value = value.number; + break; + case DDOG_INTERMEDIATE_VALUE_BOOL: + metric_value = value.bool_; + break; + case DDOG_INTERMEDIATE_VALUE_REFERENCED: + metric_value = zval_get_double(value.referenced); + break; + } + + switch (def->probe.probe.metric.kind) { + case DDOG_METRIC_KIND_COUNT: + ddtrace_sidecar_dogstatsd_count(metric_name, (zend_long)metric_value, NULL); + break; + case DDOG_METRIC_KIND_GAUGE: + ddtrace_sidecar_dogstatsd_gauge(metric_name, metric_value, NULL); + break; + case DDOG_METRIC_KIND_HISTOGRAM: + ddtrace_sidecar_dogstatsd_histogram(metric_name, metric_value, NULL); + break; + case DDOG_METRIC_KIND_DISTRIBUTION: + ddtrace_sidecar_dogstatsd_distribution(metric_name, metric_value, NULL); + break; + } + + ddog_evaluated_value_drop(result.success); + zend_string_release(metric_name); +} + +static bool dd_metric_probe_begin(zend_ulong invocation, zend_execute_data *execute_data, void *auxiliary, void *dynamic) { + zval retval; + ZVAL_NULL(&retval); + dd_metric_probe_end(invocation, execute_data, &retval, auxiliary, dynamic); + return true; +} + +static int64_t dd_set_metric_probe(const ddog_Probe *probe) { + dd_probe_def *def = emalloc(sizeof(*def)); + + zai_hook_begin begin = NULL; + zai_hook_end end = NULL; + if (probe->target.in_body_location == DDOG_IN_BODY_LOCATION_START) { + begin = dd_metric_probe_begin; + } else { + end = dd_metric_probe_end; + } + return dd_init_live_debugger_probe(probe, def, begin, end, dd_probe_dtor, 0); +} + +static int64_t dd_set_probe(const ddog_Probe probe, const ddog_MaybeShmLimiter *limiter) { + switch (probe.probe.tag) { + case DDOG_PROBE_TYPE_METRIC: + return dd_set_metric_probe(&probe); + case DDOG_PROBE_TYPE_LOG: + return dd_set_log_probe(&probe, limiter); + case DDOG_PROBE_TYPE_SPAN: + return dd_set_span_probe(&probe); + case DDOG_PROBE_TYPE_SPAN_DECORATION: + return dd_set_span_decoration(&probe); + } + return -1; +} + +static void dd_remove_live_debugger_probe(int64_t id) { + dd_probe_def *def; + if ((def = zend_hash_index_find_ptr(&DDTRACE_G(active_rc_hooks), (zend_ulong)id))) { + zend_string *scope = def->scope ? zend_string_copy(def->scope) : NULL; + zend_string *func = def->function ? zend_string_copy(def->function) : NULL; + zai_hook_remove( + def->scope ? (zai_str)ZAI_STR_FROM_ZSTR(def->scope) : (zai_str)ZAI_STR_EMPTY, + def->function ? (zai_str)ZAI_STR_FROM_ZSTR(def->function) : (zai_str)ZAI_STR_EMPTY, + id); + if (scope) { + zend_string_release(scope); + } + if (func) { + zend_string_release(func); + } + } +} + +static void dd_free_void_collection_none(struct ddog_VoidCollection collection) { + UNUSED(collection); +} + +static ddog_VoidCollection dd_empty_collection = { + .free = dd_free_void_collection_none, + .count = 0, + .elements = NULL, +}; + +static void dd_free_void_collection(struct ddog_VoidCollection collection) { + efree((void *)collection.elements); +} + +static ddog_VoidCollection dd_alloc_void_collection(uint32_t elements) { + return (ddog_VoidCollection){ + .free = dd_free_void_collection, + .count = elements, + .elements = emalloc(sizeof(void *)), + }; +} + +static void dd_intermediate_to_zval(struct ddog_IntermediateValue val, zval *zv) { + switch (val.tag) { + case DDOG_INTERMEDIATE_VALUE_STRING: + ZVAL_STRINGL(zv, val.string.ptr, val.string.len); + break; + case DDOG_INTERMEDIATE_VALUE_NUMBER: + ZVAL_DOUBLE(zv, val.number); + break; + case DDOG_INTERMEDIATE_VALUE_BOOL: + ZVAL_BOOL(zv, val.bool_); + break; + case DDOG_INTERMEDIATE_VALUE_NULL: + ZVAL_NULL(zv); + break; + case DDOG_INTERMEDIATE_VALUE_REFERENCED: + ZVAL_COPY(zv, val.referenced); + break; + } +} + +static zend_long dd_zval_convert_index(zval *zvp, bool *success) { + zval *dim = (zval *) zvp; + + ZVAL_DEREF(dim); + switch (Z_TYPE_P(dim)) { + case IS_LONG: + *success = true; + return Z_LVAL_P(dim); + case IS_STRING: ; + zend_long off; + *success = IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &off, NULL, true, NULL, NULL); + return off; + default: + *success = false; + return 0; + } +} + +static inline int dd_eval_cmp(struct ddog_IntermediateValue a, struct ddog_IntermediateValue b) { + zval zva, zvb; + dd_intermediate_to_zval(a, &zva); + dd_intermediate_to_zval(b, &zvb); + + bool objectA = Z_TYPE(zva) == IS_OBJECT || (Z_TYPE(zva) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL(zva)) == IS_OBJECT); + bool objectB = Z_TYPE(zvb) == IS_OBJECT || (Z_TYPE(zvb) == IS_REFERENCE && Z_TYPE_P(Z_REFVAL(zvb)) == IS_OBJECT); + + int ret; + if (objectA != objectB) { + // Avoid object casting, which may lead to notices + ret = objectA - objectB; + } else { + ret = zend_compare(&zva, &zvb); + } + + zval_ptr_dtor(&zva); + zval_ptr_dtor(&zvb); + + return ret; +} + +static bool dd_eval_equals(void *ctx, struct ddog_IntermediateValue a, struct ddog_IntermediateValue b) { + UNUSED(ctx); +#define TAGCASE(a, b) MIN(a, b) + (MAX(a, b) << 4) + switch (TAGCASE(a.tag, b.tag)) { + case TAGCASE(DDOG_INTERMEDIATE_VALUE_STRING, DDOG_INTERMEDIATE_VALUE_STRING): + return a.string.len == b.string.len && memcmp(a.string.ptr, b.string.ptr, a.string.len) == 0; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_NUMBER, DDOG_INTERMEDIATE_VALUE_NUMBER): + return a.number == b.number; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_BOOL, DDOG_INTERMEDIATE_VALUE_BOOL): + return a.bool_ == b.bool_; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_BOOL, DDOG_INTERMEDIATE_VALUE_NUMBER): + return a.tag == DDOG_INTERMEDIATE_VALUE_BOOL ? a.number == b.bool_ : a.bool_ == b.number; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_NULL, DDOG_INTERMEDIATE_VALUE_NULL): + return true; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_NULL, DDOG_INTERMEDIATE_VALUE_BOOL): + return (b.tag == DDOG_INTERMEDIATE_VALUE_NULL ? a.bool_ : b.bool_) == false; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_NUMBER, DDOG_INTERMEDIATE_VALUE_NULL): + return (b.tag == DDOG_INTERMEDIATE_VALUE_NULL ? a.number : b.number) == 0; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_STRING, DDOG_INTERMEDIATE_VALUE_NULL): + return (b.tag == DDOG_INTERMEDIATE_VALUE_NULL ? a.string : b.string).len == 0; + case TAGCASE(DDOG_INTERMEDIATE_VALUE_STRING, DDOG_INTERMEDIATE_VALUE_BOOL): + return ((b.tag == DDOG_INTERMEDIATE_VALUE_BOOL ? a.string : b.string).len == 0) != (b.tag != DDOG_INTERMEDIATE_VALUE_BOOL ? a.bool_ : b.bool_); + case TAGCASE(DDOG_INTERMEDIATE_VALUE_STRING, DDOG_INTERMEDIATE_VALUE_REFERENCED): ; + // avoid copies for ref == str + const zval *zv = a.tag == DDOG_INTERMEDIATE_VALUE_REFERENCED ? a.referenced : b.referenced; + if (Z_TYPE_P(zv) == IS_STRING) { + ddog_CharSlice *str = a.tag == DDOG_INTERMEDIATE_VALUE_REFERENCED ? &b.string : &a.string; + return zend_string_equals_cstr(Z_STR_P(zv), str->ptr, str->len); + } + } + + return dd_eval_cmp(a, b) == 0; +} + +static bool dd_eval_greater_than(void *ctx, struct ddog_IntermediateValue a, struct ddog_IntermediateValue b) { + UNUSED(ctx); + return dd_eval_cmp(a, b) > 0; +} + +static bool dd_eval_greater_or_equals(void *ctx, struct ddog_IntermediateValue a, struct ddog_IntermediateValue b) { + UNUSED(ctx); + return dd_eval_cmp(a, b) >= 0; +} + +static const void *dd_eval_fetch_identifier(void *ctx, const ddog_CharSlice *name) { + struct eval_ctx *eval_ctx = ctx; + zend_execute_data *execute_data = eval_ctx->frame; + + if (EX(func)) { + if (ZEND_USER_CODE(EX(func)->type)) { + zend_execute_data *current_execute_data = EG(current_execute_data); + EG(current_execute_data) = execute_data; + zend_array *symtable = zend_rebuild_symbol_table(); + if (!symtable) { + return NULL; + } + zval *zvp = zend_hash_str_find_ind(symtable, name->ptr, name->len); + EG(current_execute_data) = current_execute_data; + if (zvp) { + return zvp; + } + } else { + int call_args = MIN(EX_NUM_ARGS(), EX(func)->common.num_args); + for (int i = 0; i < call_args; ++i) { + const char *argname = EX(func)->internal_function.arg_info[i].name; + if (zend_binary_strcmp(argname, strlen(argname), name->ptr, name->len) == 0) { + return EX_VAR_NUM(i); + } + } + } + } + + if (name->len == 4 && memcmp(name->ptr, ZEND_STRL("this")) == 0) { + if (hasThis()) { + return &EX(This); + } + return NULL; + } + + if (name->len == sizeof("duration") && memcmp(name->ptr, ZEND_STRL("@duration")) == 0) { + ddtrace_span_data *span = ddtrace_active_span(); + if (span) { + zval zv; // milliseconds + ZVAL_DOUBLE(&zv, (zend_hrtime() - span->duration_start) / 1000000.); + return dd_persist_eval_arena(eval_ctx, &zv); + } else { + return NULL; + } + } + + if (name->len == sizeof("return") && memcmp(name->ptr, ZEND_STRL("@return")) == 0) { + return eval_ctx->retval; + } + + if (name->len == sizeof("exception") && memcmp(name->ptr, ZEND_STRL("@exception")) == 0) { + if (EG(exception)) { + zval zv; + ZVAL_OBJ_COPY(&zv, EG(exception)); + return dd_persist_eval_arena(eval_ctx, &zv); + } + return NULL; + } + + return NULL; +} + +static const void *dd_eval_fetch_nested(void *ctx, const void *container_ptr, struct ddog_IntermediateValue index) { + zval *container = (zval *)container_ptr, *dim = (zval *)index.referenced; + ZVAL_DEREF(container); + switch (Z_TYPE_P(container)) { + case IS_OBJECT: ; + if (ddog_snapshot_redacted_type(dd_zend_string_to_CharSlice(Z_OBJCE_P(container)->name))) { + return ddog_EVALUATOR_RESULT_REDACTED; + } + zend_property_info *prop; + zend_string *prop_name; + if (index.tag == DDOG_INTERMEDIATE_VALUE_STRING) { + prop = zend_hash_str_find_ptr(&Z_OBJCE_P(container)->properties_info, index.string.ptr, index.string.len); + if (!prop) { + prop_name = zend_string_init(index.string.ptr, index.string.len, 0); + } else { + prop_name = zend_string_copy(prop->name); + } + } else if (index.tag == DDOG_INTERMEDIATE_VALUE_REFERENCED) { + ZVAL_DEREF(dim); + if (Z_TYPE_P(dim) != IS_STRING) { + return ddog_EVALUATOR_RESULT_INVALID; + } + prop = zend_hash_find_ptr(&Z_OBJCE_P(container)->properties_info, Z_STR_P(dim)); + prop_name = zend_string_copy(Z_STR_P(dim)); + } else { + return ddog_EVALUATOR_RESULT_INVALID; + } + zval rv; + uint32_t *guard, orig_guard; + if (Z_OBJCE_P(container)->ce_flags & ZEND_ACC_USE_GUARDS) { + guard = zend_get_property_guard(Z_OBJ_P(container), prop_name); + orig_guard = *guard; + *guard |= ZEND_GUARD_PROPERTY_MASK; // bypass __magicMethods + } else { + guard = NULL; + } +#if PHP_VERSION_ID < 80000 + zval *ret = zend_read_property_ex(prop ? prop->ce : Z_OBJCE_P(container), container, prop_name, 1, &rv); +#else + zval *ret = zend_read_property_ex(prop ? prop->ce : Z_OBJCE_P(container), Z_OBJ_P(container), prop_name, 1, &rv); +#endif + if (guard) { + *guard = orig_guard; + } + zend_string_release(prop_name); + if (ret == &EG(uninitialized_zval)) { + return NULL; + } + if (ret == &rv) { + ret = dd_persist_eval_arena(ctx, ret); + } + return ret; + case IS_ARRAY: + switch (index.tag) { + case DDOG_INTERMEDIATE_VALUE_STRING: + return zend_symtable_str_find(Z_ARR_P(container), index.string.ptr, index.string.len); + case DDOG_INTERMEDIATE_VALUE_NUMBER: + return zend_hash_index_find(Z_ARR_P(container), (zend_ulong)index.number); + case DDOG_INTERMEDIATE_VALUE_REFERENCED: + ZVAL_DEREF(dim); + switch (Z_TYPE_P(dim)) { + case IS_STRING: + return zend_symtable_find(Z_ARR_P(container), Z_STR_P(dim)); + case IS_LONG: + return zend_hash_index_find(Z_ARR_P(container), (zend_ulong) Z_LVAL_P(dim)); + case IS_DOUBLE: + return zend_hash_index_find(Z_ARR_P(container), (zend_ulong) Z_DVAL_P(dim)); + } + return ddog_EVALUATOR_RESULT_INVALID; + default: + return ddog_EVALUATOR_RESULT_INVALID; + } + case IS_STRING: ; + zend_long off; + switch (index.tag) { + case DDOG_INTERMEDIATE_VALUE_STRING: ; + char *end = (char *)index.string.ptr + index.string.len; + off = strtoll(index.string.ptr, &end, 10); + break; + case DDOG_INTERMEDIATE_VALUE_NUMBER: + off = (zend_long)index.number; + break; + case DDOG_INTERMEDIATE_VALUE_REFERENCED: ; + bool success; + off = dd_zval_convert_index((zval *)index.referenced, &success); + if (!success) { + return ddog_EVALUATOR_RESULT_INVALID; + } + break; + default: + return ddog_EVALUATOR_RESULT_INVALID; + } + zval zv; + if (off < 0 || off >= (zend_long)Z_STRLEN_P(container)) { + ZVAL_EMPTY_STRING(&zv); + } else { + char chr = Z_STRVAL_P(container)[off]; +#if PHP_VERSION_ID < 70200 + ZVAL_STRINGL(&zv, &chr, 1); +#else + ZVAL_STR_COPY(&zv, zend_one_char_string[(unsigned char) chr]); +#endif + } + return dd_persist_eval_arena(ctx, &zv); + default: + return ddog_EVALUATOR_RESULT_INVALID; + } +} + +static void dd_sandboxed_read_dimension(zval *container, zval *dim, zval **ret, zval *rv) { + zai_sandbox sandbox; + zai_sandbox_open(&sandbox); + + // We expect read access to have no real side effects, but it still might throw for invalid offsets etc. + zend_try { +#if PHP_VERSION_ID < 80000 + if (Z_OBJ_HANDLER_P(container, has_dimension)(container, dim, 0)) { + *ret = Z_OBJ_HANDLER_P(container, read_dimension)(container, dim, BP_VAR_IS, rv); +#else + if (Z_OBJ_HANDLER_P(container, has_dimension)(Z_OBJ_P(container), dim, 0)) { + *ret = Z_OBJ_HANDLER_P(container, read_dimension)(Z_OBJ_P(container), dim, BP_VAR_IS, rv); +#endif + } else { + *ret = NULL; + } + } zend_catch { + zai_sandbox_bailout(&sandbox); + } zend_end_try(); + + zai_sandbox_close(&sandbox); +} + +static const void *dd_eval_fetch_index(void *ctx, const void *container_ptr, struct ddog_IntermediateValue index) { + zval *container = (zval *)container_ptr; + ZVAL_DEREF(container); + if (Z_TYPE_P(container) == IS_OBJECT) { + if (Z_OBJ_HANDLER_P(container, read_dimension) != zend_std_read_dimension) { // of internal classes like weakmap + zval dim, rv, *ret = (zval *)ddog_EVALUATOR_RESULT_INVALID; + dd_intermediate_to_zval(index, &dim); + dd_sandboxed_read_dimension(container, &dim, &ret, &rv); + + zval_ptr_dtor(&dim); + if (ret == &rv) { + return dd_persist_eval_arena(ctx, &rv); + } + return ret; + } + } + return dd_eval_fetch_nested(ctx, container_ptr, index); +} + +static uintptr_t dd_eval_length(void *ctx, const void *zvp) { + UNUSED(ctx); + const zval *zv = zvp; + retry: + switch (Z_TYPE_P(zv)) { + case IS_REFERENCE: + ZVAL_DEREF(zv); + goto retry; + + case IS_ARRAY: + return zend_array_count(Z_ARRVAL_P(zv)); + + case IS_OBJECT: + // Internal handler + if (Z_OBJ_HANDLER_P(zv, count_elements)) { + zend_long num; + zend_object *ex = EG(exception); +#if PHP_VERSION_ID < 80000 + if (SUCCESS == Z_OBJ_HANDLER_P(zv, count_elements)((zval *)zv, &num)) { +#else + if (SUCCESS == Z_OBJ_HANDLER_P(zv, count_elements)(Z_OBJ_P(zv), &num)) { +#endif + EG(exception) = ex; + return (uint64_t)num; + } + if (EG(exception)) { + zend_clear_exception(); + } + EG(exception) = ex; + } + + return zend_array_count(Z_OBJPROP_P(zv)); + + case IS_STRING: + return Z_STRLEN_P(zv); + + case IS_DOUBLE: + case IS_LONG: ; + zend_string *str = ddtrace_convert_to_str(zv); + uint64_t len = ZSTR_LEN(str); + zend_string_release(str); + return len; + + default: + return 0; + } +} + +static ddog_VoidCollection dd_eval_try_enumerate(void *ctx, const void *zvp) { + UNUSED(ctx); + const zval *zv = zvp; + HashTable *values; + retry: + switch (Z_TYPE_P(zv)) { + case IS_REFERENCE: + ZVAL_DEREF(zv); + goto retry; + + case IS_ARRAY: + values = Z_ARR_P(zv); + break; + + case IS_OBJECT: + if (ddog_snapshot_redacted_type(dd_zend_string_to_CharSlice(Z_OBJCE_P(zv)->name))) { + ddog_VoidCollection collection = dd_empty_collection; + collection.count = (intptr_t)ddog_EVALUATOR_RESULT_REDACTED; + return collection; + } + values = Z_OBJPROP_P(zv); + break; + + default: ; + ddog_VoidCollection collection = dd_empty_collection; + collection.count = (intptr_t)ddog_EVALUATOR_RESULT_INVALID; + return collection; + } + + zval *val; + int idx = 0; + ddog_VoidCollection collection = dd_alloc_void_collection(zend_hash_num_elements(values)); + ZEND_HASH_FOREACH_VAL_IND(values, val) { + ((zval **)collection.elements)[idx++] = val; + } ZEND_HASH_FOREACH_END(); + collection.count = idx; + return collection; +} + +static void dd_stringify_limited_str(zend_string *string, smart_str *str, const ddog_CaptureConfiguration *config) { + if (ZSTR_LEN(string) <= config->max_length) { + smart_str_append(str, string); + } else { + smart_str_appendl(str, ZSTR_VAL(string), config->max_length); + smart_str_appends(str, "..."); + } +} + +static void dd_stringify_zval(const zval *zv, smart_str *str, const ddog_CaptureConfiguration *config, int remaining_nesting) { + ZVAL_DEREF(zv); + switch (Z_TYPE_P(zv)) { + case IS_FALSE: + smart_str_appends(str, "false"); + break; + + case IS_TRUE: + smart_str_appends(str, "true"); + break; + + case IS_LONG: + smart_str_append_long(str, Z_LVAL_P(zv)); + break; + + case IS_DOUBLE: + smart_str_append_double(str, Z_DVAL_P(zv), EG(precision), 0); + break; + + case IS_STRING: + dd_stringify_limited_str(Z_STR_P(zv), str, config); + break; + + case IS_ARRAY: { + if (remaining_nesting == 0) { + smart_str_appends(str, "[...]"); + break; + } + zval *val; + bool first = true; + smart_str_appendc(str, '['); + if (zend_array_is_list(Z_ARR_P(zv))) { + int remaining_fields = config->max_collection_size; + ZEND_HASH_FOREACH_VAL(Z_ARR_P(zv), val) { + if (!first) { + smart_str_appends(str, ", "); + } + first = false; + if (remaining_fields-- == 0) { + smart_str_appends(str, "...]"); + break; + } + + dd_stringify_zval(val, str, config, remaining_nesting - 1); + } ZEND_HASH_FOREACH_END(); + } else { + zend_long idx; + zend_string *key; + int remaining_fields = config->max_collection_size; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARR_P(zv), idx, key, val) { + if (!first) { + smart_str_appends(str, ", "); + } + first = false; + if (remaining_fields-- == 0) { + smart_str_appends(str, "...]"); + break; + } + + if (key) { + dd_stringify_limited_str(key, str, config); + if (ddog_snapshot_redacted_name(dd_zend_string_to_CharSlice(key))) { + smart_str_appends(str, " => {redacted}"); + continue; + } + } else { + smart_str_append_long(str, idx); + } + smart_str_appends(str, " => "); + dd_stringify_zval(val, str, config, remaining_nesting - 1); + } ZEND_HASH_FOREACH_END(); + } + smart_str_appendc(str, ']'); + break; + } + + case IS_OBJECT: { + zend_class_entry *ce = Z_OBJCE_P(zv); + smart_str_appendc(str, '('); + smart_str_append(str, ce->name); + smart_str_appendc(str, ')'); + smart_str_appendc(str, '{'); + if (ddog_snapshot_redacted_type(dd_zend_string_to_CharSlice(ce->name))) { + smart_str_appends(str, "redacted}"); + break; + } + if (remaining_nesting == 0) { + smart_str_appends(str, "...}"); + break; + } + zval *val; + zend_string *key; + int remaining_fields = config->max_field_count; +#if PHP_VERSION_ID < 70400 + int is_temp = 0; +#endif + // reverse to prefer child class properties first + HashTable *ht = ce->type == ZEND_INTERNAL_CLASS ? +#if PHP_VERSION_ID < 70400 + Z_OBJDEBUG_P(zv, is_temp) +#else + zend_get_properties_for((zval *)zv, ZEND_PROP_PURPOSE_DEBUG) +#endif + : Z_OBJPROP_P(zv); + bool first = true; + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(ht, key, val) { + if (!key) { + continue; + } + + if (!first) { + smart_str_appends(str, ", "); + } + first = false; + if (remaining_fields-- == 0) { + smart_str_appends(str, "...}"); + break; + } + + ddog_CharSlice name; + if (ZSTR_LEN(key) < 3 || ZSTR_VAL(key)[0]) { + smart_str_append(str, key); + name = dd_zend_string_to_CharSlice(key); + } else if (ZSTR_VAL(key)[1] == '*') { // skip \0*\0 + name = (ddog_CharSlice){ .len = ZSTR_LEN(key) - 3, .ptr = ZSTR_VAL(key) + 3 }; + smart_str_appendl(str, name.ptr, name.len); + } else { + int classname_len = (int)strlen(ZSTR_VAL(key) + 1); + smart_str_appendl(str, ZSTR_VAL(key) + 1, classname_len); + smart_str_appends(str, "::"); + name = (ddog_CharSlice){ .len = ZSTR_LEN(key) - classname_len - 2, .ptr = ZSTR_VAL(key) + classname_len + 2 }; + smart_str_appendl(str, name.ptr, name.len); + } + smart_str_appends(str, ": "); + if (ddog_snapshot_redacted_name(name)) { + smart_str_appends(str, "{redacted}"); + } else { + ZVAL_DEINDIRECT(val); + dd_stringify_zval(val, str, config, remaining_nesting - 1); + } + } ZEND_HASH_FOREACH_END(); + if (ce->type == ZEND_INTERNAL_CLASS) { +#if PHP_VERSION_ID < 70400 + if (is_temp) { + zend_array_release(ht); + } +#else + zend_release_properties(ht); +#endif + } + smart_str_appendc(str, '}'); + break; + } + + case IS_RESOURCE: { + smart_str_appends(str, zend_rsrc_list_get_rsrc_type(Z_RES_P(zv))); + smart_str_appendc(str, '#'); + smart_str_append_long(str, Z_RES_P(zv)->handle); + break; + } + + default: + smart_str_appends(str, "null"); + } +} + + +static ddog_CharSlice dd_eval_get_string(void *ctx, const void *zvp) { + struct eval_ctx *eval_ctx = ctx; + const zval *zv = zvp; + + switch (Z_TYPE_P(zv)) { + case IS_STRING: + return dd_zend_string_to_CharSlice(Z_STR_P(zv)); + case IS_TRUE: + return DDOG_CHARSLICE_C("true"); + case IS_FALSE: + return DDOG_CHARSLICE_C("false"); + case IS_NULL: + return DDOG_CHARSLICE_C("null"); + } + + smart_str str = {0}; + dd_stringify_zval(zv, &str, eval_ctx->config, eval_ctx->config->max_reference_depth); + if (!str.s) { + return DDOG_CHARSLICE_C(""); + } + smart_str_0(&str); + return dd_persist_str_eval_arena(ctx, str.s); +} + +static ddog_CharSlice dd_eval_stringify(void *ctx, const void *zvp) { + UNUSED(ctx); + const zval *zv = zvp; + zend_string *str = ddtrace_convert_to_str(zv); + return dd_persist_str_eval_arena(ctx, str); +} + +static intptr_t dd_eval_convert_index(void *ctx, const void *zvp) { + UNUSED(ctx); + bool success; + intptr_t index = dd_zval_convert_index((zval *)zvp, &success); + if (success) { + return index; + } + return (intptr_t)ddog_EVALUATOR_RESULT_INVALID; +} + +static bool dd_eval_instanceof(void *ctx, const void *zvp, const ddog_CharSlice *class) { + UNUSED(ctx); + const zval *zv = zvp; + ZVAL_DEREF(zv); + if (Z_TYPE_P(zv) == IS_OBJECT) { + if (zend_binary_strcasecmp(ZEND_STRL("object"), class->ptr, class->len) == 0) { + return true; + } + zend_string *class_str = dd_CharSlice_to_zend_string(*class); +#if PHP_VERSION_ID < 70400 + zend_class_entry *ce = zend_lookup_class_ex(class_str, NULL, 0); +#else + zend_class_entry *ce = zend_lookup_class_ex(class_str, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); +#endif + zend_string_release(class_str); + return ce && instanceof_function(Z_OBJCE_P(zv), ce); + } + const char *name; +#if PHP_VERSION_ID < 70300 + if (Z_TYPE_P(zv) == _IS_BOOL || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_TRUE) + { + name = "bool"; + } + else +#endif + { + name = zend_zval_type_name(zv); + } + return zend_binary_strcasecmp(name, strlen(name), class->ptr, class->len) == 0; +} + +const ddog_Evaluator dd_evaluator = { + .equals = dd_eval_equals, + .greater_than = dd_eval_greater_than, + .greater_or_equals = dd_eval_greater_or_equals, + .fetch_identifier = dd_eval_fetch_identifier, + .fetch_index = dd_eval_fetch_index, + .fetch_nested = dd_eval_fetch_nested, + .length = dd_eval_length, + .try_enumerate = dd_eval_try_enumerate, + .stringify = dd_eval_stringify, + .get_string = dd_eval_get_string, + .convert_index = dd_eval_convert_index, + .instanceof = dd_eval_instanceof, +}; + +ddog_LiveDebuggerSetup ddtrace_live_debugger_setup = { + .callbacks = { + .set_probe = dd_set_probe, + .remove_probe = dd_remove_live_debugger_probe, + }, + .evaluator = &dd_evaluator, +}; + +void ddtrace_live_debugger_minit(void) { + zend_string *value; + ZEND_HASH_FOREACH_STR_KEY(get_global_DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS(), value) { + ddog_snapshot_add_redacted_name(dd_zend_string_to_CharSlice(value)); + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_FOREACH_STR_KEY(get_global_DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES(), value) { + ddog_snapshot_add_redacted_type(dd_zend_string_to_CharSlice(value)); + } ZEND_HASH_FOREACH_END(); +} diff --git a/ext/live_debugger.h b/ext/live_debugger.h new file mode 100644 index 0000000000..8759466b2c --- /dev/null +++ b/ext/live_debugger.h @@ -0,0 +1,16 @@ +#ifndef DD_LIVE_DEBUGGER_H +#define DD_LIVE_DEBUGGER_H + +#include + +extern ddog_LiveDebuggerSetup ddtrace_live_debugger_setup; + +void ddtrace_live_debugger_minit(void); + +static inline void ddtrace_snapshot_redacted_name(ddog_CaptureValue *capture_value, ddog_CharSlice name) { + if (ddog_snapshot_redacted_name(name)) { + capture_value->not_captured_reason = DDOG_CHARSLICE_C("redactedIdent"); + } +} + +#endif // DD_LIVE_DEBUGGER_H diff --git a/ext/logging.c b/ext/logging.c index 3188e83df9..163bbe4215 100644 --- a/ext/logging.c +++ b/ext/logging.c @@ -205,16 +205,16 @@ void ddtrace_log_init(void) { ddog_log_callback = ddtrace_log_callback; } -bool ddtrace_alter_dd_trace_debug(zval *old_value, zval *new_value) { - UNUSED(old_value); +bool ddtrace_alter_dd_trace_debug(zval *old_value, zval *new_value, zend_string *new_str) { + UNUSED(old_value, new_str); dd_log_set_level(Z_TYPE_P(new_value) == IS_TRUE); return true; } -bool ddtrace_alter_dd_trace_log_level(zval *old_value, zval *new_value) { - UNUSED(old_value); +bool ddtrace_alter_dd_trace_log_level(zval *old_value, zval *new_value, zend_string *new_str) { + UNUSED(old_value, new_str); if (runtime_config_first_init ? get_DD_TRACE_DEBUG() : get_global_DD_TRACE_DEBUG()) { return true; } diff --git a/ext/logging.h b/ext/logging.h index 611c7f7064..14b7260f7f 100644 --- a/ext/logging.h +++ b/ext/logging.h @@ -26,7 +26,7 @@ int ddtrace_bgs_logf(const char *fmt, ...); /* }}} */ void ddtrace_log_init(void); -bool ddtrace_alter_dd_trace_debug(zval *old_value, zval *new_value); -bool ddtrace_alter_dd_trace_log_level(zval *old_value, zval *new_value); +bool ddtrace_alter_dd_trace_debug(zval *old_value, zval *new_value, zend_string *new_str); +bool ddtrace_alter_dd_trace_log_level(zval *old_value, zval *new_value, zend_string *new_str); #endif // DD_LOGGING_H diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index 23d34a4eff..b1cb69d6ca 100644 --- a/ext/priority_sampling/priority_sampling.c +++ b/ext/priority_sampling/priority_sampling.c @@ -18,7 +18,7 @@ ZEND_EXTERN_MODULE_GLOBALS(ddtrace); void ddtrace_try_read_agent_rate(void) { ddog_CharSlice data; - if (DDTRACE_G(remote_config_reader) && ddog_agent_remote_config_read(DDTRACE_G(remote_config_reader), &data)) { + if (DDTRACE_G(agent_config_reader) && ddog_agent_remote_config_read(DDTRACE_G(agent_config_reader), &data)) { zval json; if ((int)data.len > 0 && zai_json_decode_assoc_safe(&json, data.ptr, (int)data.len, 3, true) == SUCCESS) { if (Z_TYPE(json) == IS_ARRAY) { @@ -106,19 +106,20 @@ static ddtrace_rule_result dd_match_rules(ddtrace_span_data *span, bool eval_roo int index = -3; if (++index >= skip_at) { - return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX }; + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX, .mechanism = DD_MECHANISM_RULE }; } zend_array *meta = ddtrace_property_array(&span->property_meta); if (zend_hash_str_exists(meta, ZEND_STRL("manual.keep"))) { - return (ddtrace_rule_result){ .sampling_rate = 1, .rule = -2 }; + // manual.keep and manual.drop count as manual + return (ddtrace_rule_result){ .sampling_rate = 1, .rule = -2, .mechanism = DD_MECHANISM_MANUAL }; } if (++index >= skip_at) { - return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX }; + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX, .mechanism = DD_MECHANISM_RULE }; } if (zend_hash_str_exists(meta, ZEND_STRL("manual.drop"))) { - return (ddtrace_rule_result){ .sampling_rate = 0, .rule = -1 }; + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = -1, .mechanism = DD_MECHANISM_MANUAL }; } zval *rule; @@ -140,11 +141,20 @@ static ddtrace_rule_result dd_match_rules(ddtrace_span_data *span, bool eval_roo if (dd_check_sampling_rule(Z_ARR_P(rule), span)) { zval *sample_rate_zv = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("sample_rate")); - return (ddtrace_rule_result){ .sampling_rate = sample_rate_zv ? zval_get_double(sample_rate_zv) : 1, .rule = index }; + zval *provenance_zv = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("_provenance")); + enum dd_sampling_mechanism mechanism = DD_MECHANISM_RULE; + if (provenance_zv && Z_TYPE_P(provenance_zv) == IS_STRING) { + if (zend_string_equals_literal(Z_STR_P(provenance_zv), "customer")) { + mechanism = DD_MECHANISM_REMOTE_USER_RULE; + } else if (zend_string_equals_literal(Z_STR_P(provenance_zv), "dynamic")) { + mechanism = DD_MECHANISM_REMOTE_DYNAMIC_RULE; + } + } + return (ddtrace_rule_result){ .sampling_rate = sample_rate_zv ? zval_get_double(sample_rate_zv) : 1, .rule = index, .mechanism = mechanism }; } } ZEND_HASH_FOREACH_END(); - return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX }; + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX, .mechanism = DD_MECHANISM_RULE }; } void ddtrace_decide_on_closed_span_sampling(ddtrace_span_data *span) { @@ -212,7 +222,9 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { if (result.rule != INT32_MAX) { sample_rate = result.sampling_rate; - } else if (default_sample_rate < 0) { + } else if (default_sample_rate >= 0) { + result.mechanism = DD_MECHANISM_RULE; + } else { explicit_rule = false; ddtrace_try_read_agent_rate(); @@ -262,7 +274,7 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { // this must be stable on re-evaluation bool sampling = (double)span->trace_id.low < sample_rate * (double)~0ULL; bool limited = false; - if (result.rule >= 0 && ddtrace_limiter_active() && sampling) { + if (result.mechanism != DD_MECHANISM_MANUAL && ddtrace_limiter_active() && sampling) { if (span->trace_is_limited == DD_TRACE_LIMIT_UNCHECKED) { span->trace_is_limited = ddtrace_limiter_allow() ? DD_TRACE_UNLIMITED : DD_TRACE_LIMITED; } @@ -274,8 +286,7 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { zend_array *metrics = ddtrace_property_array(&span->property_metrics); if (explicit_rule) { - // manual.keep and manual.drop count as manual - mechanism = result.rule < 0 ? DD_MECHANISM_MANUAL : DD_MECHANISM_RULE; + mechanism = result.mechanism; priority = sampling && !limited ? PRIORITY_SAMPLING_USER_KEEP : PRIORITY_SAMPLING_USER_REJECT; if (mechanism == DD_MECHANISM_MANUAL) { diff --git a/ext/priority_sampling/priority_sampling.h b/ext/priority_sampling/priority_sampling.h index 7d162a025f..eb38913239 100644 --- a/ext/priority_sampling/priority_sampling.h +++ b/ext/priority_sampling/priority_sampling.h @@ -18,6 +18,8 @@ enum dd_sampling_mechanism { DD_MECHANISM_REMOTE_RATE = 2, DD_MECHANISM_RULE = 3, DD_MECHANISM_MANUAL = 4, + DD_MECHANISM_REMOTE_USER_RULE = 11, + DD_MECHANISM_REMOTE_DYNAMIC_RULE = 12, }; void ddtrace_set_priority_sampling_on_root(zend_long priority, enum dd_sampling_mechanism mechanism); diff --git a/ext/random.c b/ext/random.c index 4501433b61..9ddbdcd096 100644 --- a/ext/random.c +++ b/ext/random.c @@ -32,8 +32,8 @@ void ddtrace_seed_prng(void) { } // Allow for usage in phpunit testsuite -bool ddtrace_reseed_seed_change(zval *old_value, zval *new_value) { - UNUSED(old_value, new_value); +bool ddtrace_reseed_seed_change(zval *old_value, zval *new_value, zend_string *new_str) { + UNUSED(old_value, new_value, new_str); ddtrace_seed_prng_with_optional_seed(Z_LVAL_P(new_value)); return true; } diff --git a/ext/random.h b/ext/random.h index 3e924033e0..d14c971ded 100644 --- a/ext/random.h +++ b/ext/random.h @@ -11,7 +11,7 @@ #define DD_TRACE_MAX_ID_LEN 40 // uint64_t -> 2**128 = 20 chars max ID void ddtrace_seed_prng(void); -bool ddtrace_reseed_seed_change(zval *old_value, zval *new_value); +bool ddtrace_reseed_seed_change(zval *old_value, zval *new_value, zend_string *new_str); uint64_t ddtrace_generate_span_id(void); uint64_t ddtrace_peek_span_id(void); ddtrace_trace_id ddtrace_peek_trace_id(void); diff --git a/ext/remote_config.c b/ext/remote_config.c new file mode 100644 index 0000000000..867281f432 --- /dev/null +++ b/ext/remote_config.c @@ -0,0 +1,116 @@ +#include "remote_config.h" +#include "ddtrace.h" +#include "sidecar.h" +#include "hook/uhook.h" +#include "span.h" +#include +#include +#include +#include +#include "threads.h" +#include "live_debugger.h" +#ifndef _WIN32 +#include +#endif + +#if PHP_VERSION_ID < 70100 +#include +#define zend_interrupt_function zai_interrupt_function +#define zend_vm_interrupt zai_vm_interrupt +#endif + +ZEND_EXTERN_MODULE_GLOBALS(ddtrace); + +static void (*dd_prev_interrupt_function)(zend_execute_data *execute_data); +static void dd_vm_interrupt(zend_execute_data *execute_data) { + if (dd_prev_interrupt_function) { + dd_prev_interrupt_function(execute_data); + } + if (DDTRACE_G(remote_config_state) && DDTRACE_G(reread_remote_configuration)) { + LOG(INFO, "Rereading remote configurations after interrupt"); + DDTRACE_G(reread_remote_configuration) = 0; + ddog_process_remote_configs(DDTRACE_G(remote_config_state)); + } +} + +// We need this exported to call it via CreateRemoteThread on Windows +DDTRACE_PUBLIC void ddtrace_set_all_thread_vm_interrupt(void) { + // broadcast interrupt to all threads on ZTS +#if ZTS + tsrm_mutex_lock(ddtrace_threads_mutex); + + void *TSRMLS_CACHE; // EG() accesses a variable named TSRMLS_CACHE. Make use of variable shadowing in scopes... + ZEND_HASH_FOREACH_PTR(&ddtrace_tls_bases, TSRMLS_CACHE) { +#endif +#if PHP_VERSION_ID >= 80200 + zend_atomic_bool_store_ex(&EG(vm_interrupt), 1); +#elif PHP_VERSION_ID >= 70100 + EG(vm_interrupt) = 1; +#else + DDTRACE_G(zai_vm_interrupt) = 1; +#endif + DDTRACE_G(reread_remote_configuration) = 1; +#if ZTS + } ZEND_HASH_FOREACH_END(); + + tsrm_mutex_unlock(ddtrace_threads_mutex); +#endif +} + +#ifndef _WIN32 +static void dd_sigvtalarm_handler(int signal, siginfo_t *siginfo, void *ctx) { + UNUSED(signal, siginfo, ctx); + ddtrace_set_all_thread_vm_interrupt(); +} +#endif + +static struct ddog_Vec_CChar *dd_dynamic_instrumentation_update(ddog_CharSlice config, ddog_CharSlice value, bool return_old) { + zend_string *name = dd_CharSlice_to_zend_string(config); + zend_string *old; + struct ddog_Vec_CChar *ret = NULL; + if (return_old) { + old = zend_string_copy(zend_ini_get_value(name)); + } + if (zend_alter_ini_entry_chars(name, value.ptr, value.len, ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME) == SUCCESS) { + if (return_old) { + ret = ddog_CharSlice_to_owned(dd_zend_string_to_CharSlice(old)); + } + } + if (return_old) { + zend_string_release(old); + } + zend_string_release(name); + return ret; +} + +void ddtrace_minit_remote_config(void) { + ddog_setup_remote_config(dd_dynamic_instrumentation_update, &ddtrace_live_debugger_setup); + dd_prev_interrupt_function = zend_interrupt_function; + zend_interrupt_function = dd_vm_interrupt; + +#ifndef _WIN32 + struct sigaction act = {0}; + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = dd_sigvtalarm_handler; + sigaction(SIGVTALRM, &act, NULL); +#endif +} + +void ddtrace_mshutdown_remote_config(void) { +#ifndef _WIN32 + struct sigaction act = {0}; + act.sa_handler = SIG_IGN; + sigaction(SIGVTALRM, &act, NULL); +#endif +} + +void ddtrace_rinit_remote_config(void) { + zend_hash_init(&DDTRACE_G(active_rc_hooks), 8, NULL, NULL, 0); + DDTRACE_G(reread_remote_configuration) = 0; + ddog_rinit_remote_config(DDTRACE_G(remote_config_state)); +} + +void ddtrace_rshutdown_remote_config(void) { + ddog_rshutdown_remote_config(DDTRACE_G(remote_config_state)); + zend_hash_destroy(&DDTRACE_G(active_rc_hooks)); +} diff --git a/ext/remote_config.h b/ext/remote_config.h new file mode 100644 index 0000000000..cac1c89d2c --- /dev/null +++ b/ext/remote_config.h @@ -0,0 +1,13 @@ +#ifndef DD_REMOTE_CONFIG_H +#define DD_REMOTE_CONFIG_H + +#include "ddtrace_export.h" + +void ddtrace_minit_remote_config(void); +void ddtrace_mshutdown_remote_config(void); +void ddtrace_rinit_remote_config(void); +void ddtrace_rshutdown_remote_config(void); + +DDTRACE_PUBLIC void ddtrace_set_all_thread_vm_interrupt(void); + +#endif diff --git a/ext/serializer.c b/ext/serializer.c index 8e521512fb..aa82bd9d62 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "arrays.h" #include "compat_string.h" @@ -40,6 +41,9 @@ #include "user_request.h" #include "ddshared.h" #include "zend_hrtime.h" +#include "sidecar.h" +#include "live_debugger.h" +#include "exception_serialize.h" ZEND_EXTERN_MODULE_GLOBALS(ddtrace); @@ -251,146 +255,6 @@ static void _add_assoc_zval_copy(zval *el, const char *name, zval *prop) { add_assoc_zval(el, (name), &value); } -typedef zend_result (*add_tag_fn_t)(void *context, ddtrace_string key, ddtrace_string value); - -#if PHP_VERSION_ID < 70100 -#define ZEND_STR_LINE "line" -#define ZEND_STR_FILE "file" -#define ZEND_STR_PREVIOUS "previous" -#endif - -enum dd_exception { - DD_EXCEPTION_THROWN, - DD_EXCEPTION_CAUGHT, - DD_EXCEPTION_UNCAUGHT, -}; - -static zend_result dd_exception_to_error_msg(zend_object *exception, void *context, add_tag_fn_t add_tag, enum dd_exception exception_state) { - zend_string *msg = zai_exception_message(exception); - zend_long line = zval_get_long(ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_LINE)); - zend_string *file = ddtrace_convert_to_str(ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_FILE)); - - char *error_text, *status_line = NULL; - - if (SG(sapi_headers).http_response_code >= 500) { - if (SG(sapi_headers).http_status_line) { - UNUSED(asprintf(&status_line, " (%s)", SG(sapi_headers).http_status_line)); - } else { - UNUSED(asprintf(&status_line, " (%d)", SG(sapi_headers).http_response_code)); - } - } - - const char *exception_type; - switch (exception_state) { - case DD_EXCEPTION_CAUGHT: exception_type = "Caught"; break; - case DD_EXCEPTION_UNCAUGHT: exception_type = "Uncaught"; break; - default: exception_type = "Thrown"; break; - } - - int error_len = asprintf(&error_text, "%s %s%s%s%s in %s:" ZEND_LONG_FMT, exception_type, - ZSTR_VAL(exception->ce->name), status_line ? status_line : "", ZSTR_LEN(msg) > 0 ? ": " : "", - ZSTR_VAL(msg), ZSTR_VAL(file), line); - - free(status_line); - - ddtrace_string key = DDTRACE_STRING_LITERAL("error.message"); - ddtrace_string value = {error_text, error_len}; - zend_result result = add_tag(context, key, value); - - zend_string_release(file); - free(error_text); - return result; -} - -static zend_result dd_exception_to_error_type(zend_object *exception, void *context, add_tag_fn_t add_tag) { - ddtrace_string value, key = DDTRACE_STRING_LITERAL("error.type"); - - if (instanceof_function(exception->ce, ddtrace_ce_fatal_error)) { - zval *code = ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_CODE); - const char *error_type_string = "{unknown error}"; - - if (Z_TYPE_P(code) == IS_LONG) { - switch (Z_LVAL_P(code)) { - case E_ERROR: - error_type_string = "E_ERROR"; - break; - case E_CORE_ERROR: - error_type_string = "E_CORE_ERROR"; - break; - case E_COMPILE_ERROR: - error_type_string = "E_COMPILE_ERROR"; - break; - case E_USER_ERROR: - error_type_string = "E_USER_ERROR"; - break; - default: - LOG_UNREACHABLE( - "Unhandled error type in DDTrace\\FatalError; is a fatal error case missing?"); - } - - } else { - LOG_UNREACHABLE("Exception was a DDTrace\\FatalError but failed to get an exception code"); - } - - value = ddtrace_string_cstring_ctor((char *)error_type_string); - } else { - zend_string *type_name = exception->ce->name; - value.ptr = ZSTR_VAL(type_name); - value.len = ZSTR_LEN(type_name); - } - - return add_tag(context, key, value); -} - -static zend_result dd_exception_trace_to_error_stack(zend_string *trace, void *context, add_tag_fn_t add_tag) { - ddtrace_string key = DDTRACE_STRING_LITERAL("error.stack"); - ddtrace_string value = {ZSTR_VAL(trace), ZSTR_LEN(trace)}; - zend_result result = add_tag(context, key, value); - zend_string_release(trace); - return result; -} - -// Guarantees that add_tag will only be called once per tag, will stop trying to add tags if one fails. -static zend_result ddtrace_exception_to_meta(zend_object *exception, void *context, add_tag_fn_t add_meta, enum dd_exception exception_state) { - zend_object *exception_root = exception; - zend_string *full_trace = zai_get_trace_without_args_from_exception(exception); - - zval *previous = ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_PREVIOUS); - while (Z_TYPE_P(previous) == IS_OBJECT && !Z_IS_RECURSIVE_P(previous) && - instanceof_function(Z_OBJCE_P(previous), zend_ce_throwable)) { - zend_string *trace_string = zai_get_trace_without_args_from_exception(Z_OBJ_P(previous)); - - zend_string *msg = zai_exception_message(exception); - zend_long line = zval_get_long(ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_LINE)); - zend_string *file = ddtrace_convert_to_str(ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_FILE)); - - zend_string *complete_trace = - zend_strpprintf(0, "%s\n\nNext %s%s%s in %s:" ZEND_LONG_FMT "\nStack trace:\n%s", ZSTR_VAL(trace_string), - ZSTR_VAL(exception->ce->name), ZSTR_LEN(msg) ? ": " : "", ZSTR_VAL(msg), ZSTR_VAL(file), - line, ZSTR_VAL(full_trace)); - zend_string_release(trace_string); - zend_string_release(full_trace); - zend_string_release(file); - full_trace = complete_trace; - - Z_PROTECT_RECURSION_P(previous); - exception = Z_OBJ_P(previous); - previous = ZAI_EXCEPTION_PROPERTY(exception, ZEND_STR_PREVIOUS); - } - - previous = ZAI_EXCEPTION_PROPERTY(exception_root, ZEND_STR_PREVIOUS); - while (Z_TYPE_P(previous) == IS_OBJECT && !Z_IS_RECURSIVE_P(previous) && - instanceof_function(Z_OBJCE_P(previous), zend_ce_throwable)) { - Z_UNPROTECT_RECURSION_P(previous); - previous = ZAI_EXCEPTION_PROPERTY(Z_OBJ_P(previous), ZEND_STR_PREVIOUS); - } - - bool success = dd_exception_to_error_msg(exception, context, add_meta, exception_state) == SUCCESS && - dd_exception_to_error_type(exception, context, add_meta) == SUCCESS && - dd_exception_trace_to_error_stack(full_trace, context, add_meta) == SUCCESS; - return success ? SUCCESS : FAILURE; -} - typedef struct dd_error_info { zend_string *type; zend_string *msg; @@ -1243,7 +1107,7 @@ static void dd_set_entrypoint_root_span_props_end(zend_array *meta, int status, } } -static void _serialize_meta(zval *el, ddtrace_span_data *span) { +static void _serialize_meta(zval *el, ddtrace_span_data *span, zend_string *service_name) { bool is_root_span = span->std.ce == ddtrace_ce_root_span_data; zval meta_zv, *meta = &span->property_meta; bool ignore_error = false; @@ -1307,7 +1171,7 @@ static void _serialize_meta(zval *el, ddtrace_span_data *span) { if (is_root_span) { exception_type = Z_PROP_FLAG_P(exception_zv) == 2 ? DD_EXCEPTION_CAUGHT : DD_EXCEPTION_UNCAUGHT; } - ddtrace_exception_to_meta(Z_OBJ_P(exception_zv), meta, dd_add_meta_array, exception_type); + ddtrace_exception_to_meta(Z_OBJ_P(exception_zv), service_name, span->start, meta, dd_add_meta_array, exception_type); } zend_array *span_links = ddtrace_property_array(&span->property_links); @@ -1429,8 +1293,7 @@ static void _serialize_meta(zval *el, ddtrace_span_data *span) { if (!zend_string_equals_ci(Z_STR(prop_service_as_string), Z_STR(prop_root_service_as_string))) { add_assoc_str(meta, "_dd.base_service", Z_STR(prop_root_service_as_string)); - } - else { + } else { zend_string_release(Z_STR(prop_root_service_as_string)); } @@ -1828,8 +1691,7 @@ void ddtrace_serialize_span_to_array(ddtrace_span_data *span, zval *array) { zend_hash_str_del(meta, ZEND_STRL("operation.name")); } - _serialize_meta(el, span); - + _serialize_meta(el, span, Z_TYPE_P(prop_service) > IS_NULL ? Z_STR(prop_service_as_string) : ZSTR_EMPTY_ALLOC()); zval metrics_zv; array_init(&metrics_zv); diff --git a/ext/serializer.h b/ext/serializer.h index 4c2d2c2f93..3c5f3cb87b 100644 --- a/ext/serializer.h +++ b/ext/serializer.h @@ -1,6 +1,7 @@ #ifndef DD_SERIALIZER_H #define DD_SERIALIZER_H #include "span.h" +#include "ddtrace_string.h" int ddtrace_serialize_simple_array(zval *trace, zval *retval); int ddtrace_serialize_simple_array_into_c_string(zval *trace, char **data_p, size_t *size_p); @@ -21,4 +22,6 @@ void ddtrace_shutdown_span_sampling_limiter(void); void ddtrace_serializer_startup(void); +typedef zend_result (*add_tag_fn_t)(void *context, ddtrace_string key, ddtrace_string value); + #endif // DD_SERIALIZER_H diff --git a/ext/sidecar.c b/ext/sidecar.c index 0f9754c433..eed8124481 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -10,11 +10,13 @@ #include "sidecar.h" #include "telemetry.h" #include "serializer.h" +#include "remote_config.h" #ifndef _WIN32 #include "coms.h" #endif -ddog_SidecarTransport *ddtrace_sidecar; +ZEND_EXTERN_MODULE_GLOBALS(ddtrace); + ddog_Endpoint *ddtrace_endpoint; struct ddog_InstanceId *ddtrace_sidecar_instance_id; static uint8_t dd_sidecar_formatted_session_id[36]; @@ -34,9 +36,6 @@ static void ddtrace_set_resettable_sidecar_globals(void) { ddtrace_sidecar_instance_id = ddog_sidecar_instanceId_build(session_id, runtime_id); } -const enum ddog_RemoteConfigProduct remote_config_products[1] = { DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING }; -const enum ddog_RemoteConfigCapabilities remote_config_capabilities[1] = { DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_ENABLED }; - ddog_SidecarTransport *dd_sidecar_connection_factory(void) { // Should not happen if (!ddtrace_endpoint) { @@ -77,20 +76,18 @@ ddog_SidecarTransport *dd_sidecar_connection_factory(void) { DDOG_CHARSLICE_C("php"), DDOG_CHARSLICE_C(PHP_DDTRACE_VERSION), get_global_DD_TRACE_AGENT_FLUSH_INTERVAL(), + (int)(get_global_DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS() * 1000), // for historical reasons in seconds get_global_DD_TELEMETRY_HEARTBEAT_INTERVAL() * 1000, get_global_DD_TRACE_BUFFER_SIZE(), get_global_DD_TRACE_AGENT_STACK_BACKLOG() * get_global_DD_TRACE_AGENT_MAX_PAYLOAD_SIZE(), get_global_DD_TRACE_DEBUG() ? DDOG_CHARSLICE_C("debug") : dd_zend_string_to_CharSlice(get_global_DD_TRACE_LOG_LEVEL()), (ddog_CharSlice){ .ptr = logpath, .len = strlen(logpath) }, - - // Not used yet - NULL, - remote_config_products, - 0, - remote_config_capabilities, - 0 - ); + ddtrace_set_all_thread_vm_interrupt, + DDTRACE_REMOTE_CONFIG_PRODUCTS.ptr, + DDTRACE_REMOTE_CONFIG_PRODUCTS.len, + DDTRACE_REMOTE_CONFIG_CAPABILITIES.ptr, + DDTRACE_REMOTE_CONFIG_CAPABILITIES.len); ddog_endpoint_drop(dogstatsd_endpoint); @@ -129,6 +126,8 @@ void ddtrace_sidecar_setup(void) { maybe_enable_appsec(); + ddog_init_remote_config(get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()); + ddtrace_sidecar = dd_sidecar_connection_factory(); if (!ddtrace_sidecar && ddtrace_endpoint) { // Something went wrong ddog_endpoint_drop(ddtrace_endpoint); @@ -142,7 +141,7 @@ void ddtrace_sidecar_setup(void) { void ddtrace_sidecar_ensure_active(void) { if (ddtrace_sidecar) { - ddog_sidecar_reconnect(&ddtrace_sidecar, dd_sidecar_connection_factory); + ddtrace_sidecar_reconnect(&ddtrace_sidecar, dd_sidecar_connection_factory); } } @@ -305,8 +304,101 @@ void ddtrace_sidecar_dogstatsd_set(zend_string *metric, zend_long value, zval *t ddog_Vec_Tag_drop(vec); } -bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value) { - UNUSED(old_value); +void ddtrace_sidecar_submit_root_span_data_direct(ddtrace_root_span_data *root) { + if (!ddtrace_sidecar || !get_global_DD_REMOTE_CONFIG_ENABLED()) { + return; + } + + ddog_CharSlice service_slice = DDOG_CHARSLICE_C("unnamed-php-service"); + zend_string *free_string = NULL; + if (root) { + zval *service = &root->property_service; + if (Z_TYPE_P(service) == IS_STRING && Z_STRLEN_P(service) > 0) { + service_slice = dd_zend_string_to_CharSlice(Z_STR_P(service)); + } + } else if (ZSTR_LEN(get_DD_SERVICE())) { + service_slice = dd_zend_string_to_CharSlice(get_DD_SERVICE()); + } else { + free_string = ddtrace_default_service_name(); + service_slice = dd_zend_string_to_CharSlice(free_string); + } + + ddog_CharSlice env_slice = DDOG_CHARSLICE_C("none"); + if (root) { + zval *env = zend_hash_str_find(ddtrace_property_array(&root->property_meta), ZEND_STRL("env")); + if (!env) { + env = &root->property_env; + } + if (Z_TYPE_P(env) == IS_STRING && Z_STRLEN_P(env) > 0) { + env_slice = dd_zend_string_to_CharSlice(Z_STR_P(env)); + } + } else if (ZSTR_LEN(get_DD_ENV())) { + env_slice = dd_zend_string_to_CharSlice(get_DD_ENV()); + } + + ddog_CharSlice version_slice = DDOG_CHARSLICE_C(""); + if (root) { + zval *version = zend_hash_str_find(ddtrace_property_array(&root->property_meta), ZEND_STRL("version")); + if (!version) { + version = &root->property_version; + } + if (version && Z_TYPE_P(version) == IS_STRING && Z_STRLEN_P(version) > 0) { + version_slice = dd_zend_string_to_CharSlice(Z_STR_P(version)); + } + } else if (ZSTR_LEN(get_DD_VERSION())) { + version_slice = dd_zend_string_to_CharSlice(get_DD_VERSION()); + } + + bool changed = true; + if (DDTRACE_G(remote_config_state)) { + changed = ddog_remote_configs_service_env_change(DDTRACE_G(remote_config_state), service_slice, env_slice, version_slice); + } + if (changed || !root) { + ddog_sidecar_set_remote_config_data(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), service_slice, env_slice, version_slice, &DDTRACE_G(active_global_tags)); + } + + if (free_string) { + zend_string_release(free_string); + } +} + +void ddtrace_sidecar_submit_root_span_data(void) { + if (DDTRACE_G(active_stack)) { + ddtrace_root_span_data *root = DDTRACE_G(active_stack)->root_span; + if (root) { + ddtrace_sidecar_submit_root_span_data_direct(root); + } + } +} + +void ddtrace_sidecar_send_debugger_data(ddog_Vec_DebuggerPayload payloads) { + LOGEV(DEBUG, UNUSED(log); ddog_log_debugger_data(&payloads);); + ddog_sidecar_send_debugger_data(&ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), payloads); +} + +void ddtrace_sidecar_send_debugger_datum(ddog_DebuggerPayload *payload) { + LOGEV(DEBUG, UNUSED(log); ddog_log_debugger_datum(payload);); + ddog_sidecar_send_debugger_datum(&ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), payload); +} + +void ddtrace_sidecar_rinit(void) { + DDTRACE_G(sidecar_queue_id) = ddog_sidecar_queueId_generate(); + + DDTRACE_G(active_global_tags) = ddog_Vec_Tag_new(); + zend_string *tag; + zval *value; + ZEND_HASH_FOREACH_STR_KEY_VAL(get_DD_TAGS(), tag, value) { + UNUSED(ddog_Vec_Tag_push(&DDTRACE_G(active_global_tags), dd_zend_string_to_CharSlice(tag), dd_zend_string_to_CharSlice(Z_STR_P(value)))); + } ZEND_HASH_FOREACH_END(); + ddtrace_sidecar_submit_root_span_data_direct(NULL); +} + +void ddtrace_sidecar_rshutdown(void) { + ddog_Vec_Tag_drop(DDTRACE_G(active_global_tags)); +} + +bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value, zend_string *new_str) { + UNUSED(old_value, new_str); if (ddtrace_sidecar) { ddog_CharSlice session_id = (ddog_CharSlice) {.ptr = (char *) dd_sidecar_formatted_session_id, .len = sizeof(dd_sidecar_formatted_session_id)}; ddtrace_ffi_try("Failed updating test session token", @@ -317,3 +409,7 @@ bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value) { #endif return true; } + +bool ddtrace_exception_debugging_is_active(void) { + return ddtrace_sidecar && ddtrace_sidecar_instance_id && get_DD_EXCEPTION_REPLAY_ENABLED(); +} diff --git a/ext/sidecar.h b/ext/sidecar.h index 225bc123a9..57a22629aa 100644 --- a/ext/sidecar.h +++ b/ext/sidecar.h @@ -1,5 +1,8 @@ +#ifndef DD_SIDECAR_H +#define DD_SIDECAR_H #include #include +#include extern ddog_SidecarTransport *ddtrace_sidecar; extern ddog_Endpoint *ddtrace_endpoint; @@ -9,17 +12,24 @@ void ddtrace_sidecar_setup(void); void ddtrace_sidecar_ensure_active(void); void ddtrace_sidecar_shutdown(void); void ddtrace_reset_sidecar_globals(void); +void ddtrace_sidecar_submit_root_span_data(void); void ddtrace_sidecar_push_tag(ddog_Vec_Tag *vec, ddog_CharSlice key, ddog_CharSlice value); void ddtrace_sidecar_push_tags(ddog_Vec_Tag *vec, zval *tags); ddog_Endpoint *ddtrace_sidecar_agent_endpoint(void); +void ddtrace_sidecar_send_debugger_data(ddog_Vec_DebuggerPayload payloads); +void ddtrace_sidecar_send_debugger_datum(ddog_DebuggerPayload *payload); + +void ddtrace_sidecar_rinit(void); +void ddtrace_sidecar_rshutdown(void); + void ddtrace_sidecar_dogstatsd_count(zend_string *metric, zend_long value, zval *tags); void ddtrace_sidecar_dogstatsd_distribution(zend_string *metric, double value, zval *tags); void ddtrace_sidecar_dogstatsd_gauge(zend_string *metric, double value, zval *tags); void ddtrace_sidecar_dogstatsd_histogram(zend_string *metric, double value, zval *tags); void ddtrace_sidecar_dogstatsd_set(zend_string *metric, zend_long value, zval *tags); -bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value); +bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value, zend_string *new_str); static inline ddog_CharSlice dd_zend_string_to_CharSlice(zend_string *str) { return (ddog_CharSlice){ .len = str->len, .ptr = str->val }; @@ -29,8 +39,8 @@ static inline ddog_CharSlice dd_zai_string_to_CharSlice(zai_string str) { return (ddog_CharSlice){ .len = str.len, .ptr = str.ptr }; } -static inline zend_string *dd_CharSlice_to_zend_string(ddog_CharSlice str) { - return zend_string_init(str.ptr, str.len, 0); +static inline zend_string *dd_CharSlice_to_zend_string(ddog_CharSlice slice) { + return zend_string_init(slice.ptr, slice.len, 0); } static inline bool ddtrace_ffi_try(const char *msg, ddog_MaybeError maybe_error) { @@ -42,3 +52,7 @@ static inline bool ddtrace_ffi_try(const char *msg, ddog_MaybeError maybe_error) } return true; } + +bool ddtrace_exception_debugging_is_active(void); + +#endif // DD_SIDECAR_H diff --git a/ext/signals.c b/ext/signals.c index d5a280e47c..4c9e6512b7 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -19,6 +19,8 @@ #include "auto_flush.h" #include "ext/version.h" #include +#include "logging.h" +#undef ddtrace_bgs_logf #include #include @@ -47,7 +49,7 @@ ZEND_EXTERN_MODULE_GLOBALS(ddtrace); static void ddtrace_sigsegv_handler(int sig) { if (!DDTRACE_G(backtrace_handler_already_run)) { DDTRACE_G(backtrace_handler_already_run) = true; - LOG(ERROR, "Segmentation fault"); + ddtrace_bgs_logf("[crash] Segmentation fault encountered"); #if HAVE_SIGACTION bool health_metrics_enabled = get_DD_TRACE_HEALTH_METRICS_ENABLED(); @@ -58,27 +60,27 @@ static void ddtrace_sigsegv_handler(int sig) { dogstatsd_client_status status = dogstatsd_client_count(client, metric, "1", tags); if (status == DOGSTATSD_CLIENT_OK) { - LOG(ERROR, "sigsegv health metric sent"); + ddtrace_bgs_logf("[crash] sigsegv health metric sent"); } } #endif #if DDTRACE_HAVE_BACKTRACE - LOG(ERROR, "Datadog PHP Trace extension (DEBUG MODE)"); - LOG(ERROR, "Received Signal %d", sig); + ddtrace_bgs_logf("Datadog PHP Trace extension (DEBUG MODE)"); + ddtrace_bgs_logf("Received Signal %d", sig); void *array[MAX_STACK_SIZE]; backtrace_size_t size = backtrace(array, MAX_STACK_SIZE); if (size == MAX_STACK_SIZE) { - LOG(ERROR, "Note: max stacktrace size reached"); + ddtrace_bgs_logf("Note: max stacktrace size reached"); } - LOG(ERROR, "Note: Backtrace below might be incomplete and have wrong entries due to optimized runtime"); - LOG(ERROR, "Backtrace:"); + ddtrace_bgs_logf("Note: Backtrace below might be incomplete and have wrong entries due to optimized runtime"); + ddtrace_bgs_logf("Backtrace:"); char **backtraces = backtrace_symbols(array, size); if (backtraces) { for (backtrace_size_t i = 0; i < size; i++) { - LOG(ERROR, backtraces[i]); + ddog_log_callback((ddog_CharSlice){ .ptr = backtraces[i], .len = strlen(backtraces[i]) }); } free(backtraces); } diff --git a/ext/span.c b/ext/span.c index b3ffc4aec9..4af6dbf9a4 100644 --- a/ext/span.c +++ b/ext/span.c @@ -17,6 +17,7 @@ #include #include "user_request.h" #include "zend_types.h" +#include "sidecar.h" #define USE_REALTIME_CLOCK 0 #define USE_MONOTONIC_CLOCK 1 @@ -143,6 +144,12 @@ static ddtrace_span_data *ddtrace_init_span(enum ddtrace_span_dataype type, zend return span; } +uint64_t ddtrace_nanoseconds_realtime(void) { + struct timespec ts; + timespec_get(&ts, TIME_UTC); + return ts.tv_sec * ZEND_NANO_IN_SEC + ts.tv_nsec; +} + ddtrace_span_data *ddtrace_open_span(enum ddtrace_span_dataype type) { ddtrace_span_stack *stack = DDTRACE_G(active_stack); // The primary stack is ancestor to all stacks, which signifies that any root spans created on top of it will inherit the distributed tracing context @@ -167,9 +174,7 @@ ddtrace_span_data *ddtrace_open_span(enum ddtrace_span_dataype type) { span->duration_start = zend_hrtime(); // Start time is nanoseconds from unix epoch // @see https://docs.datadoghq.com/api/?lang=python#send-traces - struct timespec ts; - timespec_get(&ts, TIME_UTC); - span->start = ts.tv_sec * ZEND_NANO_IN_SEC + ts.tv_nsec; + span->start = ddtrace_nanoseconds_realtime(); span->span_id = ddtrace_generate_span_id(); @@ -212,6 +217,10 @@ ddtrace_span_data *ddtrace_open_span(enum ddtrace_span_dataype type) { if (root_span) { ddtrace_root_span_data *root = ROOTSPANDATA(&span->std); LOG(SPAN_TRACE, "Starting new root span: trace_id=%s, span_id=%" PRIu64 ", parent_id=%" PRIu64 ", SpanStack=%d, parent_SpanStack=%d", Z_STRVAL(root->property_trace_id), span->span_id, root->parent_id, root->stack->std.handle, root->stack->parent_stack->std.handle); + + if (ddtrace_span_is_entrypoint_root(span)) { + ddtrace_sidecar_submit_root_span_data(); + } } else { LOG(SPAN_TRACE, "Starting new span: trace_id=%s, span_id=%" PRIu64 ", parent_id=%" PRIu64 ", SpanStack=%d", Z_STRVAL(span->root->property_trace_id), span->span_id, SPANDATA(span->parent)->span_id, span->stack->std.handle); } @@ -377,7 +386,9 @@ DDTRACE_PUBLIC zend_object *ddtrace_get_root_span() return &rsd->std; } -bool ddtrace_span_alter_root_span_config(zval *old_value, zval *new_value) { +bool ddtrace_span_alter_root_span_config(zval *old_value, zval *new_value, zend_string *new_str) { + UNUSED(new_str); + if (Z_TYPE_P(old_value) == Z_TYPE_P(new_value) || !DDTRACE_G(active_stack)) { return true; } @@ -713,6 +724,8 @@ void ddtrace_drop_span(ddtrace_span_data *span) { void ddtrace_serialize_closed_spans(zval *serialized) { if (DDTRACE_G(top_closed_stack)) { + memset(&DDTRACE_G(exception_debugger_buffer), 0, sizeof(DDTRACE_G(exception_debugger_buffer))); + ddtrace_span_stack *rootstack = DDTRACE_G(top_closed_stack); DDTRACE_G(top_closed_stack) = NULL; do { @@ -745,6 +758,14 @@ void ddtrace_serialize_closed_spans(zval *serialized) { } } while (stack); } while (rootstack); + + if (ddtrace_exception_debugging_is_active()) { + ddtrace_sidecar_send_debugger_data(DDTRACE_G(exception_debugger_buffer)); + if (DDTRACE_G(debugger_capture_arena)) { + zend_arena_destroy(DDTRACE_G(debugger_capture_arena)); + DDTRACE_G(debugger_capture_arena) = NULL; + } + } } // Reset closed span counter for limit-refresh, don't touch open spans diff --git a/ext/span.h b/ext/span.h index 58eb14d57e..c664ead825 100644 --- a/ext/span.h +++ b/ext/span.h @@ -9,6 +9,7 @@ #include "compatibility.h" #include "ddtrace.h" #include "ddtrace_export.h" +#include "priority_sampling/priority_sampling.h" #define DDTRACE_DROPPED_SPAN (-1ull) #define DDTRACE_SILENTLY_DROPPED_SPAN (-2ull) @@ -28,6 +29,7 @@ enum ddtrace_span_dataype { typedef struct { double sampling_rate; int rule; + enum dd_sampling_mechanism mechanism; } ddtrace_rule_result; enum ddtrace_trace_limited { @@ -222,6 +224,7 @@ void ddtrace_clear_execute_data_span(zend_ulong invocation, bool keep); // Note that this function is used externally by the appsec extension. DDTRACE_PUBLIC zend_object *ddtrace_get_root_span(void); +uint64_t ddtrace_nanoseconds_realtime(void); void dd_trace_stop_span_time(ddtrace_span_data *span); bool ddtrace_has_top_internal_span(ddtrace_span_data *end); void ddtrace_close_stack_userland_spans_until(ddtrace_span_data *until); @@ -239,7 +242,7 @@ zend_string *ddtrace_trace_id_as_string(ddtrace_trace_id id); zend_string *ddtrace_span_id_as_hex_string(uint64_t id); zend_string *ddtrace_trace_id_as_hex_string(ddtrace_trace_id id); -bool ddtrace_span_alter_root_span_config(zval *old_value, zval *new_value); +bool ddtrace_span_alter_root_span_config(zval *old_value, zval *new_value, zend_string *new_str); static inline bool ddtrace_span_is_dropped(ddtrace_span_data *span) { return span->duration == DDTRACE_DROPPED_SPAN || span->duration == DDTRACE_SILENTLY_DROPPED_SPAN; diff --git a/ext/telemetry.c b/ext/telemetry.c index a888170946..333b6a81fa 100644 --- a/ext/telemetry.c +++ b/ext/telemetry.c @@ -4,6 +4,7 @@ #include #include #include "telemetry.h" +#include "serializer.h" #include "sidecar.h" ZEND_EXTERN_MODULE_GLOBALS(ddtrace); @@ -23,7 +24,7 @@ static bool dd_check_for_composer_autoloader(zend_ulong invocation, zend_execute ddog_CharSlice composer_path = dd_zend_string_to_CharSlice(execute_data->func->op_array.filename); if (!ddtrace_sidecar // if sidecar connection was broken, let's skip immediately - || ddtrace_detect_composer_installed_json(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), composer_path)) { + || ddtrace_detect_composer_installed_json(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), composer_path)) { zai_hook_remove((zai_str)ZAI_STR_EMPTY, (zai_str)ZAI_STR_EMPTY, dd_composer_hook_id); } return true; @@ -143,34 +144,43 @@ void ddtrace_telemetry_finalize(void) { } ddtrace_ffi_try("Failed flushing telemetry buffer", - ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), buffer)); + ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), buffer)); + zend_string *free_string = NULL; ddog_CharSlice service_name = DDOG_CHARSLICE_C_BARE("unnamed-php-service"); if (DDTRACE_G(last_flushed_root_service_name)) { service_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_flushed_root_service_name)); + } else if (ZSTR_LEN(get_DD_SERVICE())) { + service_name = dd_zend_string_to_CharSlice(get_DD_SERVICE()); + } else { + free_string = ddtrace_default_service_name(); + service_name = dd_zend_string_to_CharSlice(free_string); } ddog_CharSlice env_name = DDOG_CHARSLICE_C_BARE("none"); if (DDTRACE_G(last_flushed_root_env_name)) { env_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_flushed_root_env_name)); + } else if (ZSTR_LEN(get_DD_ENV())) { + env_name = dd_zend_string_to_CharSlice(get_DD_ENV()); } ddog_CharSlice php_version = dd_zend_string_to_CharSlice(Z_STR_P(zend_get_constant_str(ZEND_STRL("PHP_VERSION")))); struct ddog_RuntimeMetadata *meta = ddog_sidecar_runtimeMeta_build(DDOG_CHARSLICE_C("php"), php_version, DDOG_CHARSLICE_C(PHP_DDTRACE_VERSION)); ddtrace_ffi_try("Failed flushing service data", - ddog_sidecar_telemetry_flushServiceData(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), meta, service_name, env_name)); + ddog_sidecar_telemetry_flushServiceData(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), meta, service_name, env_name)); - ddog_sidecar_runtimeMeta_drop(meta); + if (free_string) { + zend_string_release(free_string); + } - ddtrace_ffi_try("Failed signaling lifecycle end", - ddog_sidecar_lifecycle_end(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id))); + ddog_sidecar_runtimeMeta_drop(meta); } void ddtrace_telemetry_notify_integration(const char *name, size_t name_len) { if (ddtrace_sidecar && get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { ddog_CharSlice integration = (ddog_CharSlice) {.len = name_len, .ptr = name}; - ddog_sidecar_telemetry_addIntegration(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), integration, + ddog_sidecar_telemetry_addIntegration(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), integration, DDOG_CHARSLICE_C(""), true); } } diff --git a/ext/threads.c b/ext/threads.c new file mode 100644 index 0000000000..3478cda722 --- /dev/null +++ b/ext/threads.c @@ -0,0 +1,79 @@ +#include "threads.h" +#define zend_signal_globals_id zend_signal_globals_id_dummy +#define zend_signal_globals_offset zend_signal_globals_offset_dummy +#include +#undef zend_signal_globals_id +#undef zend_signal_globals_offset +#include "ddtrace.h" + +#if ZTS + +#ifdef ZEND_SIGNALS +#ifdef __APPLE__ +extern __attribute__((weak, weak_import)) int zend_signal_globals_id; +extern __attribute__((weak, weak_import)) size_t zend_signal_globals_offset; +#elif defined(_WIN32) +#error "Found zend_signals under windows!?" +#else +__attribute__((weak)) int zend_signal_globals_id; +__attribute__((weak)) size_t zend_signal_globals_offset; +#endif +#endif + +HashTable ddtrace_tls_bases; // map thread id to TSRMLS_CACHE +MUTEX_T ddtrace_threads_mutex = NULL; + +void ddtrace_thread_mshutdown() { + if (ddtrace_threads_mutex) { + tsrm_mutex_free(ddtrace_threads_mutex); + ddtrace_threads_mutex = NULL; + zend_hash_destroy(&ddtrace_tls_bases); + } +} + +void ddtrace_thread_ginit() { + if (!ddtrace_threads_mutex) { + ddtrace_threads_mutex = tsrm_mutex_alloc(); + zend_hash_init(&ddtrace_tls_bases, 8, NULL, NULL, 1); + } + +#ifdef ZEND_SIGNALS + // avoid deadlocks due to signal handlers accessing this + if (zend_signal_globals_id) { + HANDLE_BLOCK_INTERRUPTIONS(); + } +#endif + tsrm_mutex_lock(ddtrace_threads_mutex); + + zend_hash_index_add_new_ptr(&ddtrace_tls_bases, (zend_ulong)(uintptr_t)tsrm_thread_id(), TSRMLS_CACHE); + + tsrm_mutex_unlock(ddtrace_threads_mutex); +#ifdef ZEND_SIGNALS + if (zend_signal_globals_id) { + HANDLE_UNBLOCK_INTERRUPTIONS(); + } +#endif +} + +void ddtrace_thread_gshutdown() { + if (ddtrace_threads_mutex) { +#ifdef ZEND_SIGNALS + // avoid deadlocks due to signal handlers accessing this + if (zend_signal_globals_id) { + HANDLE_BLOCK_INTERRUPTIONS(); + } +#endif + tsrm_mutex_lock(ddtrace_threads_mutex); + + zend_hash_index_del(&ddtrace_tls_bases, (zend_ulong)(uintptr_t)tsrm_thread_id()); + + tsrm_mutex_unlock(ddtrace_threads_mutex); +#ifdef ZEND_SIGNALS + if (zend_signal_globals_id) { + HANDLE_UNBLOCK_INTERRUPTIONS(); + } +#endif + } +} + +#endif diff --git a/ext/threads.h b/ext/threads.h new file mode 100644 index 0000000000..9be9442447 --- /dev/null +++ b/ext/threads.h @@ -0,0 +1,16 @@ +#ifndef DD_THREADS_H +#define DD_THREADS_H + +#include +#include + +#if ZTS +extern HashTable ddtrace_tls_bases; +extern MUTEX_T ddtrace_threads_mutex; + +void ddtrace_thread_mshutdown(void); +void ddtrace_thread_ginit(void); +void ddtrace_thread_gshutdown(void); +#endif + +#endif // DD_THREADS_H diff --git a/libdatadog b/libdatadog index e899c91b52..b8a7d2d394 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit e899c91b52a15bf056e27ce164f37542b0e84b24 +Subproject commit b8a7d2d3942dc8830d279e7430e23820823737c2 diff --git a/tests/OpenTracing/InternalTelemetryTest.php b/tests/OpenTracing/InternalTelemetryTest.php index 436ea93ffe..9ff657148e 100644 --- a/tests/OpenTracing/InternalTelemetryTest.php +++ b/tests/OpenTracing/InternalTelemetryTest.php @@ -28,7 +28,7 @@ private function readTelemetryPayloads($response) } // Filter the payloads from the trace background sender - return array_values(array_filter($telemetryPayloads, function($p) { return ($p["application"]["service_name"] ?? "") != "background_sender-php-service"; })); + return array_values(array_filter($telemetryPayloads, function($p) { return ($p["application"]["service_name"] ?? "") == "service_name"; })); } public function testInternalMetricWithOpenTracing() diff --git a/tests/ext/appsec/sca_flag_is_sent_01.phpt b/tests/ext/appsec/sca_flag_is_sent_01.phpt index 14362cff42..10b3263b60 100644 --- a/tests/ext/appsec/sca_flag_is_sent_01.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_01.phpt @@ -5,6 +5,7 @@ This configuration is used by the backend to display/charge customers --SKIPIF-- diff --git a/tests/ext/appsec/sca_flag_is_sent_02.phpt b/tests/ext/appsec/sca_flag_is_sent_02.phpt index 03b8cc1c44..c2bdfeb089 100644 --- a/tests/ext/appsec/sca_flag_is_sent_02.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_02.phpt @@ -5,6 +5,7 @@ This configuration is used by the backend to display/charge customers --SKIPIF-- diff --git a/tests/ext/appsec/sca_flag_is_sent_03.phpt b/tests/ext/appsec/sca_flag_is_sent_03.phpt index 2b91959d75..0ab15c9b3c 100644 --- a/tests/ext/appsec/sca_flag_is_sent_03.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_03.phpt @@ -5,6 +5,7 @@ This configuration is used by the backend to display/charge customers --SKIPIF-- diff --git a/tests/ext/appsec/sca_flag_is_sent_04.phpt b/tests/ext/appsec/sca_flag_is_sent_04.phpt index f6259abe9a..f92835f286 100644 --- a/tests/ext/appsec/sca_flag_is_sent_04.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_04.phpt @@ -5,6 +5,7 @@ This configuration is used by the backend to display/charge customers --SKIPIF-- diff --git a/tests/ext/appsec/sca_flag_is_sent_05.phpt b/tests/ext/appsec/sca_flag_is_sent_05.phpt index cf39574103..54f9257b9f 100644 --- a/tests/ext/appsec/sca_flag_is_sent_05.phpt +++ b/tests/ext/appsec/sca_flag_is_sent_05.phpt @@ -5,6 +5,7 @@ This configuration is used by the backend to display/charge customers --SKIPIF-- diff --git a/tests/ext/background-sender/agent_headers.phpt b/tests/ext/background-sender/agent_headers.phpt index d52471ceeb..2d638aa364 100644 --- a/tests/ext/background-sender/agent_headers.phpt +++ b/tests/ext/background-sender/agent_headers.phpt @@ -1,6 +1,7 @@ --TEST-- HTTP headers are sent to the Agent from the background sender --SKIPIF-- + --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off diff --git a/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt b/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt index b7368b4b4d..dd92754546 100644 --- a/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt +++ b/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt @@ -11,19 +11,21 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_AUTO_FLUSH_ENABLED=1 +DD_REMOTE_CONFIG_ENABLED=0 --INI-- datadog.trace.agent_test_session_token=background-sender/agent_headers_unix_domain_socket --FILE-- replayRequest(); // clear + RequestReplayer::launchUnixProxy(str_replace("unix://", "", getenv("DD_TRACE_AGENT_URL"))); \DDTrace\start_span(); \DDTrace\close_span(); -$rr = new RequestReplayer(); - echo PHP_EOL; $headers = $rr->replayHeaders([ 'content-type', diff --git a/tests/ext/background-sender/agent_sampling_sidecar.phpt b/tests/ext/background-sender/agent_sampling_sidecar.phpt index e8c32b40bb..22671ff66d 100644 --- a/tests/ext/background-sender/agent_sampling_sidecar.phpt +++ b/tests/ext/background-sender/agent_sampling_sidecar.phpt @@ -28,7 +28,7 @@ function checkUpdated($marker) { do { foreach (glob("/dev/shm/*") as $f) { if (@filesize($f) < 5000) { - $file = file_get_contents($f); + $file = @file_get_contents($f); if (@strpos($file, $marker) !== false) { global $contents, $filename; $filename = $f; diff --git a/tests/ext/crashtracker_segfault.phpt b/tests/ext/crashtracker_segfault.phpt index 8a00d52b3f..5e4aae80d9 100644 --- a/tests/ext/crashtracker_segfault.phpt +++ b/tests/ext/crashtracker_segfault.phpt @@ -24,6 +24,8 @@ $rr->replayRequest(); // cleanup possible leftover usleep(100000); // Let time to the sidecar to open the crashtracker socket +posix_setrlimit(POSIX_RLIMIT_CORE, 0, 0); + $php = getenv('TEST_PHP_EXECUTABLE'); $args = getenv('TEST_PHP_ARGS')." ".getenv("TEST_PHP_EXTRA_ARGS"); $cmd = $php." ".$args." -r 'posix_kill(posix_getpid(), 11);'"; diff --git a/tests/ext/do_not_check_if_class_or_function_exists_by_default.phpt b/tests/ext/do_not_check_if_class_or_function_exists_by_default.phpt index 39f8d177af..f094385b51 100644 --- a/tests/ext/do_not_check_if_class_or_function_exists_by_default.phpt +++ b/tests/ext/do_not_check_if_class_or_function_exists_by_default.phpt @@ -33,7 +33,7 @@ echo "no exception thrown" . PHP_EOL; --EXPECT-- TRUE TRUE -TRUE +FALSE TRUE TRUE no exception thrown diff --git a/tests/ext/dogstatsd/metrics_over_uds.phpt b/tests/ext/dogstatsd/metrics_over_uds.phpt index 6fe66694e3..f68da3719c 100644 --- a/tests/ext/dogstatsd/metrics_over_uds.phpt +++ b/tests/ext/dogstatsd/metrics_over_uds.phpt @@ -14,46 +14,7 @@ DD_VERSION=1.12 --FILE-- socket = socket_create(AF_UNIX, SOCK_DGRAM, 0))) { - $errorcode = socket_last_error(); - $errormsg = socket_strerror($errorcode); - die("Couldn't create socket: [$errorcode] $errormsg\n"); - } - - if (!socket_bind($this->socket, $path)) { - $errorcode = socket_last_error(); - $errormsg = socket_strerror($errorcode); - die("Could not bind socket : [$errorcode] $errormsg\n"); - } - - // On the CI, when this test is ran using "pecl run-tests" with sudo - // the Unix socket is owned by root while the sidecar process is ran as another user - chmod($path, 0777); - } - - public function dump($expected, $iter = 5000) { - $lines = []; - for ($i = 0; $i < $iter; ++$i) { - usleep(100); - if (socket_recvfrom($this->socket, $buf, 2048, MSG_DONTWAIT, $remote_ip, $remote_port)) { - $lines[] = "$buf\n"; - if (count($lines) == $expected) { - sort($lines, SORT_STRING); - echo implode($lines); - return; - } - } - } - } - - public function close() { - socket_close($this->socket); - } -} +require __DIR__ . '/metrics_uds.inc'; $server = new UDSServer('/tmp/ddtrace-test-metrics_over_uds.socket'); diff --git a/tests/ext/dogstatsd/metrics_uds.inc b/tests/ext/dogstatsd/metrics_uds.inc new file mode 100644 index 0000000000..32050fdc35 --- /dev/null +++ b/tests/ext/dogstatsd/metrics_uds.inc @@ -0,0 +1,42 @@ +socket = socket_create(AF_UNIX, SOCK_DGRAM, 0))) { + $errorcode = socket_last_error(); + $errormsg = socket_strerror($errorcode); + die("Couldn't create socket: [$errorcode] $errormsg\n"); + } + + if (!socket_bind($this->socket, $path)) { + $errorcode = socket_last_error(); + $errormsg = socket_strerror($errorcode); + die("Could not bind socket : [$errorcode] $errormsg\n"); + } + + // On the CI, when this test is ran using "pecl run-tests" with sudo + // the Unix socket is owned by root while the sidecar process is ran as another user + chmod($path, 0777); + } + + public function dump($expected, $iter = 5000) { + $lines = []; + for ($i = 0; $i < $iter; ++$i) { + usleep(100); + if (socket_recvfrom($this->socket, $buf, 2048, MSG_DONTWAIT, $remote_ip, $remote_port)) { + $lines[] = "$buf\n"; + if (count($lines) == $expected) { + sort($lines, SORT_STRING); + echo implode($lines); + return; + } + } + } + } + + public function close() { + socket_close($this->socket); + } +} diff --git a/tests/ext/extension_no_static_tls.phpt b/tests/ext/extension_no_static_tls.phpt index 6ae89ca2fd..d331e5acae 100644 --- a/tests/ext/extension_no_static_tls.phpt +++ b/tests/ext/extension_no_static_tls.phpt @@ -26,7 +26,7 @@ if (!file_exists('/proc/self/maps')) { die('skip: no /proc/self/maps'); } // 5. Determine in which compilation unit(s) this function is defined $maps = file_get_contents('/proc/self/maps'); -if (preg_match('@(?<=\\s)\\S*/ddtrace\\.so$@m', $maps, $m) != 1) { +if (preg_match('@(?<=\\s)\\S*/ddtrace[^/]*\\.so$@m', $maps, $m) != 1) { die('cannot find loaded ddtrace.so'); } diff --git a/tests/ext/includes/request_replayer.inc b/tests/ext/includes/request_replayer.inc index 528660e9e2..fa6cf42d40 100644 --- a/tests/ext/includes/request_replayer.inc +++ b/tests/ext/includes/request_replayer.inc @@ -5,17 +5,17 @@ class RequestReplayer /** * @var string */ - private $endpoint; + public $endpoint; /** * @var int */ - private $flushInterval; + public $flushInterval; /** * @var int */ - private $maxIteration; + public $maxIteration; public function __construct() { diff --git a/tests/ext/live-debugger/debugger_log_probe.phpt b/tests/ext/live-debugger/debugger_log_probe.phpt new file mode 100644 index 0000000000..05385932bd --- /dev/null +++ b/tests/ext/live-debugger/debugger_log_probe.phpt @@ -0,0 +1,252 @@ +--TEST-- +Installing a live debugger log probe +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_DYNAMIC_INSTRUMENTATION_ENABLED=1 +DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=0.01 +--INI-- +datadog.trace.agent_test_session_token=live-debugger/log_probe +--FILE-- + ["typeName" => "Bar", "methodName" => "foo"], + "captureSnapshot" => true, + "segments" => [ + ["json" => ["filter" => [["ref" => "value"], ["not" => ["isDefined" => ["getmember" => [["ref" => "@it"], "foo"]]]]]]], + ["str" => "\n"], + ["json" => ["filter" => [["ref" => "value"], ["instanceof" => [["ref" => "@it"], "bool"]]]]], + ["str" => "\n"], + ["json" => ["filter" => [["ref" => "value"], ["instanceof" => [["ref" => "this"], "Bar"]]]]], + ["str" => "\n"], + ], + ]); + + \DDTrace\start_span(); // submit span data +}); + +var_dump((new Bar)->foo(10, "20", [true])); + +$dlr = new DebuggerLogReplayer; +$log = $dlr->waitForDebuggerDataAndReplay(); +$log = json_decode($log["body"], true)[0]; +foreach ($log["debugger"]["snapshot"]["captures"] as &$capture) { + ksort($capture["arguments"]); +} +var_dump($log); + +?> +--CLEAN-- + +--EXPECTF-- +int(30) +array(5) { + ["service"]=> + string(22) "debugger_log_probe.php" + ["ddsource"]=> + string(11) "dd_debugger" + ["timestamp"]=> + int(%d) + ["debugger"]=> + array(1) { + ["snapshot"]=> + array(5) { + ["language"]=> + string(3) "php" + ["id"]=> + string(%d) "%s" + ["timestamp"]=> + int(%d) + ["captures"]=> + array(2) { + ["entry"]=> + array(1) { + ["arguments"]=> + array(3) { + ["arg1"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "10" + } + ["arg2"]=> + array(2) { + ["type"]=> + string(6) "string" + ["value"]=> + string(2) "20" + } + ["this"]=> + array(2) { + ["type"]=> + string(3) "Bar" + ["fields"]=> + array(1) { + ["prop"]=> + array(2) { + ["type"]=> + string(5) "array" + ["elements"]=> + array(2) { + [0]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "1" + } + [1]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "2" + } + } + } + } + } + } + } + ["return"]=> + &array(2) { + ["arguments"]=> + array(4) { + ["@return"]=> + array(2) { + ["type"]=> + string(3) "Bar" + ["fields"]=> + array(1) { + ["prop"]=> + array(2) { + ["type"]=> + string(5) "array" + ["elements"]=> + array(2) { + [0]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "1" + } + [1]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "2" + } + } + } + } + } + ["arg1"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "10" + } + ["arg2"]=> + array(2) { + ["type"]=> + string(6) "string" + ["value"]=> + string(2) "20" + } + ["this"]=> + array(2) { + ["type"]=> + string(3) "Bar" + ["fields"]=> + array(1) { + ["prop"]=> + array(2) { + ["type"]=> + string(5) "array" + ["elements"]=> + array(2) { + [0]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "1" + } + [1]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "2" + } + } + } + } + } + } + ["locals"]=> + array(1) { + ["value"]=> + array(2) { + ["type"]=> + string(5) "array" + ["elements"]=> + array(1) { + [0]=> + array(2) { + ["type"]=> + string(4) "bool" + ["value"]=> + string(4) "true" + } + } + } + } + } + } + ["probe"]=> + array(2) { + ["id"]=> + string(1) "1" + ["location"]=> + array(2) { + ["method"]=> + string(3) "foo" + ["type"]=> + string(3) "Bar" + } + } + } + } + ["message"]=> + string(21) "[true] +[true] +[true] +" +} diff --git a/tests/ext/live-debugger/debugger_metric_probe.phpt b/tests/ext/live-debugger/debugger_metric_probe.phpt new file mode 100644 index 0000000000..af8039adec --- /dev/null +++ b/tests/ext/live-debugger/debugger_metric_probe.phpt @@ -0,0 +1,48 @@ +--TEST-- +Installing a live debugger metric probe +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_DOGSTATSD_URL=unix:///tmp/ddtrace-test-metric_probe.socket +DD_DYNAMIC_INSTRUMENTATION_ENABLED=1 +DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=0.01 +DD_VERSION=1.2.3 +--INI-- +datadog.trace.agent_test_session_token=live-debugger/metric_probe +--FILE-- + ["methodName" => "foo"], "metricName" => "foo", "kind" => "COUNT", "value" => ["json" => ["ref" => "@return"]]]); + return \DDTrace\start_span(); // submit span data +}); + +foo(); + +$server->dump(1); +$server->close(); + +?> +--CLEAN-- + +--EXPECT-- +dynamic.instrumentation.metric.probe.foo:123|c|#service:debugger_metric_probe.php,version:1.2.3,x-datadog-test-session-token:live-debugger/metric_probe diff --git a/tests/ext/live-debugger/debugger_span_decoration_probe.phpt b/tests/ext/live-debugger/debugger_span_decoration_probe.phpt new file mode 100644 index 0000000000..9320df9cb9 --- /dev/null +++ b/tests/ext/live-debugger/debugger_span_decoration_probe.phpt @@ -0,0 +1,146 @@ +--TEST-- +Installing a live debugger span decoration probe +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_DYNAMIC_INSTRUMENTATION_ENABLED=1 +DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=0.01 +--INI-- +datadog.trace.agent_test_session_token=live-debugger/span_decoration_probe +--FILE-- +meta; + unset($meta["runtime-id"]); + return $meta; +} + +function &root($arg) { + $meta = &\DDTrace\root_span()->meta; + unset($meta["runtime-id"]); + return $arg; +} + +await_probe_installation(function() { + build_span_decoration_probe(["where" => ["methodName" => "foo"], "inBodyLocation" => "START", "decorations" => [ + ["tags" => [["name" => "bare", "value" => ["segments" => [["str" => "raw"]]]]]], + ["tags" => [["name" => "arg", "value" => ["segments" => [["json" => ["ref" => "arg"]]]]]], "when" => ["json" => ["instanceof" => [["index" => [["ref" => "arg"], "foo"]], "object"]]]], + ]]); + build_span_decoration_probe(["where" => ["methodName" => "foo"], "decorations" => [ + ["tags" => [["name" => "error", "value" => ["segments" => [["json" => ["ref" => "arg"]]]]]], "when" => ["json" => ["eq" => [["index" => [["ref" => "arg"], "val"]], "test"]]]], + ["tags" => [["name" => "valid", "value" => ["segments" => [["json" => ["filter" => [["ref" => "arg"], ["ne" => [["ref" => "@it"], 1]]]]]]]]], "when" => ["json" => ["ne" => [["getmember" => [["index" => [["ref" => "arg"], "foo"]], "var"]], 1]]]], + ]]); + + build_span_decoration_probe(["where" => ["methodName" => "root"], "inBodyLocation" => "EXIT", "targetSpan" => "ROOT", "decorations" => [ + ["tags" => [["name" => "ret", "value" => ["segments" => [["str" => "return: "], ["json" => ["ref" => "@return"]]]]]]], + ]]); + + \DDTrace\start_span(); // submit span data +}, 3); + +\DDTrace\start_span(); +var_dump(foo(["foo" => (object)["var" => 1, "val" => "test"], "val" => 123])); +\DDTrace\active_span()->meta = []; +$meta = foo(["foo" => (object)["var" => 2]]); +ksort($meta); +var_dump($meta); + +root(1); +var_dump(\DDTrace\root_span()->meta); + +$dlr = new DebuggerLogReplayer; +$log = $dlr->waitForDebuggerDataAndReplay(); +var_dump($log["uri"]); +var_dump(json_decode($log["body"], true)); + +?> +--CLEAN-- + +--EXPECTF-- +array(4) { + ["bare"]=> + string(3) "raw" + ["_dd.di.bare.probe_id"]=> + string(1) "1" + ["arg"]=> + string(50) "[foo => (stdClass){val: test, var: 1}, val => 123]" + ["_dd.di.arg.probe_id"]=> + string(1) "1" +} +array(6) { + ["_dd.di.arg.probe_id"]=> + string(1) "1" + ["_dd.di.bare.probe_id"]=> + string(1) "1" + ["_dd.di.valid.probe_id"]=> + string(1) "2" + ["arg"]=> + string(27) "[foo => (stdClass){var: 2}]" + ["bare"]=> + string(3) "raw" + ["valid"]=> + string(20) "[(stdClass){var: 2}]" +} +array(2) { + ["ret"]=> + string(9) "return: 1" + ["_dd.di.ret.probe_id"]=> + string(1) "3" +} +string(%d) "/debugger/v1/input?ddtags=debugger_version:1.%s,env:none,version:,runtime_id:%s-%s-%s-%s-%s,host_name:%s" +array(1) { + [0]=> + array(5) { + ["service"]=> + string(34) "debugger_span_decoration_probe.php" + ["ddsource"]=> + string(11) "dd_debugger" + ["timestamp"]=> + int(1%d) + ["debugger"]=> + array(1) { + ["snapshot"]=> + array(5) { + ["language"]=> + string(3) "php" + ["id"]=> + string(36) "%s-%s-%s-%s-%s" + ["timestamp"]=> + int(1%d) + ["probe"]=> + array(2) { + ["id"]=> + string(1) "2" + ["location"]=> + array(1) { + ["method"]=> + string(3) "foo" + } + } + ["evaluationErrors"]=> + array(1) { + [0]=> + array(2) { + ["expr"]=> + string(20) "arg["val"] == "test"" + ["message"]=> + string(77) "Could not fetch index "val" on arg (evaluated to [foo => (stdClass){var: 2}])" + } + } + } + } + ["message"]=> + string(32) "Evaluation errors for probe id 2" + } +} diff --git a/tests/ext/live-debugger/debugger_span_probe.phpt b/tests/ext/live-debugger/debugger_span_probe.phpt new file mode 100644 index 0000000000..b38af41d65 --- /dev/null +++ b/tests/ext/live-debugger/debugger_span_probe.phpt @@ -0,0 +1,39 @@ +--TEST-- +Installing a live debugger span probe +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_DYNAMIC_INSTRUMENTATION_ENABLED=1 +DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=0.01 +--INI-- +datadog.trace.agent_test_session_token=live-debugger/span_probe +--FILE-- +name; +} + +await_probe_installation(function() { + build_span_probe(["where" => ["methodName" => "foo"]]); + + \DDTrace\start_span(); // submit span data +}); + +var_dump(foo()); + +?> +--CLEAN-- + +--EXPECT-- +string(15) "dd.dynamic.span" diff --git a/tests/ext/live-debugger/debugger_span_probe_class.phpt b/tests/ext/live-debugger/debugger_span_probe_class.phpt new file mode 100644 index 0000000000..04623d81af --- /dev/null +++ b/tests/ext/live-debugger/debugger_span_probe_class.phpt @@ -0,0 +1,85 @@ +--TEST-- +Installing a live debugger span probe on a class +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_DYNAMIC_INSTRUMENTATION_ENABLED=1 +DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=0.01 +--INI-- +datadog.trace.agent_test_session_token=live-debugger/span_probe_class +--FILE-- +name} {$span->resource}"; + } +} + +function delayed() { + class Delayed { + static function foo() { + $span = \DDTrace\active_span(); + return "{$span->name} {$span->resource}"; + } + } +} + +await_probe_installation(function() { + build_span_probe(["where" => ["typeName" => "Bar", "methodName" => "foo"]]); + build_span_probe(["where" => ["typeName" => "Delayed", "methodName" => "foo"]]); + + \DDTrace\start_span(); // submit span data +}, 2); + +var_dump(Bar::foo()); + +delayed(); +var_dump(Delayed::foo()); + +$dlr = new DebuggerLogReplayer; +$ordered = []; +$events = 0; +$time = time(); +do { + $log = $dlr->waitForDiagnosticsDataAndReplay(); + foreach (json_decode($log["files"]["event"]["contents"], true) as $payload) { + $diagnostic = $payload["debugger"]["diagnostics"]; + $ordered[$diagnostic["probeId"]][$payload["timestamp"]][] = $diagnostic["status"]; + ++$events; + } +} while ($events < 5 && $time > time() - 10); +ksort($ordered); +foreach ($ordered as &$value) { + ksort($value); + foreach ($value as &$v) { + uasort($v, function($a, $b) { $map = ["RECEIVED" => 0, "INSTALLED" => 1, "EMITTING" => 2]; return $map[$a] - $map[$b]; }); + } +} +var_dump($log["uri"]); +var_dump($log["files"]["event"]["name"]); +foreach ($ordered as $id => $statuses) { + print "$id: " . implode(", ", array_merge(...$statuses)) . "\n"; +} + +?> +--CLEAN-- + +--EXPECTF-- +string(23) "dd.dynamic.span Bar.foo" +string(27) "dd.dynamic.span Delayed.foo" +string(%d) "/debugger/v1/diagnostics?ddtags=debugger_version:%s,env:none,version:,runtime_id:%s,host_name:%s" +string(10) "event.json" +1: INSTALLED, EMITTING +2: RECEIVED, INSTALLED, EMITTING diff --git a/tests/ext/live-debugger/exception-replay_001.phpt b/tests/ext/live-debugger/exception-replay_001.phpt new file mode 100644 index 0000000000..bd89fe9fd1 --- /dev/null +++ b/tests/ext/live-debugger/exception-replay_001.phpt @@ -0,0 +1,234 @@ +--TEST-- +Test exception replay +--SKIPIF-- + + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_TRACE_AUTO_FLUSH_ENABLED=1 +DD_EXCEPTION_REPLAY_ENABLED=1 +DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 +--INI-- +datadog.trace.agent_test_session_token=live-debugger/exception-replay_001 +--FILE-- + STDIN]); +} catch (Exception $e) { + $span = \DDTrace\start_span(); + $span->exception = $e; + \DDTrace\close_span(); +} + +$dlr = new DebuggerLogReplayer; +$log = $dlr->waitForDebuggerDataAndReplay(); +$log = json_decode($log["body"], true); +foreach ($log[1]["debugger"]["snapshot"]["captures"] as &$capture) { + ksort($capture["locals"]); +} +var_dump($log); + +?> +--CLEAN-- + +--EXPECTF-- +array(2) { + [0]=> + array(5) { + ["service"]=> + string(24) "exception-replay_001.php" + ["ddsource"]=> + string(11) "dd_debugger" + ["timestamp"]=> + int(%d) + ["debugger"]=> + array(1) { + ["snapshot"]=> + array(8) { + ["language"]=> + string(3) "php" + ["id"]=> + string(36) "%s" + ["timestamp"]=> + int(%d) + ["exceptionCaptureId"]=> + string(36) "%s" + ["exceptionHash"]=> + string(%d) "%s" + ["frameIndex"]=> + int(0) + ["captures"]=> + array(1) { + ["return"]=> + array(2) { + ["arguments"]=> + array(1) { + ["foo"]=> + array(2) { + ["type"]=> + string(8) "stdClass" + ["fields"]=> + array(1) { + ["val"]=> + array(2) { + ["type"]=> + string(6) "stream" + ["value"]=> + string(1) "1" + } + } + } + } + ["locals"]=> + array(1) { + ["localVar"]=> + array(2) { + ["type"]=> + string(5) "array" + ["elements"]=> + array(1) { + [0]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "1" + } + } + } + } + } + } + ["probe"]=> + array(2) { + ["id"]=> + string(0) "" + ["location"]=> + array(1) { + ["method"]=> + string(3) "foo" + } + } + } + } + ["message"]=> + NULL + } + [1]=> + array(5) { + ["service"]=> + string(24) "exception-replay_001.php" + ["ddsource"]=> + string(11) "dd_debugger" + ["timestamp"]=> + int(%d) + ["debugger"]=> + array(1) { + ["snapshot"]=> + array(8) { + ["language"]=> + string(3) "php" + ["id"]=> + string(36) "%s" + ["timestamp"]=> + int(%d) + ["exceptionCaptureId"]=> + string(36) "%s" + ["exceptionHash"]=> + string(16) "0547bb1d4e434257" + ["frameIndex"]=> + int(1) + ["captures"]=> + array(1) { + ["return"]=> + &array(1) { + ["locals"]=> + array(8) { + ["_COOKIE"]=> + array(1) { + ["type"]=> + string(5) "array" + } + ["_FILES"]=> + array(1) { + ["type"]=> + string(5) "array" + } + ["_GET"]=> + array(1) { + ["type"]=> + string(5) "array" + } + ["_POST"]=> + array(1) { + ["type"]=> + string(5) "array" + } + ["_SERVER"]=> + array(%d) { + ["type"]=> + string(5) "array" + ["entries"]=> + array(%d) { + %A + } + ["argc"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "1" + } + ["argv"]=> + array(2) { + ["type"]=> + string(5) "array" + ["elements"]=> + array(1) { + [0]=> + array(2) { + ["type"]=> + string(6) "string" + ["value"]=> + string(%d) "%sexception-replay_001.php" + } + } + } + ["globalvar"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "1" + } + } + } + } + ["probe"]=> + array(2) { + ["id"]=> + string(0) "" + ["location"]=> + array(0) { + } + } + } + } + ["message"]=> + NULL + } +} \ No newline at end of file diff --git a/tests/ext/live-debugger/exception-replay_002.phpt b/tests/ext/live-debugger/exception-replay_002.phpt new file mode 100644 index 0000000000..2b794b1e6e --- /dev/null +++ b/tests/ext/live-debugger/exception-replay_002.phpt @@ -0,0 +1,1033 @@ +--TEST-- +Test exception replay capture limits +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_TRACE_AUTO_FLUSH_ENABLED=1 +DD_EXCEPTION_REPLAY_ENABLED=1 +DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 +--INI-- +datadog.trace.agent_test_session_token=live-debugger/exception-replay_002 +--FILE-- +obj = $obj; + (new MyClass)->foo($obj); +} catch (Exception $e) { + $span = \DDTrace\start_span(); + $span->exception = $e; + \DDTrace\close_span(); +} + +$dlr = new DebuggerLogReplayer; +$log = $dlr->waitForDebuggerDataAndReplay(); +$log = json_decode($log["body"], true); + +function recursive_ksort(&$arr) { + if (is_array($arr)) { + ksort($arr); + array_walk($arr, 'recursive_ksort'); + } +} + +recursive_ksort($log[0]["debugger"]["snapshot"]["captures"]); +var_dump($log[0]); + +?> +--CLEAN-- + +--EXPECTF-- +array(5) { + ["service"]=> + string(24) "exception-replay_002.php" + ["ddsource"]=> + string(11) "dd_debugger" + ["timestamp"]=> + int(%d) + ["debugger"]=> + array(1) { + ["snapshot"]=> + array(8) { + ["language"]=> + string(3) "php" + ["id"]=> + string(36) "%s" + ["timestamp"]=> + int(%d) + ["exceptionCaptureId"]=> + string(36) "%s" + ["exceptionHash"]=> + string(%d) "%s" + ["frameIndex"]=> + int(0) + ["captures"]=> + array(1) { + ["return"]=> + array(2) { + ["arguments"]=> + array(2) { + ["foo"]=> + array(2) { + ["fields"]=> + array(1) { + ["obj"]=> + array(2) { + ["fields"]=> + array(1) { + ["obj"]=> + array(2) { + ["fields"]=> + array(1) { + ["obj"]=> + array(2) { + ["notCapturedReason"]=> + string(5) "depth" + ["type"]=> + string(8) "stdClass" + } + } + ["type"]=> + string(8) "stdClass" + } + } + ["type"]=> + string(8) "stdClass" + } + } + ["type"]=> + string(8) "stdClass" + } + ["this"]=> + array(3) { + ["fields"]=> + array(20) { + ["field10"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "10" + } + ["field11"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "11" + } + ["field12"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "12" + } + ["field13"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "13" + } + ["field14"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "14" + } + ["field15"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "15" + } + ["field16"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "16" + } + ["field17"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "17" + } + ["field18"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "18" + } + ["field19"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "19" + } + ["field2"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "2" + } + ["field20"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "20" + } + ["field21"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "21" + } + ["field3"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "3" + } + ["field4"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "4" + } + ["field5"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "5" + } + ["field6"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "6" + } + ["field7"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "7" + } + ["field8"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "8" + } + ["field9"]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "9" + } + } + ["notCapturedReason"]=> + string(10) "fieldCount" + ["type"]=> + string(7) "MyClass" + } + } + ["locals"]=> + array(2) { + ["localArray"]=> + array(3) { + ["elements"]=> + array(100) { + [0]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "0" + } + [1]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "1" + } + [2]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "2" + } + [3]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "3" + } + [4]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "4" + } + [5]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "5" + } + [6]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "6" + } + [7]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "7" + } + [8]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "8" + } + [9]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(1) "9" + } + [10]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "10" + } + [11]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "11" + } + [12]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "12" + } + [13]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "13" + } + [14]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "14" + } + [15]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "15" + } + [16]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "16" + } + [17]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "17" + } + [18]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "18" + } + [19]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "19" + } + [20]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "20" + } + [21]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "21" + } + [22]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "22" + } + [23]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "23" + } + [24]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "24" + } + [25]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "25" + } + [26]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "26" + } + [27]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "27" + } + [28]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "28" + } + [29]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "29" + } + [30]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "30" + } + [31]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "31" + } + [32]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "32" + } + [33]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "33" + } + [34]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "34" + } + [35]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "35" + } + [36]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "36" + } + [37]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "37" + } + [38]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "38" + } + [39]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "39" + } + [40]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "40" + } + [41]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "41" + } + [42]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "42" + } + [43]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "43" + } + [44]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "44" + } + [45]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "45" + } + [46]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "46" + } + [47]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "47" + } + [48]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "48" + } + [49]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "49" + } + [50]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "50" + } + [51]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "51" + } + [52]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "52" + } + [53]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "53" + } + [54]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "54" + } + [55]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "55" + } + [56]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "56" + } + [57]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "57" + } + [58]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "58" + } + [59]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "59" + } + [60]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "60" + } + [61]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "61" + } + [62]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "62" + } + [63]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "63" + } + [64]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "64" + } + [65]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "65" + } + [66]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "66" + } + [67]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "67" + } + [68]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "68" + } + [69]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "69" + } + [70]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "70" + } + [71]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "71" + } + [72]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "72" + } + [73]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "73" + } + [74]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "74" + } + [75]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "75" + } + [76]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "76" + } + [77]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "77" + } + [78]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "78" + } + [79]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "79" + } + [80]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "80" + } + [81]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "81" + } + [82]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "82" + } + [83]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "83" + } + [84]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "84" + } + [85]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "85" + } + [86]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "86" + } + [87]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "87" + } + [88]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "88" + } + [89]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "89" + } + [90]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "90" + } + [91]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "91" + } + [92]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "92" + } + [93]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "93" + } + [94]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "94" + } + [95]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "95" + } + [96]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "96" + } + [97]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "97" + } + [98]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "98" + } + [99]=> + array(2) { + ["type"]=> + string(3) "int" + ["value"]=> + string(2) "99" + } + } + ["notCapturedReason"]=> + string(14) "collectionSize" + ["type"]=> + string(5) "array" + } + ["localStr"]=> + array(4) { + ["size"]=> + string(3) "293" + ["truncated"]=> + bool(true) + ["type"]=> + string(6) "string" + ["value"]=> + string(255) "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 8" + } + } + } + } + ["probe"]=> + array(2) { + ["id"]=> + string(0) "" + ["location"]=> + array(2) { + ["method"]=> + string(3) "foo" + ["type"]=> + string(7) "MyClass" + } + } + } + } + ["message"]=> + NULL +} diff --git a/tests/ext/live-debugger/live_debugger.inc b/tests/ext/live-debugger/live_debugger.inc new file mode 100644 index 0000000000..5478f16108 --- /dev/null +++ b/tests/ext/live-debugger/live_debugger.inc @@ -0,0 +1,143 @@ + (string)++$probeId, + "language" => "php", + "evaluateAt" => "EXIT", + ] + $data; + return put_live_debugger_file($json); +} + +function put_live_debugger_file($json) { + $data = json_encode($json); + $path = "datadog/2/LIVE_DEBUGGING/" . sha1($data) . "/config"; + put_rc_file($path, $data); + return $path; +} + +class DebuggerLogReplayer { + private $rr; + private $outstandingLogs = []; + private $outstandingDiagnostics = []; + + public function __construct() { + require_once __DIR__ . '/../includes/request_replayer.inc'; + $this->rr = new RequestReplayer; + } + + public function waitForDebuggerDataAndReplay() + { + $i = 0; + do { + if ($i++ == $this->rr->maxIteration) { + throw new Exception("wait for replay timeout"); + } + usleep($this->rr->flushInterval); + } while (empty($data = $this->replayDebuggerData())); + return $data; + } + + public function replayDebuggerData() + { + if ($this->outstandingLogs) { + return array_shift($this->outstandingLogs); + } + + // Request replayer now returns as many requests as were sent during a session. + // For the scope of the tests, we are returning the very first one. + $allLogs = json_decode(file_get_contents($this->rr->endpoint . '/replay', false, stream_context_create([ + "http" => [ + "header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ], + ])), true); + if ($allLogs) { + $allLogs = array_values(array_filter($allLogs, function ($v) { return strpos($v["uri"], '/debugger/v1/input') === 0; })); + } + if ($allLogs) { + $this->outstandingLogs = $allLogs; + return array_shift($this->outstandingLogs); + } + return []; + } + + public function waitForDiagnosticsDataAndReplay() + { + $i = 0; + do { + if ($i++ == $this->rr->maxIteration) { + throw new Exception("wait for replay timeout"); + } + usleep($this->rr->flushInterval); + } while (empty($data = $this->replayDiagnosticsData())); + return $data; + } + + public function replayDiagnosticsData() + { + if ($this->outstandingDiagnostics) { + return array_shift($this->outstandingDiagnostics); + } + + // Request replayer now returns as many requests as were sent during a session. + // For the scope of the tests, we are returning the very first one. + $allDiagnostics = json_decode(file_get_contents($this->rr->endpoint . '/replay', false, stream_context_create([ + "http" => [ + "header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ], + ])), true); + if ($allDiagnostics) { + $allDiagnostics = array_values(array_filter($allDiagnostics, function ($v) { return strpos($v["uri"], '/debugger/v1/diagnostics') === 0; })); + } + if ($allDiagnostics) { + $this->outstandingDiagnostics = $allDiagnostics; + return array_shift($this->outstandingDiagnostics); + } + return []; + } +} + +function await_probe_installation($trigger, $num = 1) { + ini_set("datadog.trace.hook_limit", "0"); + + $last_id = \DDtrace\install_hook("_dummy", function () {}); + $ret = $trigger(); + + for ($i = 0; $i < 6000; ++$i) { + usleep(1000); + + $id = \DDtrace\install_hook("_dummy", function () {}); + if ($last_id + 1 != $id) { + $num -= $id - $last_id - 1; + if ($num <= 0) { + return $ret; + } + } + $last_id = $id; + } + + return $ret; +} \ No newline at end of file diff --git a/tests/ext/remote_config/dynamic_config_auto_update.phpt b/tests/ext/remote_config/dynamic_config_auto_update.phpt new file mode 100644 index 0000000000..c63911af57 --- /dev/null +++ b/tests/ext/remote_config/dynamic_config_auto_update.phpt @@ -0,0 +1,44 @@ +--TEST-- +Test dynamic config update +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=0.01 +--INI-- +datadog.trace.agent_test_session_token=remote-config/dynamic_config_auto_update +--FILE-- + true, +]); + +usleep(500000); + +var_dump(ini_get("datadog.logs_injection")); + +del_rc_file($path); + +usleep(500000); + +var_dump(ini_get("datadog.logs_injection")); + +?> +--CLEAN-- + +--EXPECT-- +string(1) "1" +string(5) "false" diff --git a/tests/ext/remote_config/dynamic_config_update.phpt b/tests/ext/remote_config/dynamic_config_update.phpt new file mode 100644 index 0000000000..2f29a0a026 --- /dev/null +++ b/tests/ext/remote_config/dynamic_config_update.phpt @@ -0,0 +1,70 @@ +--TEST-- +Test dynamic config update +--SKIPIF-- + +--ENV-- +DD_AGENT_HOST=request-replayer +DD_TRACE_AGENT_PORT=80 +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=0.01 +--INI-- +datadog.trace.agent_test_session_token=remote-config/dynamic_config_update +--FILE-- + 0.5, + "tracing_header_tags" => [["header" => "foo", "tag_name" => "bar"], ["header" => "other", "tag_name" => "baz"]], + "log_injection_enabled" => true, + "tracing_tags" => ["foo:bar", "baz:qux"], + "tracing_enabled" => true, + "tracing_sampling_rules" => [ + [ + "service" => "foo", + "resource" => "bar", + "provenance" => "customer", + "sample_rate" => 0, + ], + [ + "service" => "f?o", + "resource" => "b*r", + "name" => "*", + "provenance" => "dynamic", + "sample_rate" => 0, + "tags" => [ + ["key" => "vuz", "value_glob" => "v?"], + ], + ], + ], +]); + +// submit span data +\DDTrace\start_span(); +usleep(500000); + +var_dump(ini_get("datadog.trace.sample_rate")); +$tags = explode(",", ini_get("datadog.trace.header_tags")); +sort($tags); // rust has no stable sorting in hashmaps, but that's fine +var_dump(implode(",", $tags)); +var_dump(ini_get("datadog.logs_injection")); +var_dump(ini_get("datadog.tags")); +var_dump(ini_get("datadog.trace.enabled")); +var_dump(ini_get("datadog.trace.sampling_rules")); + +?> +--CLEAN-- + +--EXPECT-- +string(3) "0.5" +string(9) "foo,other" +string(1) "1" +string(15) "foo:bar,baz:qux" +string(1) "1" +string(187) "[{"service":"foo","resource":"bar","_provenance":"customer","sample_rate":0.0},{"name":"*","service":"f?o","resource":"b*r","tags":{"vuz":"v?"},"_provenance":"dynamic","sample_rate":0.0}]" diff --git a/tests/ext/remote_config/remote_config.inc b/tests/ext/remote_config/remote_config.inc new file mode 100644 index 0000000000..4aa1a326df --- /dev/null +++ b/tests/ext/remote_config/remote_config.inc @@ -0,0 +1,58 @@ + [ + "header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ], + ])); +} + +function put_rc_file($path, $contents, $service = null) { + $ctx = stream_context_create([ + 'http' => [ + 'method' => 'PUT', + "header" => [ + "Content-Type: application/json", + "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ], + 'content' => $contents + ] + ]); + file_get_contents(rc_base_url() . "/add-rc-config-file?path=" . rawurlencode($path) . "&service=" . rawurlencode($service ?? default_rc_service()), false, $ctx); +} + +function del_rc_file($path) { + file_get_contents(rc_base_url() . "/del-rc-config-file?path=" . rawurlencode($path), false, stream_context_create([ + "http" => [ + "header" => "X-Datadog-Test-Session-Token: " . ini_get("datadog.trace.agent_test_session_token"), + ], + ])); +} + +function default_rc_service() { + return (\DDTrace\root_span() ? \DDTrace\root_span()->service : ini_get("datadog.service")) ?: basename($_SERVER["argv"][0]); +} + +function put_dynamic_config_file($configs, $service = null, $env = null) { + $json = [ + "action" => "enable", + "service_target" => [ + "service" => $service ?? default_rc_service(), + "env" => $env ?? (ini_get("datadog.env") ?: "none"), + ], + "lib_config" => $configs, + ]; + $data = json_encode($json); + $path = "datadog/2/APM_TRACING/" . sha1($data) . "/config"; + put_rc_file($path, $data, $service); + return $path; +} diff --git a/tests/ext/sandbox-regression/do_not_check_if_class_or_function_exists_by_default.phpt b/tests/ext/sandbox-regression/do_not_check_if_class_or_function_exists_by_default.phpt index c4000aa5e9..946d50ce0f 100644 --- a/tests/ext/sandbox-regression/do_not_check_if_class_or_function_exists_by_default.phpt +++ b/tests/ext/sandbox-regression/do_not_check_if_class_or_function_exists_by_default.phpt @@ -32,7 +32,7 @@ echo "no exception thrown" . PHP_EOL; --EXPECT-- TRUE TRUE -TRUE +FALSE TRUE TRUE no exception thrown diff --git a/tests/ext/segfault_backtrace_disabled.phpt b/tests/ext/segfault_backtrace_disabled.phpt index 73c12cefab..86430817ea 100644 --- a/tests/ext/segfault_backtrace_disabled.phpt +++ b/tests/ext/segfault_backtrace_disabled.phpt @@ -10,6 +10,9 @@ if (file_exists("/etc/os-release") && preg_match("/alpine/i", file_get_contents( DD_LOG_BACKTRACE=0 --FILE-- diff --git a/tests/ext/telemetry/composer.phpt b/tests/ext/telemetry/composer.phpt index 92dae36f07..c7858cc569 100644 --- a/tests/ext/telemetry/composer.phpt +++ b/tests/ext/telemetry/composer.phpt @@ -3,6 +3,7 @@ Read telemetry via composer --SKIPIF-- diff --git a/tests/ext/telemetry/config.phpt b/tests/ext/telemetry/config.phpt index bbd7b1754f..0ab7e50b00 100644 --- a/tests/ext/telemetry/config.phpt +++ b/tests/ext/telemetry/config.phpt @@ -3,6 +3,7 @@ Report user config telemetry --SKIPIF-- diff --git a/tests/ext/telemetry/disabled.phpt b/tests/ext/telemetry/disabled.phpt index 57b9de851b..606c412925 100644 --- a/tests/ext/telemetry/disabled.phpt +++ b/tests/ext/telemetry/disabled.phpt @@ -3,6 +3,7 @@ Disabled telemetry test --SKIPIF-- --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/telemetry/integration.phpt b/tests/ext/telemetry/integration.phpt index d8001a3f8c..5c1672acd3 100644 --- a/tests/ext/telemetry/integration.phpt +++ b/tests/ext/telemetry/integration.phpt @@ -3,6 +3,7 @@ Signal integration telemetry --SKIPIF-- diff --git a/tests/ext/telemetry/metrics_logs_created.phpt b/tests/ext/telemetry/metrics_logs_created.phpt index 34f23f1107..c276408173 100644 --- a/tests/ext/telemetry/metrics_logs_created.phpt +++ b/tests/ext/telemetry/metrics_logs_created.phpt @@ -3,6 +3,7 @@ --SKIPIF-- diff --git a/tests/ext/telemetry/metrics_spans_created.phpt b/tests/ext/telemetry/metrics_spans_created.phpt index 5b637b6479..cbbe30e911 100644 --- a/tests/ext/telemetry/metrics_spans_created.phpt +++ b/tests/ext/telemetry/metrics_spans_created.phpt @@ -3,6 +3,7 @@ --SKIPIF-- diff --git a/tests/ext/telemetry/simple.phpt b/tests/ext/telemetry/simple.phpt index 2287c6e1e9..fbe1662849 100644 --- a/tests/ext/telemetry/simple.phpt +++ b/tests/ext/telemetry/simple.phpt @@ -3,6 +3,7 @@ Simple telemetry test --SKIPIF-- diff --git a/tests/ext/valgrind/suppressions.txt b/tests/ext/valgrind/suppressions.txt index c743db259d..3c4fade279 100644 --- a/tests/ext/valgrind/suppressions.txt +++ b/tests/ext/valgrind/suppressions.txt @@ -1,7 +1,9 @@ { - + Memcheck:Param sendmsg(msg.msg_control) + ... fun:sendmsg + ... fun:*send_with_fd* -} \ No newline at end of file +} diff --git a/zend_abstract_interface/config/config.c b/zend_abstract_interface/config/config.c index 196d577b34..6f57b0ed96 100644 --- a/zend_abstract_interface/config/config.c +++ b/zend_abstract_interface/config/config.c @@ -232,8 +232,9 @@ void zai_config_rinit(void) { void zai_config_rshutdown(void) { zai_config_runtime_config_dtor(); } -bool zai_config_system_ini_change(zval *old_value, zval *new_value) { +bool zai_config_system_ini_change(zval *old_value, zval *new_value, zend_string *new_str) { (void)old_value; (void)new_value; + (void)new_str; return false; } diff --git a/zend_abstract_interface/config/config_ini.c b/zend_abstract_interface/config/config_ini.c index e1fbeee065..7b2f48d750 100644 --- a/zend_abstract_interface/config/config_ini.c +++ b/zend_abstract_interface/config/config_ini.c @@ -216,7 +216,7 @@ static ZEND_INI_MH(ZaiConfigOnUpdateIni) { return SUCCESS; } - if (memoized->ini_change && !memoized->ini_change(zai_config_get_value(id), &new_zv)) { + if (memoized->ini_change && !memoized->ini_change(zai_config_get_value(id), &new_zv, new_value)) { zval_dtor(&new_zv); return FAILURE; } diff --git a/zend_abstract_interface/config/config_ini.h b/zend_abstract_interface/config/config_ini.h index dcea28a1af..fb253295a9 100644 --- a/zend_abstract_interface/config/config_ini.h +++ b/zend_abstract_interface/config/config_ini.h @@ -34,9 +34,9 @@ int16_t zai_config_initialize_ini_value(zend_ini_entry **entries, zai_str default_value, zai_config_id entry_id); -typedef bool (*zai_config_apply_ini_change)(zval *old_value, zval *new_value); +typedef bool (*zai_config_apply_ini_change)(zval *old_value, zval *new_value, zend_string *new_str); typedef bool (*zai_env_config_fallback)(zai_env_buffer buf, bool pre_rinit); -bool zai_config_system_ini_change(zval *old_value, zval *new_value); +bool zai_config_system_ini_change(zval *old_value, zval *new_value, zend_string *new_str); bool zai_config_is_modified(zai_config_id entry_id); diff --git a/zend_abstract_interface/exceptions/exceptions.c b/zend_abstract_interface/exceptions/exceptions.c index 166829e678..107ab0a317 100644 --- a/zend_abstract_interface/exceptions/exceptions.c +++ b/zend_abstract_interface/exceptions/exceptions.c @@ -18,6 +18,7 @@ #define ZEND_STR_TYPE "type" #define ZEND_STR_FUNCTION "function" #define ZEND_STR_TRACE "trace" +#define ZEND_STR_MESSAGE "message" #define zend_hash_find(ht, name) zend_hash_str_find(ht, ZEND_STRL(name)) #define ZSTR_KNOWN(id) id diff --git a/zend_abstract_interface/exceptions/exceptions.h b/zend_abstract_interface/exceptions/exceptions.h index 6122d214ed..aa06dde378 100644 --- a/zend_abstract_interface/exceptions/exceptions.h +++ b/zend_abstract_interface/exceptions/exceptions.h @@ -12,10 +12,7 @@ static inline zend_class_entry *zai_get_exception_base(zend_object *object) { return instanceof_function(object->ce, zend_ce_exception) ? zend_ce_exception : zend_ce_error; } -#if PHP_VERSION_ID < 70100 -#define ZEND_STR_MESSAGE "message" -#define ZEND_STR_CODE "code" -static inline zval *zai_exception_read_property(zend_object *object, const char *pn, size_t pnl) { +static inline zval *zai_exception_read_property_str(zend_object *object, const char *pn, size_t pnl) { zval zv; ZVAL_OBJ(&zv, object); @@ -28,8 +25,7 @@ static inline zval *zai_exception_read_property(zend_object *object, const char return property; } -#define ZAI_EXCEPTION_PROPERTY(object, id) zai_exception_read_property(object, ZEND_STRL(id)) -#else + static inline zval *zai_exception_read_property(zend_object *object, zend_string *name) { zval zv; @@ -43,12 +39,14 @@ static inline zval *zai_exception_read_property(zend_object *object, zend_string return property; } -#if PHP_VERSION_ID < 70200 + +#if PHP_VERSION_ID < 70100 +#define ZAI_EXCEPTION_PROPERTY(object, id) zai_exception_read_property_str(object, ZEND_STRL(id)) +#elif PHP_VERSION_ID < 70200 #define ZAI_EXCEPTION_PROPERTY(object, id) zai_exception_read_property(object, CG(known_strings)[id]) #else #define ZAI_EXCEPTION_PROPERTY(object, id) zai_exception_read_property(object, ZSTR_KNOWN(id)) #endif -#endif zend_string *zai_exception_message(zend_object *ex); // fallback string if message invalid zend_string *zai_get_trace_without_args(zend_array *trace); diff --git a/zend_abstract_interface/hook/hook.c b/zend_abstract_interface/hook/hook.c index ee9151a522..01f9e60aaa 100644 --- a/zend_abstract_interface/hook/hook.c +++ b/zend_abstract_interface/hook/hook.c @@ -274,7 +274,7 @@ static inline zend_function *zai_hook_lookup_function(zai_str scope, zai_str fun } function = zai_symbol_lookup_function(ZAI_SYMBOL_SCOPE_CLASS, *ce, &func); } else { - ce = NULL; + *ce = NULL; function = zai_symbol_lookup_function(ZAI_SYMBOL_SCOPE_GLOBAL, NULL, &func); } return function; @@ -524,6 +524,10 @@ static zend_long zai_hook_resolved_install(zai_hook_t *hook, zend_function *reso zai_hooks_entry *hooks = zai_hook_resolved_ensure_hooks_entry(resolved, ce); zend_long index = zai_hook_add_entry(hooks, hook); + if (hook->aux.resolved) { + hook->aux.resolved(hook->aux.data, index >= 0); + } + if (hook->is_abstract) { zai_hook_resolved_install_abstract_recursive(hook, (zend_ulong)index, resolved->common.scope); } else if (!ZEND_USER_CODE(resolved->type) && resolved->common.scope) { @@ -548,6 +552,13 @@ static zend_long zai_hook_request_install(zai_hook_t *hook) { hook->resolved_scope = ce; hook->is_abstract = (function->common.fn_flags & ZEND_ACC_ABSTRACT) != 0; return zai_hook_resolved_install(hook, function, ce); + } else if (ce) { // class exists, but function does not; report it as error + if (!hook->is_global) { + zend_string_release(hook->scope); + zend_string_release(hook->function); + } + efree(hook); + return -1; } HashTable *funcs; @@ -763,6 +774,10 @@ static inline void zai_hook_resolve(HashTable *base_ht, zend_class_entry *ce, ze } else if (!ZEND_USER_CODE(function->type) && function->common.scope) { zai_hook_resolved_install_inherited_internal_function_recursive(hook, (zend_ulong)index, function->common.scope, function->internal_function.handler); } + + if (hook->aux.resolved) { + hook->aux.resolved(hook->aux.data, true); + } } ZEND_HASH_FOREACH_END(); // we remove the whole zai_hooks_entry, excluding the individual zai_hook_t which we moved @@ -791,6 +806,10 @@ static inline void zai_hook_resolve(HashTable *base_ht, zend_class_entry *ce, ze } else if (!ZEND_USER_CODE(function->type) && function->common.scope) { zai_hook_resolved_install_inherited_internal_function_recursive(hook, (zend_ulong)index, function->common.scope, function->internal_function.handler); } + + if (hook->aux.resolved) { + hook->aux.resolved(hook->aux.data, true); + } } ZEND_HASH_FOREACH_END(); } } @@ -837,6 +856,17 @@ void zai_hook_resolve_class(zend_class_entry *ce, zend_string *lcname) { if (zend_hash_num_elements(method_table) == 0) { // note: no pDestructor handling needed: zai_hook_resolve empties the table for us zend_hash_del(&zai_hook_tls->request_classes, lcname); + } else { + // Notify about missing methods + zai_hooks_entry *hooks; + ZEND_HASH_FOREACH_PTR(method_table, hooks) { + zai_hook_t *hook; + ZEND_HASH_FOREACH_PTR(&hooks->hooks, hook) { + if (hook->aux.resolved) { + hook->aux.resolved(hook->aux.data, false); + } + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); } } @@ -1280,7 +1310,7 @@ static zend_string *zai_zend_string_init_lower(const char *ptr, size_t len, bool zend_long zai_hook_install_generator(zai_str scope, zai_str function, zai_hook_begin begin, zai_hook_generator_resume resumption, zai_hook_generator_yield yield, zai_hook_end end, zai_hook_aux aux, size_t dynamic) { - bool persistent = !PG(modules_activated); + bool persistent = !PG(modules_activated) && !PG(during_request_startup); zai_hook_t *hook = pemalloc(sizeof(*hook), persistent); *hook = (zai_hook_t){ @@ -1304,7 +1334,11 @@ zend_long zai_hook_install_generator(zai_str scope, zai_str function, zend_hash_next_index_insert_ptr(&zai_hook_static, hook); return hook->id = zai_hook_static.nNextFreeElement - 1; } else { - return hook->id = zai_hook_request_install(hook); + zend_long id = zai_hook_request_install(hook); + if (id >= 0) { + hook->id = id; + } + return id; } } diff --git a/zend_abstract_interface/hook/hook.h b/zend_abstract_interface/hook/hook.h index 445968c26e..c57a6402a4 100644 --- a/zend_abstract_interface/hook/hook.h +++ b/zend_abstract_interface/hook/hook.h @@ -28,10 +28,12 @@ typedef void (*zai_hook_generator_yield)(zend_ulong invocation, zend_execute_dat typedef struct { void *data; void (*dtor)(void *data); + void (*resolved)(void *data, bool found); } zai_hook_aux; /* }}} */ /* {{{ zai_hook_aux ZAI_HOOK_AUX(void *pointer, void (*destructor)(void *pointer)) */ -#define ZAI_HOOK_AUX(pointer, destructor) (zai_hook_aux){ .data = (pointer), .dtor = (destructor) } +#define ZAI_HOOK_AUX_RESOLVED(pointer, destructor, resolvedfn) (zai_hook_aux){ .data = (pointer), .dtor = (destructor), .resolved = (resolvedfn) } +#define ZAI_HOOK_AUX(pointer, destructor) ZAI_HOOK_AUX_RESOLVED(pointer, destructor, NULL) #define ZAI_HOOK_AUX_UNUSED ZAI_HOOK_AUX(NULL, NULL) /* }}} */ diff --git a/zend_abstract_interface/interceptor/php7/interceptor.c b/zend_abstract_interface/interceptor/php7/interceptor.c index 7ff2a9ba97..fe52e4a8f2 100644 --- a/zend_abstract_interface/interceptor/php7/interceptor.c +++ b/zend_abstract_interface/interceptor/php7/interceptor.c @@ -44,6 +44,10 @@ static const zend_internal_function zend_pass_function = { NULL, /* module */ {0} /* reserved */ }; + +void (*zai_interrupt_function)(zend_execute_data *execute_data) = NULL; +static bool _zai_default_vm_interrupt = false; +TSRM_TLS bool *zai_vm_interrupt = &_zai_default_vm_interrupt; #endif typedef struct { @@ -167,6 +171,11 @@ uint32_t zai_interceptor_find_temporary(zend_op_array *op_array) { static user_opcode_handler_t prev_ext_nop_handler; static inline int zai_interceptor_ext_nop_handler_no_prev(zend_execute_data *execute_data) { +#if PHP_VERSION_ID < 70100 + if (UNEXPECTED(*zai_vm_interrupt) && zai_interrupt_function) { + zai_interrupt_function(execute_data); + } +#endif zend_op_array *op_array = &execute_data->func->op_array; if (UNEXPECTED(zai_hook_installed_user(op_array))) { zai_interceptor_frame_memory frame_memory, *tmp; @@ -473,6 +482,11 @@ static int zai_interceptor_bailout_get_closure(zval *obj, zend_class_entry **ce_ static void (*prev_execute_internal)(zend_execute_data *execute_data, zval *return_value); static inline void zai_interceptor_execute_internal_impl(zend_execute_data *execute_data, zval *return_value, bool prev) { +#if PHP_VERSION_ID < 70100 + if (UNEXPECTED(*zai_vm_interrupt) && zai_interrupt_function) { + zai_interrupt_function(execute_data); + } +#endif zend_function *func = execute_data->func; if (UNEXPECTED(zai_hook_installed_internal(&func->internal_function))) { zai_interceptor_frame_memory frame_memory; diff --git a/zend_abstract_interface/interceptor/php7/interceptor.h b/zend_abstract_interface/interceptor/php7/interceptor.h index 679ff8070e..4e4ea72019 100644 --- a/zend_abstract_interface/interceptor/php7/interceptor.h +++ b/zend_abstract_interface/interceptor/php7/interceptor.h @@ -2,6 +2,7 @@ #define ZAI_INTERCEPTOR_H #include +#include void zai_interceptor_op_array_ctor(zend_op_array *op_array); void zai_interceptor_op_array_pass_two(zend_op_array *op_array); @@ -14,4 +15,9 @@ void zai_interceptor_shutdown(void); void zai_interceptor_terminate_all_pending_observers(void); +#if PHP_VERSION_ID < 70100 +extern void (*zai_interrupt_function)(zend_execute_data *execute_data); +extern TSRM_TLS bool *zai_vm_interrupt; +#endif + #endif // ZAI_INTERCEPTOR_H From 6eb87bcfafdf5ee1920fb01527d3f68188db3877 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Thu, 3 Oct 2024 10:10:15 +0200 Subject: [PATCH 093/103] Add SSI requirements.json file (#2813) * Add SSI requirements.json file * Update requirements.json to new format * Enable requirements_json_test CI job * OS support is managed at the package level --- .gitlab-ci.yml | 8 +++++ loader/packaging/allow_tests.json | 11 +++++++ loader/packaging/block_tests.json | 14 +++++++++ loader/packaging/requirements.json | 45 +++++++++++++++++++++++++++++ tooling/bin/generate-ssi-package.sh | 1 + 5 files changed, 79 insertions(+) create mode 100644 loader/packaging/allow_tests.json create mode 100644 loader/packaging/block_tests.json create mode 100644 loader/packaging/requirements.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d2ff93383b..fb8861f01a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,6 +13,7 @@ variables: value: "master" description: "Run a specific datadog-reliability-env branch downstream" SYSTEM_TESTS_LIBRARY: php + include: - remote: https://gitlab-templates.ddbuild.io/libdatadog/include/ci_authenticated_job.yml - remote: https://gitlab-templates.ddbuild.io/libdatadog/include/one-pipeline.yml @@ -118,3 +119,10 @@ onboarding_tests_k8s_injection: - WEBLOG_VARIANT: - dd-lib-php-init-test-83 - dd-lib-php-init-test-alpine + +requirements_json_test: + rules: + - when: on_success + variables: + REQUIREMENTS_BLOCK_JSON_PATH: "loader/packaging/block_tests.json" + REQUIREMENTS_ALLOW_JSON_PATH: "loader/packaging/allow_tests.json" diff --git a/loader/packaging/allow_tests.json b/loader/packaging/allow_tests.json new file mode 100644 index 0000000000..18374c58e8 --- /dev/null +++ b/loader/packaging/allow_tests.json @@ -0,0 +1,11 @@ +[ + {"filepath": "/usr/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/usr/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "musl"}}, + {"filepath": "/usr/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "arm64", "libc": "glibc:2.17"}}, + {"filepath": "/usr/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "arm64", "libc": "musl:1.5"}}, + {"filepath": "/elephp5ant/thing", "args": [], "envars": [], "host": {"os": "linux", "arch": "amd64", "libc": "musl"}}, + {"filepath": "/usr/bin/gluglu", "args": [], "envars": [], "host": {"os": "linux", "arch": "amd64", "libc": "glibc:3.42"}}, + {"filepath": "/opt/php/7/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/opt/php/7.4/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/usr/bin/php83", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}} +] diff --git a/loader/packaging/block_tests.json b/loader/packaging/block_tests.json new file mode 100644 index 0000000000..969fc51923 --- /dev/null +++ b/loader/packaging/block_tests.json @@ -0,0 +1,14 @@ +[ + {"filepath": "/usr/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "amd64", "libc": "glibc:2.15"}}, + {"filepath": "/usr/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "amd64", "libc": "gluglu:2.20"}}, + + {"filepath": "/opt/php5/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/opt/php5.3/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/opt/php/5/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/opt/php/5.4/bin/php", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + + {"filepath": "/usr/bin/php5", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/usr/bin/php53", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/usr/bin/php-54", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}}, + {"filepath": "/usr/bin/php.54", "args": [], "envars": [], "host": {"os": "linux", "arch": "x64", "libc": "glibc:2.17"}} +] diff --git a/loader/packaging/requirements.json b/loader/packaging/requirements.json new file mode 100644 index 0000000000..ad2ccdefb8 --- /dev/null +++ b/loader/packaging/requirements.json @@ -0,0 +1,45 @@ +{ + "version": 1, + "native_deps": { + "glibc": [ + { + "arch": "x64", + "supported": true, + "description": "From centOS 7", + "min": "2.17" + }, + { + "arch": "arm64", + "supported": true, + "description": "From centOS 7", + "min": "2.17" + } + ], + "musl": [ + { + "arch": "x64", + "supported": true + }, + { + "arch": "arm64", + "supported": true + } + ] + }, + "deny": [ + { + "id": "php5", + "description": "Do not inject if PHP 5", + "os": null, + "cmds": [ + "**/php/5*/**", + "**/php5*/**", + "**/php5*", + "**/php-5*", + "**/php.5*" + ], + "args": [], + "envars": null + } + ] +} diff --git a/tooling/bin/generate-ssi-package.sh b/tooling/bin/generate-ssi-package.sh index 3406e6596a..6d60d84f2c 100755 --- a/tooling/bin/generate-ssi-package.sh +++ b/tooling/bin/generate-ssi-package.sh @@ -62,6 +62,7 @@ for architecture in "${architectures[@]}"; do cp -r ./src ${trace}/ echo "$release_version_sanitized" > ${root}/version + ln ./loader/packaging/requirements.json ${root}/requirements.json ######################## # Final archives From ee973a5a7a42a357ba3548d2b6d0d74dc0116744 Mon Sep 17 00:00:00 2001 From: Julio Gonzalez <107922352+hoolioh@users.noreply.github.com> Date: Thu, 3 Oct 2024 22:06:55 +0200 Subject: [PATCH 094/103] Enable sidecar trace sender on PHP 8.3 (#2729) * Revert "Disable sidecar on 8.3 (#2700)" This reverts commit d943c7123370e51b6e74a7caa963fd6f7e0bc3b1. * Enable sidecar sender in all integration tests. * Modify command. * Force flush on ZendFramework tests. * Fix instrumentation telemetry test Signed-off-by: Bob Weinand * Fix pcntl web test * Move ddtrace_tls_bases cleanup to gshutdown Signed-off-by: Bob Weinand --------- Signed-off-by: Bob Weinand Co-authored-by: Pierre Bonet Co-authored-by: Bob Weinand --- .circleci/continue_config.yml | 2 +- Cargo.lock | 149 +++++++++++++++++- Makefile | 3 +- components-rs/common.h | 11 ++ components-rs/remote_config.rs | 2 + components-rs/sidecar.h | 11 ++ ext/coms.c | 5 + ext/configuration.h | 2 +- ext/ddtrace.c | 6 - ext/threads.c | 14 +- ext/threads.h | 1 - libdatadog | 2 +- tests/Common/TracerTestTrait.php | 2 +- .../ZendFramework/V1/CommonScenariosTest.php | 9 ++ .../background-sender/sidecar_fallback.phpt | 3 +- ...ecar_disabled_when_telemetry_disabled.phpt | 2 +- tests/ext/sidecar_enabled.phpt | 2 +- 17 files changed, 200 insertions(+), 26 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index b996e3a43e..7e1b77fcaa 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -4897,7 +4897,7 @@ workflows: run: | cd system-tests DD_API_KEY=$SYSTEM_TESTS_DD_API_KEY ./run.sh - + - system_tests: requires: [ 'package extension' ] name: "System tests Integration Tests" diff --git a/Cargo.lock b/Cargo.lock index 22ccbb5d89..432f48c53b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,6 +445,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -1216,6 +1222,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ct-logs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" +dependencies = [ + "sct 0.6.1", +] + [[package]] name = "current_platform" version = "0.2.0" @@ -1629,6 +1644,7 @@ dependencies = [ "futures", "httpmock", "hyper 0.14.28", + "hyper-proxy", "hyper-rustls 0.27.2", "log", "prost 0.11.9", @@ -2420,6 +2436,30 @@ dependencies = [ "num-traits", ] +[[package]] +name = "headers" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http 0.2.11", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http 0.2.11", +] + [[package]] name = "heck" version = "0.4.1" @@ -2629,6 +2669,42 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "hyper-proxy" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" +dependencies = [ + "bytes", + "futures", + "headers", + "http 0.2.11", + "hyper 0.14.28", + "hyper-rustls 0.22.1", + "rustls-native-certs 0.5.0", + "tokio", + "tokio-rustls 0.22.0", + "tower-service", + "webpki 0.21.4", +] + +[[package]] +name = "hyper-rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +dependencies = [ + "ct-logs", + "futures-util", + "hyper 0.14.28", + "log", + "rustls 0.19.1", + "rustls-native-certs 0.5.0", + "tokio", + "tokio-rustls 0.22.0", + "webpki 0.21.4", +] + [[package]] name = "hyper-rustls" version = "0.23.2" @@ -4347,6 +4423,19 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.1", + "log", + "ring 0.16.20", + "sct 0.6.1", + "webpki 0.21.4", +] + [[package]] name = "rustls" version = "0.20.9" @@ -4355,8 +4444,8 @@ checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring 0.16.20", - "sct", - "webpki", + "sct 0.7.1", + "webpki 0.22.4", ] [[package]] @@ -4388,6 +4477,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +dependencies = [ + "openssl-probe", + "rustls 0.19.1", + "schannel", + "security-framework", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -4497,6 +4598,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + [[package]] name = "sct" version = "0.7.1" @@ -4653,6 +4764,17 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -5233,6 +5355,17 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls 0.19.1", + "tokio", + "webpki 0.21.4", +] + [[package]] name = "tokio-rustls" version = "0.23.4" @@ -5241,7 +5374,7 @@ checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls 0.20.9", "tokio", - "webpki", + "webpki 0.22.4", ] [[package]] @@ -5741,6 +5874,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + [[package]] name = "webpki" version = "0.22.4" diff --git a/Makefile b/Makefile index 9f1e60e3f5..8d3614995b 100644 --- a/Makefile +++ b/Makefile @@ -180,6 +180,7 @@ test_c_mem: $(SO_FILE) $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.p test_c2php: $(SO_FILE) $(INIT_HOOK_TEST_FILES) $(BUILD_DIR)/run-tests.php ( \ set -xe; \ + export PATH="$(PROJECT_ROOT)/tests/ext/valgrind:$$PATH"; \ sed -i 's/stream_socket_accept($$listenSock, 5)/stream_socket_accept($$listenSock, 20)/' $(BUILD_DIR)/run-tests.php; \ export DD_TRACE_CLI_ENABLED=1; \ export USE_ZEND_ALLOC=0; \ @@ -1042,7 +1043,7 @@ define run_composer_with_retry endef define run_tests_without_coverage - $(TEST_EXTRA_ENV) $(ENV_OVERRIDE) php $(TEST_EXTRA_INI) -d datadog.instrumentation_telemetry_enabled=$(shell (test $(TELEMETRY_ENABLED) && echo 1) || (test $(PHP_MAJOR_MINOR) -ge 84 && echo 1) || echo 0) -d datadog.trace.sidecar_trace_sender=$(shell test $(PHP_MAJOR_MINOR) -ge 84 && echo 1 || echo 0) $(TRACER_SOURCES_INI) $(PHPUNIT) $(1) --filter=$(FILTER) + $(TEST_EXTRA_ENV) $(ENV_OVERRIDE) php $(TEST_EXTRA_INI) -d datadog.instrumentation_telemetry_enabled=$(shell (test $(TELEMETRY_ENABLED) && echo 1) || (test $(PHP_MAJOR_MINOR) -ge 83 && echo 1) || echo 0) -d datadog.trace.sidecar_trace_sender=$(shell test $(PHP_MAJOR_MINOR) -ge 83 && echo 1 || echo 0) $(TRACER_SOURCES_INI) $(PHPUNIT) $(1) --filter=$(FILTER) endef define run_tests_with_coverage diff --git a/components-rs/common.h b/components-rs/common.h index 7881c5feef..4d0bfaf234 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -359,6 +359,10 @@ typedef enum ddog_RemoteConfigCapabilities { typedef enum ddog_RemoteConfigProduct { DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_DATA, + DDOG_REMOTE_CONFIG_PRODUCT_ASM, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_DD, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_FEATURES, DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, } ddog_RemoteConfigProduct; @@ -821,6 +825,13 @@ typedef struct ddog_AgentRemoteConfigReader ddog_AgentRemoteConfigReader; typedef struct ddog_AgentRemoteConfigWriter_ShmHandle ddog_AgentRemoteConfigWriter_ShmHandle; +typedef struct ddog_Arc_Target ddog_Arc_Target; + +/** + * Fundamental configuration of the RC client, which always must be set. + */ +typedef struct ddog_ConfigInvariants ddog_ConfigInvariants; + typedef struct ddog_MappedMem_ShmHandle ddog_MappedMem_ShmHandle; /** diff --git a/components-rs/remote_config.rs b/components-rs/remote_config.rs index 68a648c133..6bddeb4681 100644 --- a/components-rs/remote_config.rs +++ b/components-rs/remote_config.rs @@ -209,6 +209,7 @@ pub extern "C" fn ddog_process_remote_configs(remote_config: &mut RemoteConfigSt remote_config.dynamic_config.active_config_path = Some(value.config_id); } }, + RemoteConfigData::Ignored(_) => {} }, RemoteConfigUpdate::Remove(path) => match path.product { RemoteConfigProduct::LiveDebugger => { @@ -221,6 +222,7 @@ pub extern "C" fn ddog_process_remote_configs(remote_config: &mut RemoteConfigSt remove_old_configs(remote_config); } }, + _ => {}, }, } } diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index 4d03e11a46..955cfeb8fe 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -71,6 +71,17 @@ struct ddog_RemoteConfigReader *ddog_remote_config_reader_for_endpoint(const ddo const enum ddog_RemoteConfigCapabilities *remote_config_capabilities, uintptr_t remote_config_capabilities_count); +/** + * # Safety + * Argument should point to a valid C string. + */ +struct ddog_RemoteConfigReader *ddog_remote_config_reader_for_path(const char *path); + +char *ddog_remote_config_path(const struct ddog_ConfigInvariants *id, + const struct ddog_Arc_Target *target); + +void ddog_remote_config_path_free(char *path); + bool ddog_remote_config_read(struct ddog_RemoteConfigReader *reader, ddog_CharSlice *data); void ddog_remote_config_reader_drop(struct ddog_RemoteConfigReader*); diff --git a/ext/coms.c b/ext/coms.c index d1c3fe4221..f5baf81077 100644 --- a/ext/coms.c +++ b/ext/coms.c @@ -1072,6 +1072,11 @@ static void *_dd_writer_loop(void *_) { ddtrace_curl_set_timeout(writer->curl); ddtrace_curl_set_connect_timeout(writer->curl); struct curl_slist *headers = curl_slist_append(NULL, "Content-Type: application/json"); + if (*ddtrace_coms_globals.test_session_token) { + char buffer[300]; + sprintf(buffer, "x-datadog-test-session-token: %s", ddtrace_coms_globals.test_session_token); + headers = curl_slist_append(headers, buffer); + } curl_easy_setopt(writer->curl, CURLOPT_HTTPHEADER, headers); ddtrace_curl_set_telemetry_url(writer->curl); curl_easy_perform(writer->curl); diff --git a/ext/configuration.h b/ext/configuration.h index 9639858923..ddc8516953 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -53,7 +53,7 @@ enum ddtrace_sampling_rules_format { #define DD_INTEGRATION_ANALYTICS_ENABLED_DEFAULT false #define DD_INTEGRATION_ANALYTICS_SAMPLE_RATE_DEFAULT 1 -#if PHP_VERSION_ID >= 80400 || defined(_WIN32) +#if PHP_VERSION_ID >= 80300 || defined(_WIN32) #define DD_SIDECAR_TRACE_SENDER_DEFAULT true #else #define DD_SIDECAR_TRACE_SENDER_DEFAULT false diff --git a/ext/ddtrace.c b/ext/ddtrace.c index 8a2cb02c63..8826533419 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -1399,9 +1399,6 @@ static PHP_MSHUTDOWN_FUNCTION(ddtrace) { if (ddtrace_disable == 1) { zai_config_mshutdown(); zai_json_shutdown_bindings(); -#if ZTS - ddtrace_thread_mshutdown(); -#endif return SUCCESS; } @@ -1437,9 +1434,6 @@ static PHP_MSHUTDOWN_FUNCTION(ddtrace) { ddtrace_user_req_shutdown(); ddtrace_sidecar_shutdown(); -#if ZTS - ddtrace_thread_mshutdown(); -#endif #if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 // See dd_register_span_data_ce for explanation diff --git a/ext/threads.c b/ext/threads.c index 3478cda722..d77e48b3da 100644 --- a/ext/threads.c +++ b/ext/threads.c @@ -23,14 +23,6 @@ __attribute__((weak)) size_t zend_signal_globals_offset; HashTable ddtrace_tls_bases; // map thread id to TSRMLS_CACHE MUTEX_T ddtrace_threads_mutex = NULL; -void ddtrace_thread_mshutdown() { - if (ddtrace_threads_mutex) { - tsrm_mutex_free(ddtrace_threads_mutex); - ddtrace_threads_mutex = NULL; - zend_hash_destroy(&ddtrace_tls_bases); - } -} - void ddtrace_thread_ginit() { if (!ddtrace_threads_mutex) { ddtrace_threads_mutex = tsrm_mutex_alloc(); @@ -73,6 +65,12 @@ void ddtrace_thread_gshutdown() { HANDLE_UNBLOCK_INTERRUPTIONS(); } #endif + + if (zend_hash_num_elements(&ddtrace_tls_bases) == 0) { + tsrm_mutex_free(ddtrace_threads_mutex); + ddtrace_threads_mutex = NULL; + zend_hash_destroy(&ddtrace_tls_bases); + } } } diff --git a/ext/threads.h b/ext/threads.h index 9be9442447..58a564ec66 100644 --- a/ext/threads.h +++ b/ext/threads.h @@ -8,7 +8,6 @@ extern HashTable ddtrace_tls_bases; extern MUTEX_T ddtrace_threads_mutex; -void ddtrace_thread_mshutdown(void); void ddtrace_thread_ginit(void); void ddtrace_thread_gshutdown(void); #endif diff --git a/libdatadog b/libdatadog index b8a7d2d394..67d4f7a19a 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit b8a7d2d3942dc8830d279e7430e23820823737c2 +Subproject commit 67d4f7a19afd7f7301423e400008d42cc9284c04 diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index a74e03f946..a78342c38c 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -507,7 +507,7 @@ public function retrieveDumpedTraceData(callable $until = null, $throw = false) function untilNumberOfTraces($number) { $count = 0; return function ($request) use (&$count, $number) { - $count += $request['headers']['X-Datadog-Trace-Count'] ?? 0; + $count += $request['headers']['X-Datadog-Trace-Count'] ?? $request["headers"]["x-datadog-trace-count"] ?? 0; return $count >= $number; }; } diff --git a/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php b/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php index 77a8660b67..3bbec5542a 100644 --- a/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php +++ b/tests/Integrations/ZendFramework/V1/CommonScenariosTest.php @@ -14,6 +14,13 @@ protected static function getAppIndexScript() return __DIR__ . '/../../../Frameworks/ZendFramework/Version_1_12/public/index.php'; } + protected static function getEnvs() + { + return array_merge(parent::getEnvs(), [ + 'DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS' => 1, + ]); + } + /** * @dataProvider provideSpecs * @param RequestSpec $spec @@ -22,6 +29,8 @@ protected static function getAppIndexScript() */ public function testScenario(RequestSpec $spec, array $spanExpectations) { + $this->resetRequestDumper(); + $traces = $this->tracesFromWebRequest(function () use ($spec) { $this->call($spec); }); diff --git a/tests/ext/background-sender/sidecar_fallback.phpt b/tests/ext/background-sender/sidecar_fallback.phpt index 045d1784a0..8b9783795b 100644 --- a/tests/ext/background-sender/sidecar_fallback.phpt +++ b/tests/ext/background-sender/sidecar_fallback.phpt @@ -2,7 +2,7 @@ Send telemetry about the sidecar being disabled --SKIPIF-- - + replayRequest(); /* avoid cross-pollination */ ?> @@ -11,6 +11,7 @@ DD_AGENT_HOST=request-replayer DD_TRACE_AGENT_PORT=80 DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_REMOTE_CONFIG_ENABLED=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_SERVICE=service --INI-- diff --git a/tests/ext/sidecar_disabled_when_telemetry_disabled.phpt b/tests/ext/sidecar_disabled_when_telemetry_disabled.phpt index b2cbd26a70..0f311a6408 100644 --- a/tests/ext/sidecar_disabled_when_telemetry_disabled.phpt +++ b/tests/ext/sidecar_disabled_when_telemetry_disabled.phpt @@ -6,7 +6,7 @@ Sidecar should be disabled when telemetry is disabled '1', From 77f1a7f77231cecee4cd01dc97905f0d46bb6c42 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 3 Oct 2024 23:31:32 +0200 Subject: [PATCH 095/103] Enable CLI by default (#2871) * Enable CLI by default And enable autoflushing by default in CLI environments. Signed-off-by: Bob Weinand * Do not trace composer by default Signed-off-by: Bob Weinand * Fix pcntl integration test flakiness --------- Signed-off-by: Bob Weinand --- .circleci/continue_config.yml | 5 +---- Makefile | 15 ++++++--------- appsec/cmake/run-tests-wrapper.sh | 1 - dockerfiles/frameworks/contrib/entrypoint.sh | 2 -- .../frameworks/contrib/phpredis/3.1.6/run.sh | 4 ++-- .../frameworks/contrib/phpredis/4.3.0/run.sh | 4 ++-- .../frameworks/contrib/phpredis/5.3.1/run.sh | 4 ++-- .../release-candidates/docker-compose.yml | 2 -- dockerfiles/verify_packages/verify.sh | 2 +- examples/long-running/Dockerfile | 4 +--- ext/configuration.c | 4 ++++ ext/configuration.h | 4 ++-- ext/ddtrace.c | 19 +++++++++++++++++-- .../test_ddtrace_is_fully_loaded.php | 4 ++-- tests/Common/CLITestCase.php | 2 +- tests/Common/TracerTestTrait.php | 1 - tests/Integrations/PCNTL/PCNTLTest.php | 12 ++++++++---- tests/bootstrap_phpunit.php | 2 ++ .../dd_init_open_basedir.phpt | 1 + .../error_get_last_is_unaffected.phpt | 1 + .../autoload-php-files/file_not_found.phpt | 1 + .../ignores_exceptions.phpt | 1 + .../ignores_fatal_errors.phpt | 1 + .../ext/background-sender/agent_headers.phpt | 2 -- .../agent_headers_container_id.phpt | 1 - .../agent_headers_container_id_empty.phpt | 1 - .../agent_headers_container_id_fargate.phpt | 1 - .../agent_headers_unix_domain_socket.phpt | 1 - .../ext/background-sender/agent_sampling.phpt | 1 - .../agent_sampling_sidecar.phpt | 1 - tests/ext/close_spans_until.phpt | 1 + .../ext/dd_trace_serialize_msgpack_error.phpt | 1 + .../distributed_trace_bogus_ids.phpt | 1 + .../distributed_trace_inherit.phpt | 1 + ...stributed_trace_overwrite_active_span.phpt | 1 + tests/ext/extract_server_values.phpt | 1 + tests/ext/fibers/fiber_observer_bailout.phpt | 1 + tests/ext/fibers/fiber_stack_switch.phpt | 1 + tests/ext/flush-autofinish.phpt | 1 + tests/ext/force_flush_traces.phpt | 1 + tests/ext/generate_128_bit_trace_id.phpt | 1 + tests/ext/inherit_meta_from_parent.phpt | 2 ++ .../curl/distributed_tracing_curl.phpt | 1 + .../distributed_tracing_curl_copy_handle.phpt | 1 + ...ted_tracing_curl_existing_headers_001.phpt | 1 + ...ted_tracing_curl_existing_headers_002.phpt | 1 + ...ted_tracing_curl_existing_headers_003.phpt | 1 + ...stributed_tracing_curl_multi_exec_001.phpt | 1 + ...stributed_tracing_curl_multi_exec_002.phpt | 1 + ...stributed_tracing_curl_multi_exec_003.phpt | 1 + ...d_tracing_curl_multi_exec_copy_handle.phpt | 1 + ..._curl_multi_exec_existing_headers_001.phpt | 1 + ..._curl_multi_exec_existing_headers_002.phpt | 1 + ...t_metadata_injection_from_valid_files.phpt | 1 + ...metadata_injection_from_invalid_files.phpt | 1 + .../source_code/commit_sha_env_var.phpt | 1 + .../git_metadata_injection_from_env.phpt | 1 + ...t_metadata_injection_from_global_tags.phpt | 1 + ...injection_remove_credentials_from_env.phpt | 1 + .../source_code/repository_url_env_var.phpt | 1 + tests/ext/ip_collection_03.phpt | 1 + tests/ext/limiter/002-limiter-reached.phpt | 1 + .../live-debugger/exception-replay_001.phpt | 1 - .../live-debugger/exception-replay_002.phpt | 1 - .../pcntl_fork_long_running_autoflush.phpt | 1 - .../pcntl/pcntl_fork_long_running_manual.phpt | 1 - tests/ext/profiling/runtime_id_01.phpt | 1 - tests/ext/profiling/runtime_id_02.phpt | 1 - .../dd_trace_exception_span_event.phpt | 1 - .../request-replayer/dd_trace_span_event.phpt | 1 - .../dd_trace_span_link_with_exception.phpt | 1 - tests/ext/root_span_http_client_ip.phpt | 1 + ...oot_span_http_client_ip_custom_header.phpt | 1 + ...n_http_client_ip_duplicate_ip_headers.phpt | 1 + ...t_span_http_client_ip_x_forwarded_for.phpt | 1 + tests/ext/root_span_http_useragent.phpt | 1 + .../ext/root_span_url_with_post_no_param.phpt | 1 + .../root_span_url_with_post_no_param_set.phpt | 1 + .../ext/sandbox-prehook/dd_trace_method.phpt | 1 + .../sandbox-prehook/exception_error_log.phpt | 1 + ...in_limited_tracing_userland_functions.phpt | 1 + .../class_resolver_bailout_hook.phpt | 1 + .../dd_trace_tracer_is_limited_hard.phpt | 1 + .../dd_trace_tracer_limiter_reset_flush.phpt | 1 - .../limiter_reset_flush_with_open_spans.phpt | 1 - .../nested_dropped_spans.phpt | 1 + tests/ext/sandbox/auto_flush.phpt | 1 - .../sandbox/auto_flush_attach_exception.phpt | 1 - .../sandbox/auto_flush_disables_tracing.phpt | 1 - .../sandbox/auto_flush_sandbox_exception.phpt | 1 - .../auto_flush_userland_root_span.phpt | 1 - .../ext/sandbox/dd_trace_function_alias.phpt | 1 + .../sandbox/dd_trace_function_complex.phpt | 1 + .../sandbox/dd_trace_function_internal.phpt | 1 + tests/ext/sandbox/dd_trace_method.phpt | 1 + tests/ext/sandbox/dd_trace_method_alias.phpt | 1 + .../ext/sandbox/default_span_properties.phpt | 1 + .../default_span_properties_method.phpt | 1 + .../deferred_load_attempt_loading_once.phpt | 1 + tests/ext/sandbox/die_in_sandbox.phpt | 2 ++ .../errors_are_flagged_from_userland.phpt | 1 + tests/ext/sandbox/exception_error_log.phpt | 1 + tests/ext/sandbox/hook_function/03.phpt | 1 + .../hook_function/posthook_error_02.phpt | 1 + .../hook_function/posthook_exceptions_04.phpt | 1 + .../hook_function/prehook_error_02.phpt | 1 + .../hook_function/prehook_exceptions_02.phpt | 1 + .../hook_function/prehook_exceptions_04.phpt | 1 + tests/ext/sandbox/hook_method/03.phpt | 1 + .../ext/sandbox/hook_method/posthook_07.phpt | 1 + .../hook_method/posthook_error_02.phpt | 1 + .../hook_method/posthook_span_ids_02.phpt | 1 + .../sandbox/hook_method/prehook_error_02.phpt | 1 + .../hook_method/prehook_exceptions_02.phpt | 1 + .../hook_method/prehook_span_ids_02.phpt | 1 + .../install_hook/hook_scoped_file.phpt | 2 ++ .../sandbox/install_hook/trace_callable.phpt | 2 ++ .../sandbox/install_hook/trace_closure.phpt | 2 ++ .../trace_closure_from_callable.phpt | 2 ++ .../ext/sandbox/install_hook/trace_file.phpt | 2 ++ .../sandbox/install_hook/trace_function.phpt | 2 ++ .../sandbox/install_hook/trace_generator.phpt | 2 ++ ...in_limited_tracing_internal_functions.phpt | 1 + ...s_in_limited_tracing_internal_methods.phpt | 1 + ...in_limited_tracing_userland_functions.phpt | 1 + ...s_in_limited_tracing_userland_methods.phpt | 1 + tests/ext/sandbox/manual_flush.phpt | 2 +- tests/ext/sandbox/new_static.phpt | 2 ++ .../retval_is_null_with_exception.phpt | 1 + tests/ext/sandbox/span_clone.phpt | 1 + ...c_tracing_closures_will_not_bind_this.phpt | 1 + .../check-sample-rate.phpt | 1 + .../limited-single-span-with-match.phpt | 1 + .../limited-single-span.phpt | 1 + .../name-matching-single-span.phpt | 1 + ...single-span-sampling-config-from-file.phpt | 1 + tests/ext/span_stack/span_stack_clone.phpt | 1 + tests/ext/span_stack/span_stack_swap.phpt | 1 + .../span_stack_swap_traced_function.phpt | 1 + .../span_trace_stack_autoclose.phpt | 1 + tests/ext/span_stack/span_trace_swap.phpt | 1 + .../ext/span_stack/stack_dropped_parent.phpt | 2 ++ .../ext/span_stack/start_span_new_trace.phpt | 1 + tests/ext/span_stack/start_span_stack.phpt | 1 + .../start_top_level_span_stack.phpt | 1 + tests/ext/span_with_removed_exception.phpt | 1 + tests/ext/start_span_with_all_properties.phpt | 1 + tests/ext/start_span_without_closing.phpt | 1 + tests/ext/startup_logging_json.phpt | 2 ++ tests/ext/startup_logging_json_config.phpt | 2 -- tests/ext/telemetry/config.phpt | 11 ++--------- tests/ext/traced_attribute.phpt | 1 + tests/ext/traced_attribute_delayed.phpt | 1 + tests/overhead/Makefile | 6 +++--- 154 files changed, 178 insertions(+), 79 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 7e1b77fcaa..2dc12adc07 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -836,7 +836,6 @@ jobs: name: Run xdebug tests command: | export REPORT_EXIT_STATUS=1 - export DD_TRACE_CLI_ENABLED=1 targetdir() { if [[ ${1:0:1} -eq 2 ]]; then echo $1 @@ -1154,7 +1153,7 @@ jobs: name: Set test environment variables shell: powershell.exe command: | - docker exec php powershell.exe 'setx DD_AUTOLOAD_NO_COMPILE true; setx DD_TRACE_CLI_ENABLED 1; setx DATADOG_HAVE_DEV_ENV 1; setx DD_TRACE_GIT_METADATA_ENABLED 0' + docker exec php powershell.exe 'setx DD_AUTOLOAD_NO_COMPILE true; setx DATADOG_HAVE_DEV_ENV 1; setx DD_TRACE_GIT_METADATA_ENABLED 0' - run: name: Run extension tests shell: powershell.exe @@ -1843,7 +1842,6 @@ jobs: ) fi cd /usr/local/src/php - export DD_TRACE_CLI_ENABLED=true export DD_TRACE_STARTUP_LOGS=0 export DD_TRACE_WARN_CALL_STACK_DEPTH=0 export DD_TRACE_WARN_LEGACY_DD_TRACE=0 @@ -2584,7 +2582,6 @@ jobs: TERM=dumb \ HTTPBIN_HOSTNAME=${HTTPBIN_HOSTNAME} \ DATADOG_HAVE_DEV_ENV=1 \ - DD_TRACE_CLI_ENABLED=1 \ DD_TRACE_GIT_METADATA_ENABLED=0 \ pecl run-tests <<# parameters.showdiff >> --showdiff <> --ini=" -d datadog.trace.sources_path=" -p datadog_trace - run: diff --git a/Makefile b/Makefile index 8d3614995b..2defe8b86f 100644 --- a/Makefile +++ b/Makefile @@ -153,10 +153,10 @@ install_appsec: install_all: install install_ini run_tests: $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php - DD_TRACE_CLI_ENABLED=1 DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) $(TESTS) + DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) $(TESTS) test_c: $(SO_FILE) $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php - $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_CLI_ENABLED=1 DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) $(BUILD_DIR)/$(subst $(BUILD_DIR_NAME)/,,$(TESTS)) + $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) $(BUILD_DIR)/$(subst $(BUILD_DIR_NAME)/,,$(TESTS)) test_c_coverage: dist_clean DD_TRACE_DOCKER_DEBUG=1 EXTRA_CFLAGS="-fprofile-arcs -ftest-coverage" $(MAKE) test_c || exit 0 @@ -168,12 +168,11 @@ test_c_disabled: $(SO_FILE) $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-te ) test_c_observer: $(SO_FILE) $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php - $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_CLI_ENABLED=1 DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) -d extension=zend_test.so -d zend_test.observer.enabled=1 -d zend_test.observer.observe_all=1 -d zend_test.observer.show_output=0 $(BUILD_DIR)/$(TESTS) + $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_GIT_METADATA_ENABLED=0 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) -d extension=zend_test.so -d zend_test.observer.enabled=1 -d zend_test.observer.observe_all=1 -d zend_test.observer.show_output=0 $(BUILD_DIR)/$(TESTS) test_opcache: $(SO_FILE) $(TEST_OPCACHE_FILES) $(BUILD_DIR)/run-tests.php - $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_CLI_ENABLED=1 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) -d zend_extension=opcache.so $(BUILD_DIR)/tests/opcache + $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) $(RUN_TESTS_CMD) -d extension=$(SO_FILE) -d zend_extension=opcache.so $(BUILD_DIR)/tests/opcache -test_c_mem: export DD_TRACE_CLI_ENABLED=1 test_c_mem: $(SO_FILE) $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php $(RUN_TESTS_CMD) -d extension=$(SO_FILE) -m $(BUILD_DIR)/$(TESTS) @@ -182,7 +181,6 @@ test_c2php: $(SO_FILE) $(INIT_HOOK_TEST_FILES) $(BUILD_DIR)/run-tests.php set -xe; \ export PATH="$(PROJECT_ROOT)/tests/ext/valgrind:$$PATH"; \ sed -i 's/stream_socket_accept($$listenSock, 5)/stream_socket_accept($$listenSock, 20)/' $(BUILD_DIR)/run-tests.php; \ - export DD_TRACE_CLI_ENABLED=1; \ export USE_ZEND_ALLOC=0; \ export ZEND_DONT_UNLOAD_MODULES=1; \ export USE_TRACKED_ALLOC=1; \ @@ -190,13 +188,12 @@ test_c2php: $(SO_FILE) $(INIT_HOOK_TEST_FILES) $(BUILD_DIR)/run-tests.php ) test_with_init_hook: $(SO_FILE) $(INIT_HOOK_TEST_FILES) $(BUILD_DIR)/run-tests.php - $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) DD_TRACE_CLI_ENABLED=1 $(RUN_TESTS_CMD) -d extension=$(SO_FILE) -d datadog.trace.sources_path=$(TRACER_SOURCE_DIR) $(INIT_HOOK_TEST_FILES); + $(if $(ASAN), USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1) $(RUN_TESTS_CMD) -d extension=$(SO_FILE) -d datadog.trace.sources_path=$(TRACER_SOURCE_DIR) $(INIT_HOOK_TEST_FILES); test_extension_ci: $(SO_FILE) $(TEST_FILES) $(TEST_STUB_FILES) $(BUILD_DIR)/run-tests.php ( \ set -xe; \ export PATH="$(PROJECT_ROOT)/tests/ext/valgrind:$$PATH"; \ - export DD_TRACE_CLI_ENABLED=1; \ export TEST_PHP_JUNIT=$(JUNIT_RESULTS_DIR)/normal-extension-test.xml; \ export DD_TRACE_GIT_METADATA_ENABLED=0; \ $(RUN_TESTS_CMD) -d extension=$(SO_FILE) $(BUILD_DIR)/$(TESTS); \ @@ -519,7 +516,7 @@ cores: # TESTS ######################################################################################################################## TRACER_SOURCES_INI := -d datadog.trace.sources_path=$(TRACER_SOURCE_DIR) -ENV_OVERRIDE := $(shell [ -n "${DD_TRACE_DOCKER_DEBUG}" ] && echo DD_AUTOLOAD_NO_COMPILE=true DD_TRACE_SOURCES_PATH=$(TRACER_SOURCE_DIR)) DD_DOGSTATSD_URL=http://request-replayer:80 DD_TRACE_CLI_ENABLED=true DD_TRACE_GIT_METADATA_ENABLED=false +ENV_OVERRIDE := $(shell [ -n "${DD_TRACE_DOCKER_DEBUG}" ] && echo DD_AUTOLOAD_NO_COMPILE=true DD_TRACE_SOURCES_PATH=$(TRACER_SOURCE_DIR)) DD_DOGSTATSD_URL=http://request-replayer:80 DD_TRACE_GIT_METADATA_ENABLED=false TEST_EXTRA_INI ?= TEST_EXTRA_ENV ?= diff --git a/appsec/cmake/run-tests-wrapper.sh b/appsec/cmake/run-tests-wrapper.sh index 701cb35dd3..df84590d5f 100755 --- a/appsec/cmake/run-tests-wrapper.sh +++ b/appsec/cmake/run-tests-wrapper.sh @@ -7,7 +7,6 @@ export NO_INTERACTION=1 export DD_TRACE_ENABLED=true #export DD_TRACE_DEBUG=true export DD_TRACE_GENERATE_ROOT_SPAN=true -export DD_TRACE_CLI_ENABLED=true export DD_TRACE_AGENT_PORT=18126 export PHPRC= diff --git a/dockerfiles/frameworks/contrib/entrypoint.sh b/dockerfiles/frameworks/contrib/entrypoint.sh index 091a51b0a7..9cb39364fe 100755 --- a/dockerfiles/frameworks/contrib/entrypoint.sh +++ b/dockerfiles/frameworks/contrib/entrypoint.sh @@ -2,7 +2,5 @@ if [[ -z "$NO_DDTRACE" ]]; then curl -o /tmp/ddtrace.deb http://nginx_file_server/ddtrace.deb dpkg -i /tmp/ddtrace.deb - - export DD_TRACE_CLI_ENABLED=true fi exec "$@" diff --git a/dockerfiles/frameworks/contrib/phpredis/3.1.6/run.sh b/dockerfiles/frameworks/contrib/phpredis/3.1.6/run.sh index c66a6ee525..57d5283fe0 100755 --- a/dockerfiles/frameworks/contrib/phpredis/3.1.6/run.sh +++ b/dockerfiles/frameworks/contrib/phpredis/3.1.6/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -xe switch_php 7.3 -DD_TRACE_CLI_ENABLED=true php ./tests/TestRedis.php --host ${REDIS_HOST} --class Redis -DD_TRACE_CLI_ENABLED=true php ./tests/TestRedis.php --host ${REDIS_HOST} --class RedisArray +php ./tests/TestRedis.php --host ${REDIS_HOST} --class Redis +php ./tests/TestRedis.php --host ${REDIS_HOST} --class RedisArray diff --git a/dockerfiles/frameworks/contrib/phpredis/4.3.0/run.sh b/dockerfiles/frameworks/contrib/phpredis/4.3.0/run.sh index c66a6ee525..57d5283fe0 100755 --- a/dockerfiles/frameworks/contrib/phpredis/4.3.0/run.sh +++ b/dockerfiles/frameworks/contrib/phpredis/4.3.0/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -xe switch_php 7.3 -DD_TRACE_CLI_ENABLED=true php ./tests/TestRedis.php --host ${REDIS_HOST} --class Redis -DD_TRACE_CLI_ENABLED=true php ./tests/TestRedis.php --host ${REDIS_HOST} --class RedisArray +php ./tests/TestRedis.php --host ${REDIS_HOST} --class Redis +php ./tests/TestRedis.php --host ${REDIS_HOST} --class RedisArray diff --git a/dockerfiles/frameworks/contrib/phpredis/5.3.1/run.sh b/dockerfiles/frameworks/contrib/phpredis/5.3.1/run.sh index c66a6ee525..57d5283fe0 100755 --- a/dockerfiles/frameworks/contrib/phpredis/5.3.1/run.sh +++ b/dockerfiles/frameworks/contrib/phpredis/5.3.1/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -xe switch_php 7.3 -DD_TRACE_CLI_ENABLED=true php ./tests/TestRedis.php --host ${REDIS_HOST} --class Redis -DD_TRACE_CLI_ENABLED=true php ./tests/TestRedis.php --host ${REDIS_HOST} --class RedisArray +php ./tests/TestRedis.php --host ${REDIS_HOST} --class Redis +php ./tests/TestRedis.php --host ${REDIS_HOST} --class RedisArray diff --git a/dockerfiles/release-candidates/docker-compose.yml b/dockerfiles/release-candidates/docker-compose.yml index 3244155a42..bfad8b453b 100644 --- a/dockerfiles/release-candidates/docker-compose.yml +++ b/dockerfiles/release-candidates/docker-compose.yml @@ -11,13 +11,11 @@ x-base-php-service: tty: true environment: - DD_AGENT_HOST=ddagent - - DD_TRACE_CLI_ENABLED=1 - DD_TRACE_DEBUG=1 - DD_TRACE_STARTUP_LOGS=0 - DD_ENV=testing # Should take priority over DD_TAGS=env:localhost - DD_TAGS=env:localhost,foo.tag:custom - DD_SERVICE=rc-${PHP_FPM_CONTAINER:-testing-service} # Will emmit deprecated diagnostic - #- DD_TRACE_AUTO_FLUSH_ENABLED=1 #- DD_TRACE_GENERATE_ROOT_SPAN=0 cap_add: - SYS_PTRACE diff --git a/dockerfiles/verify_packages/verify.sh b/dockerfiles/verify_packages/verify.sh index e97765b324..480391119f 100755 --- a/dockerfiles/verify_packages/verify.sh +++ b/dockerfiles/verify_packages/verify.sh @@ -32,7 +32,7 @@ fi echo "PHP version: $(${DD_TRACE_PHP_BIN} -v)" # Script output -CLI_OUTPUT=$(DD_TRACE_CLI_ENABLED=true ${DD_TRACE_PHP_BIN} /var/www/html/index.php) +CLI_OUTPUT=$(${DD_TRACE_PHP_BIN} /var/www/html/index.php) if [ "${CLI_OUTPUT}" != "hi" ]; then echo "Error: expected request output is 'hi'. Actual:\n${APACHE_OUTPUT}" exit 1 diff --git a/examples/long-running/Dockerfile b/examples/long-running/Dockerfile index 4224d5d712..28ec93aff4 100644 --- a/examples/long-running/Dockerfile +++ b/examples/long-running/Dockerfile @@ -6,7 +6,5 @@ RUN dpkg -i /tmp/dd-trace-php.deb WORKDIR /app -CMD DD_TRACE_CLI_ENABLED=true \ - DD_TRACE_AUTO_FLUSH_ENABLED=true \ - DD_TRACE_GENERATE_ROOT_SPAN=false \ +CMD DD_TRACE_GENERATE_ROOT_SPAN=false \ php long-running-script.php diff --git a/ext/configuration.c b/ext/configuration.c index 240c050ec7..ab51b810e6 100644 --- a/ext/configuration.c +++ b/ext/configuration.c @@ -176,6 +176,10 @@ static void dd_ini_env_to_ini_name(const zai_str env_name, zai_config_name *ini_ } bool ddtrace_config_minit(int module_number) { + if (ddtrace_active_sapi == DATADOG_PHP_SAPI_CLI) { + config_entries[DDTRACE_CONFIG_DD_TRACE_AUTO_FLUSH_ENABLED].default_encoded_value = (zai_str) ZAI_STR_FROM_CSTR("true"); + } + if (!zai_config_minit(config_entries, (sizeof config_entries / sizeof *config_entries), dd_ini_env_to_ini_name, module_number)) { ddtrace_log_ginit(); diff --git a/ext/configuration.h b/ext/configuration.h index ddc8516953..57dd53cc7e 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -118,8 +118,8 @@ enum ddtrace_sampling_rules_format { CONFIG(INT, DD_TRACE_AGENT_PORT, "0", .ini_change = zai_config_system_ini_change) \ CONFIG(BOOL, DD_TRACE_ANALYTICS_ENABLED, "false") \ CONFIG(BOOL, DD_TRACE_APPEND_TRACE_IDS_TO_LOGS, "false") \ - CONFIG(BOOL, DD_TRACE_AUTO_FLUSH_ENABLED, "false") \ - CONFIG(BOOL, DD_TRACE_CLI_ENABLED, "false") \ + CONFIG(BOOL, DD_TRACE_AUTO_FLUSH_ENABLED, "false") /* true in CLI */ \ + CONFIG(BOOL, DD_TRACE_CLI_ENABLED, "true") \ CONFIG(BOOL, DD_TRACE_MEASURE_COMPILE_TIME, "true") \ CONFIG(BOOL, DD_TRACE_MEASURE_PEAK_MEMORY_USAGE, "true") \ CONFIG(BOOL, DD_TRACE_DEBUG, "false", .ini_change = ddtrace_alter_dd_trace_debug) \ diff --git a/ext/ddtrace.c b/ext/ddtrace.c index 8826533419..4e8fcb3c35 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -453,6 +453,13 @@ static void dd_activate_once(void) { static pthread_once_t dd_activate_once_control = PTHREAD_ONCE_INIT; +static bool dd_is_cli_autodisabled(const char *arg) { + const char *slashend = strrchr(arg, '/'); + const char *backslashend = strrchr(arg, '\\'); + arg = MAX(MAX(slashend, backslashend) + 1, arg); + return strcmp(arg, "composer") == 0 || strcmp(arg, "composer.phar") == 0; +} + static void ddtrace_activate(void) { ddog_reset_logger(); @@ -482,8 +489,16 @@ static void ddtrace_activate(void) { dd_save_sampling_rules_file_config(sampling_rules_file, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); } - if (!ddtrace_disable && strcmp(sapi_module.name, "cli") == 0 && !get_DD_TRACE_CLI_ENABLED()) { - ddtrace_disable = 2; + if (!ddtrace_disable && strcmp(sapi_module.name, "cli") == 0) { + if (zai_config_memoized_entries[DDTRACE_CONFIG_DD_TRACE_CLI_ENABLED].name_index < 0 && SG(request_info).argv && dd_is_cli_autodisabled(SG(request_info).argv[0])) { + zend_string *zero = zend_string_init("0", 1, 0); + zend_alter_ini_entry(zai_config_memoized_entries[DDTRACE_CONFIG_DD_TRACE_CLI_ENABLED].ini_entries[0]->name, zero, + ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME); + zend_string_release(zero); + } + if (!get_DD_TRACE_CLI_ENABLED()) { + ddtrace_disable = 2; + } } if (ddtrace_disable) { diff --git a/loader/tests/functional/test_ddtrace_is_fully_loaded.php b/loader/tests/functional/test_ddtrace_is_fully_loaded.php index 43f309fe78..6fefda6bd7 100644 --- a/loader/tests/functional/test_ddtrace_is_fully_loaded.php +++ b/loader/tests/functional/test_ddtrace_is_fully_loaded.php @@ -3,7 +3,7 @@ require_once __DIR__."/includes/autoload.php"; skip_if_php5(); -$output = runCLI(__DIR__.'/fixtures/ddtrace.php'); +$output = runCLI('-ddatadog.trace.cli_enabled=0 '.__DIR__.'/fixtures/ddtrace.php'); assertEquals($output, << 'true', 'DD_AGENT_HOST' => 'test-agent', 'DD_TRACE_AGENT_PORT' => '9126', // Uncomment to see debug-level messages 'DD_TRACE_DEBUG' => 'true', 'DD_TEST_INTEGRATION' => 'true', + 'DD_TRACE_AUTO_FLUSH_ENABLED' => 'false', 'DD_TRACE_EXEC_ENABLED' => 'false', 'DD_TRACE_SHUTDOWN_TIMEOUT' => '666666', // Arbitrarily high value to avoid flakiness 'DD_TRACE_AGENT_RETRIES' => '3', diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index a78342c38c..e10f91cf96 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -233,7 +233,6 @@ public function executeCli($scriptPath, $customEnvs = [], $customInis = [], $arg $envs = (string) new EnvSerializer(array_merge( [ 'DD_AUTOLOAD_NO_COMPILE' => getenv('DD_AUTOLOAD_NO_COMPILE'), - 'DD_TRACE_CLI_ENABLED' => 'true', 'DD_AGENT_HOST' => 'test-agent', 'DD_TRACE_AGENT_PORT' => '9126', // Uncomment to see debug-level messages diff --git a/tests/Integrations/PCNTL/PCNTLTest.php b/tests/Integrations/PCNTL/PCNTLTest.php index bbebe09dd4..3df2bc579d 100644 --- a/tests/Integrations/PCNTL/PCNTLTest.php +++ b/tests/Integrations/PCNTL/PCNTLTest.php @@ -5,12 +5,16 @@ use DDTrace\Tests\Common\IntegrationTestCase; use DDTrace\Tests\Common\SpanAssertion; -const ACCEPTABLE_TEST_EXECTION_TIME_S = 1.4; - final class PCNTLTest extends IntegrationTestCase { + private static $acceptable_test_execution_time = 2; + protected function ddSetUp() { + if (!\dd_trace_env_config("DD_TRACE_SIDECAR_TRACE_SENDER")) { + self::$acceptable_test_execution_time = 1.4; + } + $this->resetRequestDumper(); parent::ddSetUp(); } @@ -34,7 +38,7 @@ public function testDoesNoHangAtShutdownWhenDisabled($scriptPath) ] ); $end = \microtime(true); - $this->assertLessThan(ACCEPTABLE_TEST_EXECTION_TIME_S, $end - $start); + $this->assertLessThan(self::$acceptable_test_execution_time, $end - $start); } /** @@ -59,7 +63,7 @@ public function testDoesNoHangAtShutdownWhenEnabled($scriptPath) true ); $end = \microtime(true); - $this->assertLessThan(ACCEPTABLE_TEST_EXECTION_TIME_S, $end - $start); + $this->assertLessThan(self::$acceptable_test_execution_time, $end - $start); if (\dd_trace_env_config("DD_TRACE_SIDECAR_TRACE_SENDER")) { \dd_trace_synchronous_flush(); } diff --git a/tests/bootstrap_phpunit.php b/tests/bootstrap_phpunit.php index 640e78c709..fbfd2c177d 100644 --- a/tests/bootstrap_phpunit.php +++ b/tests/bootstrap_phpunit.php @@ -5,3 +5,5 @@ require __DIR__ . '/bootstrap_common.php'; require_once __DIR__ . '/Appsec/Mock.php'; + +ini_set("datadog.trace.auto_flush_enabled", "false"); diff --git a/tests/ext/autoload-php-files/dd_init_open_basedir.phpt b/tests/ext/autoload-php-files/dd_init_open_basedir.phpt index b564db7805..12a5fabde3 100644 --- a/tests/ext/autoload-php-files/dd_init_open_basedir.phpt +++ b/tests/ext/autoload-php-files/dd_init_open_basedir.phpt @@ -1,6 +1,7 @@ --TEST-- Calling dd_init.php is confined to open_basedir settings --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off,datadog_sidecar=off DD_AUTOLOAD_NO_COMPILE=1 --INI-- diff --git a/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt b/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt index 5dbe07e0d0..fe12ee4637 100644 --- a/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt +++ b/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt @@ -1,6 +1,7 @@ --TEST-- Errors in ddtrace autoloader do not affect error_get_last() --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_AUTOLOAD_NO_COMPILE=1 --INI-- diff --git a/tests/ext/autoload-php-files/file_not_found.phpt b/tests/ext/autoload-php-files/file_not_found.phpt index 50919d4a24..413b3d19ad 100644 --- a/tests/ext/autoload-php-files/file_not_found.phpt +++ b/tests/ext/autoload-php-files/file_not_found.phpt @@ -1,6 +1,7 @@ --TEST-- Do not fail when PHP code couldn't be loaded --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_AUTOLOAD_NO_COMPILE=1 --INI-- diff --git a/tests/ext/autoload-php-files/ignores_exceptions.phpt b/tests/ext/autoload-php-files/ignores_exceptions.phpt index 953a5fc77e..993d1bd08b 100644 --- a/tests/ext/autoload-php-files/ignores_exceptions.phpt +++ b/tests/ext/autoload-php-files/ignores_exceptions.phpt @@ -1,6 +1,7 @@ --TEST-- Request init hook ignores exceptions --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_AUTOLOAD_NO_COMPILE=1 --INI-- diff --git a/tests/ext/autoload-php-files/ignores_fatal_errors.phpt b/tests/ext/autoload-php-files/ignores_fatal_errors.phpt index 40b22ab8f6..e3a8593020 100644 --- a/tests/ext/autoload-php-files/ignores_fatal_errors.phpt +++ b/tests/ext/autoload-php-files/ignores_fatal_errors.phpt @@ -3,6 +3,7 @@ Request init hook ignores fatal errors --SKIPIF-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_AUTOLOAD_NO_COMPILE=1 --INI-- diff --git a/tests/ext/background-sender/agent_headers.phpt b/tests/ext/background-sender/agent_headers.phpt index 2d638aa364..92e036efba 100644 --- a/tests/ext/background-sender/agent_headers.phpt +++ b/tests/ext/background-sender/agent_headers.phpt @@ -1,7 +1,6 @@ --TEST-- HTTP headers are sent to the Agent from the background sender --SKIPIF-- - --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off @@ -11,7 +10,6 @@ DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS=1 DD_TRACE_AGENT_FLUSH_INTERVAL=666 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 --INI-- datadog.trace.agent_test_session_token=background-sender/agent_headers --FILE-- diff --git a/tests/ext/background-sender/agent_headers_container_id.phpt b/tests/ext/background-sender/agent_headers_container_id.phpt index 0dbdedce3a..cb8b6e732f 100644 --- a/tests/ext/background-sender/agent_headers_container_id.phpt +++ b/tests/ext/background-sender/agent_headers_container_id.phpt @@ -12,7 +12,6 @@ DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS=1 DD_TRACE_AGENT_FLUSH_INTERVAL=666 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 --INI-- ddtrace.cgroup_file={PWD}/stubs/cgroup.docker datadog.trace.agent_test_session_token=background-sender/agent_headers_container_id diff --git a/tests/ext/background-sender/agent_headers_container_id_empty.phpt b/tests/ext/background-sender/agent_headers_container_id_empty.phpt index 2ab2c997b2..afbd716b0a 100644 --- a/tests/ext/background-sender/agent_headers_container_id_empty.phpt +++ b/tests/ext/background-sender/agent_headers_container_id_empty.phpt @@ -11,7 +11,6 @@ DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS=1 DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 --INI-- ddtrace.cgroup_file={PWD}/stubs/cgroup.empty datadog.trace.agent_test_session_token=background-sender/agent_headers_container_id_empty diff --git a/tests/ext/background-sender/agent_headers_container_id_fargate.phpt b/tests/ext/background-sender/agent_headers_container_id_fargate.phpt index d8845243f2..796629e986 100644 --- a/tests/ext/background-sender/agent_headers_container_id_fargate.phpt +++ b/tests/ext/background-sender/agent_headers_container_id_fargate.phpt @@ -12,7 +12,6 @@ DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS=1 DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 --INI-- ddtrace.cgroup_file={PWD}/stubs/cgroup.fargate.1.4 datadog.trace.agent_test_session_token=background-sender/agent_headers_container_id_fargate diff --git a/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt b/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt index dd92754546..0dc5715bf3 100644 --- a/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt +++ b/tests/ext/background-sender/agent_headers_unix_domain_socket.phpt @@ -10,7 +10,6 @@ DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS=1 DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 DD_REMOTE_CONFIG_ENABLED=0 --INI-- datadog.trace.agent_test_session_token=background-sender/agent_headers_unix_domain_socket diff --git a/tests/ext/background-sender/agent_sampling.phpt b/tests/ext/background-sender/agent_sampling.phpt index e3834ea205..2896404e3f 100644 --- a/tests/ext/background-sender/agent_sampling.phpt +++ b/tests/ext/background-sender/agent_sampling.phpt @@ -10,7 +10,6 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_SIDECAR_TRACE_SENDER=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 --INI-- datadog.trace.agent_test_session_token=background-sender/agent_sampling --FILE-- diff --git a/tests/ext/background-sender/agent_sampling_sidecar.phpt b/tests/ext/background-sender/agent_sampling_sidecar.phpt index 22671ff66d..e33c298f11 100644 --- a/tests/ext/background-sender/agent_sampling_sidecar.phpt +++ b/tests/ext/background-sender/agent_sampling_sidecar.phpt @@ -11,7 +11,6 @@ DD_TRACE_AGENT_FLUSH_INTERVAL=333 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_INSTRUMENTATION_TELEMETRY_ENABLED=0 DD_TRACE_SIDECAR_TRACE_SENDER=1 -DD_TRACE_AUTO_FLUSH_ENABLED=1 --INI-- datadog.trace.agent_test_session_token=background-sender/agent_sampling_sidecar --FILE-- diff --git a/tests/ext/close_spans_until.phpt b/tests/ext/close_spans_until.phpt index f362895631..3c0a6a8be3 100644 --- a/tests/ext/close_spans_until.phpt +++ b/tests/ext/close_spans_until.phpt @@ -1,6 +1,7 @@ --TEST-- Test DDTrace\close_spans_until --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,span=trace,startup=off --FILE-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_TRACE_HEADER_TAGS=0 HTTP_0=http_zero_header diff --git a/tests/ext/fibers/fiber_observer_bailout.phpt b/tests/ext/fibers/fiber_observer_bailout.phpt index 45ade74c5f..d14863ee91 100644 --- a/tests/ext/fibers/fiber_observer_bailout.phpt +++ b/tests/ext/fibers/fiber_observer_bailout.phpt @@ -5,6 +5,7 @@ Test fiber observing with bailout --INI-- memory_limit=100M --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_GENERATE_ROOT_SPAN=0 DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH=25 diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_copy_handle.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_copy_handle.phpt index a75671c5cf..09de7f66e4 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_copy_handle.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_copy_handle.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate after curl_copy_handle() --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec --FILE-- diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_001.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_001.phpt index 6ad7a1b523..bba8baf561 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_001.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_001.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate with existing headers set with curl_setopt --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec HTTP_X_DATADOG_ORIGIN=phpt-test diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_002.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_002.phpt index 654cde6513..f63d45dfbd 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_002.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_002.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate with existing headers set with curl_setopt --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec HTTP_X_DATADOG_ORIGIN=phpt-test diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_003.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_003.phpt index dd4eb84044..971955fd9b 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_003.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_existing_headers_003.phpt @@ -10,6 +10,7 @@ Some libraries do not check the return stats when setting curl opts. The original headers should still be applied even when there is an error from setting the curl opts. --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec HTTP_X_DATADOG_ORIGIN=phpt-test diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_001.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_001.phpt index 91b70610aa..5ba3863ad0 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_001.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_001.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate with curl_multi_exec() --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off HTTP_X_DATADOG_ORIGIN=phpt-test --FILE-- diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_002.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_002.phpt index 455cd64170..6b4d9bc988 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_002.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_002.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate when curl_multi_init() is called before cu --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off HTTP_X_DATADOG_ORIGIN=phpt-test --FILE-- diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_003.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_003.phpt index b2c270c525..4d15eeab48 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_003.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_003.phpt @@ -4,6 +4,7 @@ Test CurlMulti during garbage collection --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off HTTP_X_DATADOG_ORIGIN=phpt-test --FILE-- diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_copy_handle.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_copy_handle.phpt index 92dca7cad5..810fe5fd70 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_copy_handle.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_copy_handle.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate with curl_multi_exec() after curl_copy_han --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off HTTP_X_DATADOG_ORIGIN=phpt-test --FILE-- diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_001.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_001.phpt index 3066d66262..8d67dd86a6 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_001.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_001.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate with curl_multi_exec() and headers set wit --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off HTTP_X_DATADOG_ORIGIN=phpt-test --FILE-- diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_002.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_002.phpt index 20ec53efdf..5eef54eff1 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_002.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_multi_exec_existing_headers_002.phpt @@ -4,6 +4,7 @@ Distributed tracing headers propagate with curl_multi_exec() and headers set wit --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off HTTP_X_DATADOG_ORIGIN=phpt-test --FILE-- diff --git a/tests/ext/integrations/source_code/001/git_metadata_injection_from_valid_files.phpt b/tests/ext/integrations/source_code/001/git_metadata_injection_from_valid_files.phpt index 43cc2d33ba..064eec1f73 100644 --- a/tests/ext/integrations/source_code/001/git_metadata_injection_from_valid_files.phpt +++ b/tests/ext/integrations/source_code/001/git_metadata_injection_from_valid_files.phpt @@ -1,6 +1,7 @@ --TEST-- Basic Git Metadata Injection from valid .git files (Repository URL & Commit Sha) --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_TRACE_GIT_METADATA_ENABLED=1 --SKIPIF-- diff --git a/tests/ext/integrations/source_code/002/git_metadata_injection_from_invalid_files.phpt b/tests/ext/integrations/source_code/002/git_metadata_injection_from_invalid_files.phpt index 05936f451b..1c292234cf 100644 --- a/tests/ext/integrations/source_code/002/git_metadata_injection_from_invalid_files.phpt +++ b/tests/ext/integrations/source_code/002/git_metadata_injection_from_invalid_files.phpt @@ -1,6 +1,7 @@ --TEST-- Basic Git Metadata Injection from invalid .git files (Repository URL & Commit Sha) --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_TRACE_GIT_METADATA_ENABLED=1 --SKIPIF-- diff --git a/tests/ext/integrations/source_code/commit_sha_env_var.phpt b/tests/ext/integrations/source_code/commit_sha_env_var.phpt index d158be18a1..1c49277aba 100644 --- a/tests/ext/integrations/source_code/commit_sha_env_var.phpt +++ b/tests/ext/integrations/source_code/commit_sha_env_var.phpt @@ -1,6 +1,7 @@ --TEST-- When DD_GIT_COMMIT_SHA is specified, _dd.git_commit_sha is injected --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_GIT_COMMIT_SHA=123456 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- diff --git a/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt b/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt index 31c2d5013f..1e86838aee 100644 --- a/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt +++ b/tests/ext/integrations/source_code/git_metadata_injection_from_env.phpt @@ -1,6 +1,7 @@ --TEST-- Basic Git Metadata Injection from env var (Repository URL & Commit Sha) --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_GIT_REPOSITORY_URL=github.com/user/env_repo DD_GIT_COMMIT_SHA=123456 DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt b/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt index 56059b30e5..c24f36e055 100644 --- a/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt +++ b/tests/ext/integrations/source_code/git_metadata_injection_from_global_tags.phpt @@ -1,6 +1,7 @@ --TEST-- Basic Git Metadata Injection from global tags (Repository URL & Commit Sha) --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TAGS=git.commit.sha:123456,git.repository_url:github.com/user/env_repo DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- diff --git a/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt b/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt index e90a2a8e7e..c9588deee1 100644 --- a/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt +++ b/tests/ext/integrations/source_code/git_metadata_injection_remove_credentials_from_env.phpt @@ -1,6 +1,7 @@ --TEST-- Remove credentials from repository URL --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_GIT_REPOSITORY_URL=https://u:t@github.com/user/repo_new DD_GIT_COMMIT_SHA=123456 DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/integrations/source_code/repository_url_env_var.phpt b/tests/ext/integrations/source_code/repository_url_env_var.phpt index db27df5267..467aa21c14 100644 --- a/tests/ext/integrations/source_code/repository_url_env_var.phpt +++ b/tests/ext/integrations/source_code/repository_url_env_var.phpt @@ -1,6 +1,7 @@ --TEST-- When DD_GIT_REPOSITORY_URL is specified, _dd.git.repository_url is injected --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_GIT_REPOSITORY_URL=github.com/user/env_repo DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- diff --git a/tests/ext/ip_collection_03.phpt b/tests/ext/ip_collection_03.phpt index 65dc888ec7..3c55dc4be1 100644 --- a/tests/ext/ip_collection_03.phpt +++ b/tests/ext/ip_collection_03.phpt @@ -1,6 +1,7 @@ --TEST-- Client IP should be collected if env DD_TRACE_CLIENT_IP_ENABLED is set to true --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 REMOTE_ADDR=127.0.0.1 DD_TRACE_CLIENT_IP_ENABLED=true diff --git a/tests/ext/limiter/002-limiter-reached.phpt b/tests/ext/limiter/002-limiter-reached.phpt index 1559bbb9bf..b3a1803ed7 100644 --- a/tests/ext/limiter/002-limiter-reached.phpt +++ b/tests/ext/limiter/002-limiter-reached.phpt @@ -3,6 +3,7 @@ rate limiter reached --SKIPIF-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_TRACE_RATE_LIMIT=10 --FILE-- diff --git a/tests/ext/live-debugger/exception-replay_001.phpt b/tests/ext/live-debugger/exception-replay_001.phpt index bd89fe9fd1..e8cdd472b7 100644 --- a/tests/ext/live-debugger/exception-replay_001.phpt +++ b/tests/ext/live-debugger/exception-replay_001.phpt @@ -7,7 +7,6 @@ Test exception replay DD_AGENT_HOST=request-replayer DD_TRACE_AGENT_PORT=80 DD_TRACE_GENERATE_ROOT_SPAN=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 DD_EXCEPTION_REPLAY_ENABLED=1 DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 --INI-- diff --git a/tests/ext/live-debugger/exception-replay_002.phpt b/tests/ext/live-debugger/exception-replay_002.phpt index 2b794b1e6e..1880a57a5b 100644 --- a/tests/ext/live-debugger/exception-replay_002.phpt +++ b/tests/ext/live-debugger/exception-replay_002.phpt @@ -6,7 +6,6 @@ Test exception replay capture limits DD_AGENT_HOST=request-replayer DD_TRACE_AGENT_PORT=80 DD_TRACE_GENERATE_ROOT_SPAN=0 -DD_TRACE_AUTO_FLUSH_ENABLED=1 DD_EXCEPTION_REPLAY_ENABLED=1 DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 --INI-- diff --git a/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt b/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt index c482ff0684..f37763d85a 100644 --- a/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt +++ b/tests/ext/pcntl/pcntl_fork_long_running_autoflush.phpt @@ -4,7 +4,6 @@ Long running autoflush --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=false -DD_TRACE_AUTO_FLUSH_ENABLED=true DD_TRACE_LOG_LEVEL=info,startup=off --INI-- datadog.trace.sources_path= diff --git a/tests/ext/pcntl/pcntl_fork_long_running_manual.phpt b/tests/ext/pcntl/pcntl_fork_long_running_manual.phpt index 4099eb1ddc..2b2393ce86 100644 --- a/tests/ext/pcntl/pcntl_fork_long_running_manual.phpt +++ b/tests/ext/pcntl/pcntl_fork_long_running_manual.phpt @@ -3,7 +3,6 @@ Long running manual flush --SKIPIF-- --ENV-- -DD_TRACE_CLI_ENABLED=true DD_TRACE_GENERATE_ROOT_SPAN=false DD_TRACE_AUTO_FLUSH_ENABLED=false --FILE-- diff --git a/tests/ext/profiling/runtime_id_01.phpt b/tests/ext/profiling/runtime_id_01.phpt index 534d71dfa4..d883c870f7 100644 --- a/tests/ext/profiling/runtime_id_01.phpt +++ b/tests/ext/profiling/runtime_id_01.phpt @@ -2,7 +2,6 @@ runtime-id exists in meta when profiling is enabled --ENV-- DD_PROFILING_ENABLED=true -DD_TRACE_CLI_ENABLED=true --SKIPIF-- = 70300 && PHP_VERSION_ID < 70400) die('skip: Bailing out in autoloaders is fundamentally broken in PHP 7.3'); ?> --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off --FILE-- --INI-- datadog.trace.debug=1 +--ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 --FILE-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_SAMPLE_RATE=0 DD_SPAN_SAMPLING_RULES=[{"sample_rate":0.5,"max_per_second":10}] DD_TRACE_DEBUG_PRNG_SEED=30 diff --git a/tests/ext/single-span_sampling/limited-single-span-with-match.phpt b/tests/ext/single-span_sampling/limited-single-span-with-match.phpt index 8c95487d70..20df9b9f21 100644 --- a/tests/ext/single-span_sampling/limited-single-span-with-match.phpt +++ b/tests/ext/single-span_sampling/limited-single-span-with-match.phpt @@ -3,6 +3,7 @@ Test max_per_second single span limiting with multiple buckets --SKIPIF-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_SAMPLE_RATE=0 DD_SPAN_SAMPLING_RULES=[{"sample_rate":1,"max_per_second":2,"service":"a","name":"b"},{"sample_rate":1,"max_per_second":2,"service":"a"},{"sample_rate":1,"max_per_second":2,"name":"b"}] DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/single-span_sampling/limited-single-span.phpt b/tests/ext/single-span_sampling/limited-single-span.phpt index 6bf02b93c8..8ce5d712dc 100644 --- a/tests/ext/single-span_sampling/limited-single-span.phpt +++ b/tests/ext/single-span_sampling/limited-single-span.phpt @@ -3,6 +3,7 @@ Test max_per_second single span limiting --SKIPIF-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_SAMPLE_RATE=0 DD_SPAN_SAMPLING_RULES=[{"sample_rate":1,"max_per_second":10}] DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/single-span_sampling/name-matching-single-span.phpt b/tests/ext/single-span_sampling/name-matching-single-span.phpt index 3053b8305f..713dd552a2 100644 --- a/tests/ext/single-span_sampling/name-matching-single-span.phpt +++ b/tests/ext/single-span_sampling/name-matching-single-span.phpt @@ -1,6 +1,7 @@ --TEST-- Ingest all spans --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_SAMPLE_RATE=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- diff --git a/tests/ext/single-span_sampling/read-single-span-sampling-config-from-file.phpt b/tests/ext/single-span_sampling/read-single-span-sampling-config-from-file.phpt index 3d844e136b..235bb2cdf8 100644 --- a/tests/ext/single-span_sampling/read-single-span-sampling-config-from-file.phpt +++ b/tests/ext/single-span_sampling/read-single-span-sampling-config-from-file.phpt @@ -1,6 +1,7 @@ --TEST-- Check sample rate is in effect --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_SAMPLE_RATE=0 DD_SPAN_SAMPLING_RULES=[{"sample_rate":1}] DD_TRACE_GENERATE_ROOT_SPAN=0 diff --git a/tests/ext/span_stack/span_stack_clone.phpt b/tests/ext/span_stack/span_stack_clone.phpt index 176d7e6229..bcd1bf6481 100644 --- a/tests/ext/span_stack/span_stack_clone.phpt +++ b/tests/ext/span_stack/span_stack_clone.phpt @@ -1,6 +1,7 @@ --TEST-- Test cloning a span stack --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- diff --git a/tests/ext/span_stack/span_trace_swap.phpt b/tests/ext/span_stack/span_trace_swap.phpt index a6bdd2c6a9..fd58c9fd42 100644 --- a/tests/ext/span_stack/span_trace_swap.phpt +++ b/tests/ext/span_stack/span_trace_swap.phpt @@ -1,6 +1,7 @@ --TEST-- Test creating swapping traces --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- =')) diff --git a/tests/ext/start_span_with_all_properties.phpt b/tests/ext/start_span_with_all_properties.phpt index 28a6701904..46254bd2bd 100644 --- a/tests/ext/start_span_with_all_properties.phpt +++ b/tests/ext/start_span_with_all_properties.phpt @@ -1,6 +1,7 @@ --TEST-- Set DDTrace\start_span() properties --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- Array - ( - [name] => trace.cli_enabled - [value] => 1 - [origin] => EnvVar - ) - - [2] => Array ( [name] => instrumentation_telemetry_enabled [value] => 1 [origin] => EnvVar ) - [3] => Array + [2] => Array ( [name] => trace.generate_root_span [value] => 0 [origin] => EnvVar ) - [4] => Array + [3] => Array ( [name] => trace.git_metadata_enabled [value] => 0 diff --git a/tests/ext/traced_attribute.phpt b/tests/ext/traced_attribute.phpt index 1e1d713b27..eb196337fd 100644 --- a/tests/ext/traced_attribute.phpt +++ b/tests/ext/traced_attribute.phpt @@ -3,6 +3,7 @@ Test tracing via attributes --SKIPIF-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- --ENV-- +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- Date: Fri, 4 Oct 2024 10:27:43 +0200 Subject: [PATCH 096/103] fix(mongodb): Deprecated exception in v1.20+ (#2870) --- .../Integrations/MongoDB/MongoDBIntegration.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php b/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php index a8bc4be9ec..027a87bc1c 100644 --- a/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php +++ b/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php @@ -17,13 +17,25 @@ function register_subscriber() { class DatadogSubscriber implements \MongoDB\Driver\Monitoring\CommandSubscriber { + private static $useDeprecatedMethods = null; + #[\ReturnTypeWillChange] public function commandStarted(\MongoDB\Driver\Monitoring\CommandStartedEvent $event) { $span = \DDTrace\active_span(); if ($span) { - $span->meta['out.host'] = $event->getServer()->getHost(); - $span->meta['out.port'] = $event->getServer()->getPort(); + if (is_null(self::$useDeprecatedMethods)) { + // v1.20+: getServer() is deprecated in favor of getHost() and getPort() + self::$useDeprecatedMethods = !method_exists($event, 'getHost') || method_exists($event, 'getPort'); + } + + if (self::$useDeprecatedMethods) { + $span->meta['out.host'] = $event->getServer()->getHost(); + $span->meta['out.port'] = $event->getServer()->getPort(); + } else { + $span->meta['out.host'] = $event->getHost(); + $span->meta['out.port'] = $event->getPort(); + } } } From c1fd94c76d188dd5016e3387a4e33d695e1bc683 Mon Sep 17 00:00:00 2001 From: Alejandro Estringana Ruiz Date: Fri, 4 Oct 2024 21:45:27 +0200 Subject: [PATCH 097/103] Fix failing appsec tests on pipeline (#2874) Co-authored-by: Anil Mahtani <929854+Anilm3@users.noreply.github.com> --- .circleci/continue_config.yml | 4 +++- appsec/src/extension/helper_process.c | 6 ++++-- appsec/src/extension/helper_process.h | 5 +++-- appsec/tests/extension/report_backtrace_01.phpt | 1 + appsec/tests/extension/report_backtrace_05.phpt | 1 + appsec/tests/extension/root_span_add_tag.phpt | 1 + 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 2dc12adc07..26a217d3f6 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -1411,7 +1411,9 @@ jobs: command: | export DEBIAN_FRONTEND=noninteractive apt update - apt install -y wget sudo git g++ gcc gcovr cmake make curl libcurl4-gnutls-dev clang clang-tidy clang-format git php-dev php8.2-xml php-cgi cargo + apt install -y wget sudo git g++ gcc gcovr cmake make curl libcurl4-gnutls-dev clang clang-tidy clang-format git php-dev php8.2-xml php-cgi + curl https://sh.rustup.rs -sSf | sh -s -- --profile minimal -y --default-toolchain "1.76.0" + echo 'export PATH=${PATH}:$HOME/.cargo/bin' >> $BASH_ENV - run: git config --global --add safe.directory /home/circleci/datadog/appsec/third_party/libddwaf - run: name: CMake diff --git a/appsec/src/extension/helper_process.c b/appsec/src/extension/helper_process.c index 06f7a76443..47f6ed9f7c 100644 --- a/appsec/src/extension/helper_process.c +++ b/appsec/src/extension/helper_process.c @@ -129,8 +129,10 @@ dd_conn *nullable dd_helper_mgr_cur_conn(void) return NULL; } -// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -bool dd_on_runtime_path_update(zval *nullable old_val, zval *nonnull new_val, zend_string *nonnull new_str) +bool dd_on_runtime_path_update( + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + zval *nullable old_val, zval *nonnull new_val, + zend_string *nullable new_str) { UNUSED(old_val); UNUSED(new_str); diff --git a/appsec/src/extension/helper_process.h b/appsec/src/extension/helper_process.h index ebf6b989eb..c21487f07f 100644 --- a/appsec/src/extension/helper_process.h +++ b/appsec/src/extension/helper_process.h @@ -27,7 +27,8 @@ dd_conn *nullable dd_helper_mgr_acquire_conn(client_init_func nonnull, void *uns dd_conn *nullable dd_helper_mgr_cur_conn(void); void dd_helper_close_conn(void); -bool dd_on_runtime_path_update( - zval *nullable old_value, zval *nonnull new_value, zend_string *nonnull new_str); +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +bool dd_on_runtime_path_update(zval *nullable old_value, + zval *nonnull new_value, zend_string *nullable new_str); #endif // DD_HELPER_MGR_H diff --git a/appsec/tests/extension/report_backtrace_01.phpt b/appsec/tests/extension/report_backtrace_01.phpt index f4624e3263..d39c19746c 100644 --- a/appsec/tests/extension/report_backtrace_01.phpt +++ b/appsec/tests/extension/report_backtrace_01.phpt @@ -2,6 +2,7 @@ Report backtrace --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_TRACE_AUTO_FLUSH_ENABLED=0 --INI-- extension=ddtrace.so --FILE-- diff --git a/appsec/tests/extension/report_backtrace_05.phpt b/appsec/tests/extension/report_backtrace_05.phpt index 4dc58ce567..5f24dc18d6 100644 --- a/appsec/tests/extension/report_backtrace_05.phpt +++ b/appsec/tests/extension/report_backtrace_05.phpt @@ -2,6 +2,7 @@ Trace are reported when helper indicates so --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_TRACE_AUTO_FLUSH_ENABLED=0 --INI-- extension=ddtrace.so datadog.appsec.enabled=1 diff --git a/appsec/tests/extension/root_span_add_tag.phpt b/appsec/tests/extension/root_span_add_tag.phpt index 46b3375cf5..a5fb6112e1 100644 --- a/appsec/tests/extension/root_span_add_tag.phpt +++ b/appsec/tests/extension/root_span_add_tag.phpt @@ -2,6 +2,7 @@ Test ddtrace_root_span_add_tag --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_SERVICE=appsec_tests --INI-- extension=ddtrace.so From 1c81b25efbe5ae617fea6477b8a378dcad8a393f Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 7 Oct 2024 20:19:13 +0100 Subject: [PATCH 098/103] Move helper to sidecar remote config (#2864) * Use sidecar Remote Config impl in AppSec helper * wip * Fixes after review * More review changes * Helper: check shmem against process euid, rather than uid * Remove stray test * Suppress appsec on certain tracer tests * Fix Appsec integration tests --------- Co-authored-by: Anil Mahtani <929854+Anilm3@users.noreply.github.com> --- .circleci/continue_config.yml | 13 +- appsec/cmake/helper.cmake | 4 +- appsec/src/extension/backtrace.c | 3 +- appsec/src/extension/commands/client_init.c | 91 +- appsec/src/extension/commands/config_sync.c | 22 +- appsec/src/extension/commands/config_sync.h | 7 +- appsec/src/extension/ddappsec.c | 36 +- appsec/src/extension/ddappsec.h | 3 + appsec/src/extension/ddappsec.version | 1 + appsec/src/extension/ddtrace.c | 20 + appsec/src/extension/ddtrace.h | 2 + appsec/src/extension/helper_process.c | 5 +- appsec/src/extension/helper_process.h | 4 +- appsec/src/extension/msgpack_helpers.c | 5 +- appsec/src/extension/request_lifecycle.c | 45 +- appsec/src/helper/client.cpp | 63 +- appsec/src/helper/client.hpp | 6 +- appsec/src/helper/engine.cpp | 45 +- appsec/src/helper/engine.hpp | 34 +- appsec/src/helper/engine_settings.hpp | 25 +- appsec/src/helper/json_helper.cpp | 15 +- appsec/src/helper/json_helper.hpp | 3 +- appsec/src/helper/main.cpp | 8 +- appsec/src/helper/network/proto.hpp | 15 +- appsec/src/helper/rate_limit.hpp | 9 +- appsec/src/helper/remote_config/client.cpp | 352 ++--- appsec/src/helper/remote_config/client.hpp | 85 +- .../helper/remote_config/client_handler.cpp | 106 +- .../helper/remote_config/client_handler.hpp | 62 +- appsec/src/helper/remote_config/config.cpp | 105 ++ appsec/src/helper/remote_config/config.hpp | 120 +- appsec/src/helper/remote_config/http_api.cpp | 147 -- appsec/src/helper/remote_config/http_api.hpp | 47 - .../listeners/asm_features_listener.cpp | 16 +- .../listeners/asm_features_listener.hpp | 10 +- .../config_aggregators/asm_aggregator.cpp | 7 +- .../config_aggregators/asm_aggregator.hpp | 8 +- .../asm_data_aggregator.cpp | 10 +- .../asm_data_aggregator.hpp | 6 +- .../config_aggregators/asm_dd_aggregator.cpp | 6 +- .../config_aggregators/asm_dd_aggregator.hpp | 6 +- .../config_aggregators/config_aggregator.hpp | 8 +- .../listeners/engine_listener.cpp | 27 +- .../listeners/engine_listener.hpp | 34 +- .../remote_config/listeners/listener.hpp | 11 +- appsec/src/helper/remote_config/product.cpp | 83 - appsec/src/helper/remote_config/product.hpp | 82 +- .../protocol/cached_target_file_hash.hpp | 22 - .../protocol/cached_target_files.hpp | 27 - .../helper/remote_config/protocol/client.hpp | 70 - .../remote_config/protocol/client_state.hpp | 30 - .../remote_config/protocol/client_tracer.hpp | 31 - .../remote_config/protocol/config_state.hpp | 32 - .../helper/remote_config/protocol/path.hpp | 26 - .../remote_config/protocol/target_file.hpp | 23 - .../helper/remote_config/protocol/targets.hpp | 28 - .../protocol/tuf/get_configs_request.hpp | 28 - .../protocol/tuf/get_configs_response.hpp | 24 - .../protocol/tuf/info_response.hpp | 22 - .../remote_config/protocol/tuf/parser.cpp | 357 ----- .../remote_config/protocol/tuf/parser.hpp | 86 -- .../remote_config/protocol/tuf/serializer.cpp | 171 --- .../remote_config/protocol/tuf/serializer.hpp | 19 - appsec/src/helper/remote_config/settings.hpp | 46 +- appsec/src/helper/runner.cpp | 91 ++ appsec/src/helper/runner.hpp | 10 +- appsec/src/helper/service.cpp | 29 +- appsec/src/helper/service.hpp | 32 +- appsec/src/helper/service_identifier.hpp | 55 - appsec/src/helper/service_manager.cpp | 42 +- appsec/src/helper/service_manager.hpp | 51 +- appsec/src/helper/subscriber/base.hpp | 8 +- appsec/src/helper/subscriber/waf.cpp | 28 +- appsec/src/helper/subscriber/waf.hpp | 13 +- appsec/src/helper/utils.hpp | 18 + .../tests/extension/report_backtrace_01.phpt | 2 +- .../extension/rinit_agent_host_port_01.phpt | 30 - .../extension/rinit_agent_host_port_02.phpt | 27 - .../extension/rinit_agent_host_port_03.phpt | 27 - .../extension/rinit_agent_host_port_04.phpt | 32 - .../extension/rinit_agent_host_port_05.phpt | 30 - .../extension/rinit_agent_host_port_06.phpt | 29 - .../extension/rinit_agent_host_port_07.phpt | 29 - .../extension/rinit_agent_host_port_08.phpt | 30 - .../extension/rinit_rshutdown_basic.phpt | Bin 5562 -> 4867 bytes appsec/tests/helper/CMakeLists.txt | 4 + appsec/tests/helper/broker_test.cpp | 47 +- appsec/tests/helper/client_test.cpp | 31 +- appsec/tests/helper/engine_test.cpp | 208 +-- .../remote_config/client_handler_test.cpp | 331 ---- .../helper/remote_config/client_test.cpp | 1348 ----------------- .../listeners/asm_features_listener_test.cpp | 33 +- .../asm_aggregator_test.cpp | 135 +- .../asm_data_aggregator_test.cpp | 51 +- .../asm_dd_aggregator_test.cpp | 12 +- .../listeners/engine_listener_test.cpp | 100 +- appsec/tests/helper/remote_config/mocks.cpp | 41 + appsec/tests/helper/remote_config/mocks.hpp | 28 +- .../helper/remote_config/parser_test.cpp | 1227 --------------- .../helper/remote_config/product_test.cpp | 262 ---- .../helper/remote_config/serializer_test.cpp | 351 ----- appsec/tests/helper/service_manager_test.cpp | 43 +- appsec/tests/helper/service_test.cpp | 78 +- appsec/tests/helper/waf_test.cpp | 36 +- appsec/tests/integration/build.gradle | 1 + .../src/docker/php/Dockerfile-php-deps | 5 +- .../appsec/php/docker/AppSecContainer.groovy | 12 + .../php/mock_agent/ConfigV07Handler.groovy | 44 +- .../php/mock_agent/MockDatadogAgent.groovy | 12 +- .../php/mock_agent/TracesV04Handler.groovy | 4 +- .../php/mock_agent/rem_cfg/Capability.groovy | 51 + .../rem_cfg/RemoteConfigRequest.java | 30 +- .../rem_cfg/RemoteConfigResponse.java | 38 +- .../php/mock_agent/rem_cfg/Target.groovy | 14 + .../src/test/bin/enable_extensions.sh | 1 + .../appsec/php/integration/CommonTests.groovy | 2 +- .../php/integration/NginxFpmTests.groovy | 2 - .../php/integration/RemoteConfigTests.groovy | 404 +++++ .../src/test/www/base/public/change_env.php | 7 + .../src/test/www/laravel8x/initialize.sh | 2 + .../src/test/www/symfony62/initialize.sh | 2 + components-rs/ddtrace.h | 4 +- components-rs/remote_config.rs | 274 +++- ddtrace.sym | 7 + dockerfiles/verify_packages/verify.sh | 2 + ext/ddtrace.c | 6 +- ext/remote_config.c | 7 + ext/remote_config.h | 1 + ext/sidecar.c | 19 +- .../error_get_last_is_unaffected.phpt | 1 + tests/ext/extension_no_static_tls.phpt | 2 +- .../live-debugger/exception-replay_001.phpt | 2 +- .../live-debugger/exception-replay_002.phpt | 2 +- ...errors_are_ignored_in_tracing_closure.phpt | 1 + tests/ext/sandbox/exception_is_defined.phpt | 1 + ...errors_are_ignored_in_tracing_closure.phpt | 1 + .../fatal_errors_ignored_in_shutdown.phpt | 1 + ...tal_errors_ignored_in_tracing_closure.phpt | 1 + 138 files changed, 2348 insertions(+), 6458 deletions(-) create mode 100644 appsec/src/helper/remote_config/config.cpp delete mode 100644 appsec/src/helper/remote_config/http_api.cpp delete mode 100644 appsec/src/helper/remote_config/http_api.hpp delete mode 100644 appsec/src/helper/remote_config/product.cpp delete mode 100644 appsec/src/helper/remote_config/protocol/cached_target_file_hash.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/cached_target_files.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/client.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/client_state.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/client_tracer.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/config_state.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/path.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/target_file.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/targets.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/tuf/get_configs_request.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/tuf/get_configs_response.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/tuf/info_response.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/tuf/parser.cpp delete mode 100644 appsec/src/helper/remote_config/protocol/tuf/parser.hpp delete mode 100644 appsec/src/helper/remote_config/protocol/tuf/serializer.cpp delete mode 100644 appsec/src/helper/remote_config/protocol/tuf/serializer.hpp delete mode 100644 appsec/src/helper/service_identifier.hpp delete mode 100644 appsec/tests/extension/rinit_agent_host_port_01.phpt delete mode 100644 appsec/tests/extension/rinit_agent_host_port_02.phpt delete mode 100644 appsec/tests/extension/rinit_agent_host_port_03.phpt delete mode 100644 appsec/tests/extension/rinit_agent_host_port_04.phpt delete mode 100644 appsec/tests/extension/rinit_agent_host_port_05.phpt delete mode 100644 appsec/tests/extension/rinit_agent_host_port_06.phpt delete mode 100644 appsec/tests/extension/rinit_agent_host_port_07.phpt delete mode 100644 appsec/tests/extension/rinit_agent_host_port_08.phpt delete mode 100644 appsec/tests/helper/remote_config/client_handler_test.cpp delete mode 100644 appsec/tests/helper/remote_config/client_test.cpp create mode 100644 appsec/tests/helper/remote_config/mocks.cpp delete mode 100644 appsec/tests/helper/remote_config/parser_test.cpp delete mode 100644 appsec/tests/helper/remote_config/product_test.cpp delete mode 100644 appsec/tests/helper/remote_config/serializer_test.cpp create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Capability.groovy create mode 100644 appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Target.groovy create mode 100644 appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RemoteConfigTests.groovy create mode 100644 appsec/tests/integration/src/test/www/base/public/change_env.php diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 26a217d3f6..39ac8aa2fc 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -1412,8 +1412,13 @@ jobs: export DEBIAN_FRONTEND=noninteractive apt update apt install -y wget sudo git g++ gcc gcovr cmake make curl libcurl4-gnutls-dev clang clang-tidy clang-format git php-dev php8.2-xml php-cgi - curl https://sh.rustup.rs -sSf | sh -s -- --profile minimal -y --default-toolchain "1.76.0" - echo 'export PATH=${PATH}:$HOME/.cargo/bin' >> $BASH_ENV + - run: + name: Install rust + command: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rustup.sh + chmod +x /tmp/rustup.sh + /tmp/rustup.sh -y --default-toolchain 1.76 + sudo ln -s $HOME/.cargo/bin/* /usr/bin/ - run: git config --global --add safe.directory /home/circleci/datadog/appsec/third_party/libddwaf - run: name: CMake @@ -1424,7 +1429,7 @@ jobs: - run: name: Test command: | - make -C appsec/build -j $(nproc) xtest ddappsec_helper_test + PATH=$PATH:$HOME/.cargo/bin make -C appsec/build -j $(nproc) xtest ddappsec_helper_test ./appsec/build/tests/helper/ddappsec_helper_test - run: name: Generate XML coverage @@ -3857,7 +3862,7 @@ jobs: php datadog-setup.php --file "${installable_bundle}" --php-bin php --enable-profiling # run phpize just to get run-tests.php phpize - php run-tests.php -p $(which php) --show-diff -g "FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP" tests/ext/profiling + php run-tests.php -p $(which php) -d datadog.remote_config_enabled=false --show-diff -g "FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP" tests/ext/profiling "cbindgen up-to-date": working_directory: ~/datadog diff --git a/appsec/cmake/helper.cmake b/appsec/cmake/helper.cmake index 7846afb5b6..eb265cdcba 100644 --- a/appsec/cmake/helper.cmake +++ b/appsec/cmake/helper.cmake @@ -20,7 +20,7 @@ set_target_properties(helper_objects PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES POSITION_INDEPENDENT_CODE 1) -target_include_directories(helper_objects PUBLIC ${HELPER_INCLUDE_DIR}) +target_include_directories(helper_objects INTERFACE ${HELPER_INCLUDE_DIR}) target_compile_definitions(helper_objects PUBLIC SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) target_compile_options(helper_objects PRIVATE -ftls-model=global-dynamic) target_link_libraries(helper_objects PUBLIC libddwaf_objects pthread spdlog cpp-base64 msgpack_c RapidJSON::rapidjson Boost::system zlibstatic) @@ -35,6 +35,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") # Bind symbols lookup of symbols defined in the library to the library itself # also avoids relocation problems with libc++.a on linux/aarch64 target_link_options(ddappsec-helper PRIVATE -Wl,-Bsymbolic) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + target_link_options(ddappsec-helper PRIVATE -undefined dynamic_lookup) endif() set_target_properties(ddappsec-helper PROPERTIES CXX_VISIBILITY_PRESET hidden diff --git a/appsec/src/extension/backtrace.c b/appsec/src/extension/backtrace.c index 0009583c66..8d5e5c8e9f 100644 --- a/appsec/src/extension/backtrace.c +++ b/appsec/src/extension/backtrace.c @@ -48,7 +48,8 @@ php_backtrace_frame_to_datadog_backtrace_frame( // NOLINTNEXTLINE(bugprone-easil if (file) { // In order to be able to test full path encoded everywhere lets set // only the file name without path - char *file_name = memrchr(Z_STRVAL_P(file), '/', Z_STRLEN_P(file)); + const char *file_name = + zend_memrchr(Z_STRVAL_P(file), '/', Z_STRLEN_P(file)); if (file_name) { zend_string *new_file = zend_string_init(file_name + 1, Z_STRLEN_P(file) - (file_name + 1 - Z_STRVAL_P(file)), 0); diff --git a/appsec/src/extension/commands/client_init.c b/appsec/src/extension/commands/client_init.c index 1329561a67..a20513129d 100644 --- a/appsec/src/extension/commands/client_init.c +++ b/appsec/src/extension/commands/client_init.c @@ -17,68 +17,20 @@ #include "../version.h" #include "client_init.h" -static const unsigned int DEFAULT_AGENT_PORT = 8126; -static const char *DEFAULT_AGENT_HOST = "127.0.0.1"; -static const unsigned int MAX_TCP_PORT_ALLOWED = UINT16_MAX; - static dd_result _pack_command(mpack_writer_t *nonnull w, void *nullable ctx); static dd_result _process_response(mpack_node_t root, void *nullable ctx); static void _process_meta_and_metrics( mpack_node_t root, struct req_info *nonnull ctx); -static void _pack_agent_details(mpack_writer_t *nonnull w); static const dd_command_spec _spec = { .name = "client_init", .name_len = sizeof("client_init") - 1, - .num_args = 7, + .num_args = 6, .outgoing_cb = _pack_command, .incoming_cb = _process_response, .config_features_cb = dd_command_process_config_features_unexpected, }; -static void _pack_agent_details(mpack_writer_t *nonnull w) -{ - zend_string *agent_host = get_global_DD_AGENT_HOST(); - zend_string *agent_url = get_global_DD_TRACE_AGENT_URL(); - unsigned int port = get_global_DD_TRACE_AGENT_PORT(); - char *host = NULL; - php_url *parsed_url = NULL; - - if (agent_host && ZSTR_LEN(agent_host) > 0) { - host = ZSTR_VAL(agent_host); - } else if (agent_url && ZSTR_LEN(agent_url) > 0) { - parsed_url = php_url_parse(ZSTR_VAL(agent_url)); - if (parsed_url) { -#if PHP_VERSION_ID < 70300 - if (parsed_url->host && strlen(parsed_url->host) > 0) { - host = parsed_url->host; - } -#else - if (parsed_url->host && ZSTR_LEN(parsed_url->host) > 0) { - host = ZSTR_VAL(parsed_url->host); - } -#endif - port = parsed_url->port; - } - } - - if (!host) { - host = (char *)DEFAULT_AGENT_HOST; - } - if (port <= 0 || port > MAX_TCP_PORT_ALLOWED) { - port = DEFAULT_AGENT_PORT; - } - - dd_mpack_write_lstr(w, "host"); - dd_mpack_write_nullable_cstr(w, host); - dd_mpack_write_lstr(w, "port"); - mpack_write_uint(w, port); - - if (parsed_url) { - php_url_free(parsed_url); - } -} - dd_result dd_client_init(dd_conn *nonnull conn, struct req_info *nonnull ctx) { return dd_command_exec_cred(conn, &_spec, ctx); @@ -97,39 +49,6 @@ static dd_result _pack_command( mpack_write_bool(w, DDAPPSEC_G(active)); } - // Service details - // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) - mpack_start_map(w, 6); - - dd_mpack_write_lstr(w, "service"); - dd_mpack_write_nullable_cstr(w, ZSTR_VAL(get_DD_SERVICE())); - - dd_mpack_write_lstr(w, "extra_services"); - zval extra_services; - ZVAL_ARR(&extra_services, get_global_DD_EXTRA_SERVICES()); - dd_mpack_write_zval(w, &extra_services); - - dd_mpack_write_lstr(w, "env"); - dd_mpack_write_nullable_cstr(w, ZSTR_VAL(get_DD_ENV())); - - dd_mpack_write_lstr(w, "tracer_version"); - dd_mpack_write_nullable_cstr(w, dd_trace_version()); - - dd_mpack_write_lstr(w, "app_version"); - dd_mpack_write_nullable_cstr(w, ZSTR_VAL(get_DD_VERSION())); - - // We send this empty for now. The helper will check for empty and if so it - // will generate it - dd_mpack_write_lstr(w, "runtime_id"); - zend_string *runtime_id = dd_trace_get_formatted_runtime_id(false); - if (runtime_id == NULL) { - dd_mpack_write_nullable_cstr(w, ""); - } else { - dd_mpack_write_nullable_zstr(w, runtime_id); - zend_string_free(runtime_id); - } - mpack_finish_map(w); - // Engine settings // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) mpack_start_map(w, 6); @@ -180,15 +99,13 @@ static dd_result _pack_command( // Remote config settings // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) - mpack_start_map(w, 4); + mpack_start_map(w, 2); dd_mpack_write_lstr(w, "enabled"); mpack_write_bool(w, get_DD_REMOTE_CONFIG_ENABLED()); - _pack_agent_details(w); - - dd_mpack_write_lstr(w, "poll_interval"); - mpack_write_u32(w, get_DD_REMOTE_CONFIG_POLL_INTERVAL()); + dd_mpack_write_lstr(w, "shmem_path"); + dd_mpack_write_nullable_cstr(w, dd_trace_remote_config_get_path()); mpack_finish_map(w); diff --git a/appsec/src/extension/commands/config_sync.c b/appsec/src/extension/commands/config_sync.c index 1938561d5e..c7fcbddd58 100644 --- a/appsec/src/extension/commands/config_sync.c +++ b/appsec/src/extension/commands/config_sync.c @@ -8,32 +8,36 @@ #include #include "../commands_helpers.h" +#include "../ddtrace.h" +#include "../msgpack_helpers.h" +#include "config_sync.h" #include -static dd_result _request_pack( - mpack_writer_t *nonnull w, void *nullable ATTR_UNUSED ctx); +static dd_result _request_pack(mpack_writer_t *nonnull w, void *nonnull ctx); dd_result dd_command_process_config_sync( mpack_node_t root, ATTR_UNUSED void *unspecnull ctx); static const dd_command_spec _spec = { .name = "config_sync", .name_len = sizeof("config_sync") - 1, - .num_args = 0, // a single map + .num_args = 1, .outgoing_cb = _request_pack, .incoming_cb = dd_command_process_config_sync, .config_features_cb = dd_command_process_config_features, }; -dd_result dd_config_sync(dd_conn *nonnull conn) +dd_result dd_config_sync( + dd_conn *nonnull conn, const struct config_sync_data *nonnull data) { - return dd_command_exec(conn, &_spec, NULL); + return dd_command_exec(conn, &_spec, (void *)data); } -static dd_result _request_pack( - mpack_writer_t *nonnull w, void *nullable ATTR_UNUSED ctx) +static dd_result _request_pack(mpack_writer_t *nonnull w, void *nonnull ctx_) { - UNUSED(ctx); - UNUSED(w); + const struct config_sync_data *nonnull data = + (struct config_sync_data *)ctx_; + + dd_mpack_write_nullable_cstr(w, data->rem_cfg_path); return dd_success; } diff --git a/appsec/src/extension/commands/config_sync.h b/appsec/src/extension/commands/config_sync.h index 95a6ae39a5..eeb8976485 100644 --- a/appsec/src/extension/commands/config_sync.h +++ b/appsec/src/extension/commands/config_sync.h @@ -7,4 +7,9 @@ #include "../network.h" -dd_result dd_config_sync(dd_conn *nonnull conn); +struct config_sync_data { + char *nullable rem_cfg_path; +}; + +dd_result dd_config_sync( + dd_conn *nonnull conn, const struct config_sync_data *nonnull data); diff --git a/appsec/src/extension/ddappsec.c b/appsec/src/extension/ddappsec.c index 26fbaa6b2b..eb840d940a 100644 --- a/appsec/src/extension/ddappsec.c +++ b/appsec/src/extension/ddappsec.c @@ -45,6 +45,7 @@ #include "user_tracking.h" #include +#include #if ZTS static atomic_int _thread_count; @@ -100,7 +101,7 @@ static zend_extension ddappsec_extension_entry = { PHP_DDAPPSEC_EXTNAME, PHP_DDAPPSEC_VERSION, "Datadog", - "https://github.com/DataDog/dd-appsec-php", + "https://github.com/DataDog/dd-trace-php", "Copyright Datadog", ddappsec_startup, NULL, @@ -253,6 +254,21 @@ void dd_appsec_rinit_once() pthread_once(&_rinit_once_control, _rinit_once); } +static void _warn_on_empty_service_or_env() +{ + if (!get_global_DD_APPSEC_TESTING() && get_DD_REMOTE_CONFIG_ENABLED() && + DDAPPSEC_G(enabled) != APPSEC_FULLY_DISABLED && + (zend_string_equals_literal(get_DD_ENV(), "") || + zend_string_equals_literal(get_DD_SERVICE(), ""))) { + mlog(dd_log_warning, + "AppSec is not disabled and Datadog service or env is empty. " + "Please set DD_SERVICE and DD_ENV rather than setting the " + "corresponding properties on the root span. Otherwise, remote " + "configuration for AppSec will use service=unnamed-php-service and " + "env=none"); + } +} + // NOLINTNEXTLINE static PHP_RINIT_FUNCTION(ddappsec) { @@ -265,6 +281,7 @@ static PHP_RINIT_FUNCTION(ddappsec) dd_appsec_rinit_once(); zai_config_rinit(); _check_enabled(); + _warn_on_empty_service_or_env(); if (DDAPPSEC_G(enabled) == APPSEC_FULLY_DISABLED) { return SUCCESS; @@ -378,6 +395,23 @@ static void _check_enabled() }; } +__attribute__((visibility("default"))) void dd_appsec_rc_conf( + bool *nonnull appsec_features, bool *nonnull appsec_conf) // NOLINT +{ + bool prev_enabled = DDAPPSEC_G(enabled); + bool prev_active = DDAPPSEC_G(active); + bool prev_to_be_configured = DDAPPSEC_G(to_be_configured); + _check_enabled(); + + *appsec_features = DDAPPSEC_G(enabled) == APPSEC_ENABLED_VIA_REMCFG; + // only enable ASM / ASM_DD / ASM_DATA if no rules file is specified + *appsec_conf = get_global_DD_APPSEC_RULES()->len == 0; + + DDAPPSEC_G(enabled) = prev_enabled; + DDAPPSEC_G(active) = prev_active; + DDAPPSEC_G(to_be_configured) = prev_to_be_configured; +} + static PHP_FUNCTION(datadog_appsec_is_enabled) { if (zend_parse_parameters_none() == FAILURE) { diff --git a/appsec/src/extension/ddappsec.h b/appsec/src/extension/ddappsec.h index e019a2850b..7983ca2d4b 100644 --- a/appsec/src/extension/ddappsec.h +++ b/appsec/src/extension/ddappsec.h @@ -56,6 +56,9 @@ extern __thread void *unspecnull ATTR_TLS_LOCAL_DYNAMIC TSRMLS_CACHE; void dd_appsec_rinit_once(void); int dd_appsec_rshutdown(bool ignore_verdict); +__attribute__((visibility("default"))) void dd_appsec_rc_conf( + bool *nonnull appsec_features, bool *nonnull appsec_conf); // NOLINT + // Add a NO_CACHE version. // Use tsrm_get_ls_cache() instead of thread-local _tsrmls_ls_cache #ifdef ZTS diff --git a/appsec/src/extension/ddappsec.version b/appsec/src/extension/ddappsec.version index eb4dab81b2..7ab9abb2c3 100644 --- a/appsec/src/extension/ddappsec.version +++ b/appsec/src/extension/ddappsec.version @@ -2,5 +2,6 @@ global: get_module; dd_appsec_maybe_enable_helper; + dd_appsec_rc_conf; local: *; }; diff --git a/appsec/src/extension/ddtrace.c b/appsec/src/extension/ddtrace.c index d9dd88c052..36fb0da7fd 100644 --- a/appsec/src/extension/ddtrace.c +++ b/appsec/src/extension/ddtrace.c @@ -45,6 +45,8 @@ static bool (*nullable _ddtrace_user_req_add_listeners)( static zend_string *(*_ddtrace_ip_extraction_find)(zval *server); +static const char *nullable (*_ddtrace_remote_config_get_path)(void); + static void dd_trace_load_symbols(void) { bool testing = get_global_DD_APPSEC_TESTING(); @@ -98,6 +100,14 @@ static void dd_trace_load_symbols(void) dlerror()); // NOLINT(concurrency-mt-unsafe) } + _ddtrace_remote_config_get_path = + dlsym(handle, "ddtrace_remote_config_get_path"); + if (_ddtrace_remote_config_get_path == NULL && !testing) { + mlog(dd_log_error, + // NOLINTNEXTLINE(concurrency-mt-unsafe) + "Failed to load ddtrace_remote_config_get_path: %s", dlerror()); + } + dlclose(handle); } @@ -358,6 +368,16 @@ zend_string *nullable dd_ip_extraction_find(zval *nonnull server) return _ddtrace_ip_extraction_find(server); } +const char *nullable dd_trace_remote_config_get_path() +{ + if (!_ddtrace_remote_config_get_path) { + return NULL; + } + __auto_type path = _ddtrace_remote_config_get_path(); + mlog(dd_log_trace, "Remote config path: %s", path ? path : "(unset)"); + return path; +} + static PHP_FUNCTION(datadog_appsec_testing_ddtrace_rshutdown) { if (zend_parse_parameters_none() == FAILURE) { diff --git a/appsec/src/extension/ddtrace.h b/appsec/src/extension/ddtrace.h index 9e634b6fec..4a8a6ca624 100644 --- a/appsec/src/extension/ddtrace.h +++ b/appsec/src/extension/ddtrace.h @@ -79,3 +79,5 @@ bool dd_trace_user_req_add_listeners( ddtrace_user_req_listeners *nonnull listeners); zend_string *nullable dd_ip_extraction_find(zval *nonnull server); + +const char *nullable dd_trace_remote_config_get_path(void); diff --git a/appsec/src/extension/helper_process.c b/appsec/src/extension/helper_process.c index 47f6ed9f7c..8b15e64a2e 100644 --- a/appsec/src/extension/helper_process.c +++ b/appsec/src/extension/helper_process.c @@ -129,9 +129,8 @@ dd_conn *nullable dd_helper_mgr_cur_conn(void) return NULL; } -bool dd_on_runtime_path_update( - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - zval *nullable old_val, zval *nonnull new_val, +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +bool dd_on_runtime_path_update(zval *nullable old_val, zval *nonnull new_val, zend_string *nullable new_str) { UNUSED(old_val); diff --git a/appsec/src/extension/helper_process.h b/appsec/src/extension/helper_process.h index c21487f07f..9534bfdecf 100644 --- a/appsec/src/extension/helper_process.h +++ b/appsec/src/extension/helper_process.h @@ -23,11 +23,11 @@ void dd_helper_gshutdown(void); void dd_helper_rshutdown(void); typedef dd_result (*client_init_func)(dd_conn *nonnull, void *unspecnull ctx); -dd_conn *nullable dd_helper_mgr_acquire_conn(client_init_func nonnull, void *unspecnull ctx); +dd_conn *nullable dd_helper_mgr_acquire_conn( + client_init_func nonnull, void *unspecnull ctx); dd_conn *nullable dd_helper_mgr_cur_conn(void); void dd_helper_close_conn(void); -// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) bool dd_on_runtime_path_update(zval *nullable old_value, zval *nonnull new_value, zend_string *nullable new_str); diff --git a/appsec/src/extension/msgpack_helpers.c b/appsec/src/extension/msgpack_helpers.c index 535690cff1..69d90c60f2 100644 --- a/appsec/src/extension/msgpack_helpers.c +++ b/appsec/src/extension/msgpack_helpers.c @@ -292,9 +292,10 @@ static bool parse_element( case mpack_type_int: ZVAL_LONG(output, mpack_tag_int_value(&tag)); break; - case mpack_type_uint: - ZVAL_LONG(output, mpack_tag_int_value(&tag)); + case mpack_type_uint: { + ZVAL_LONG(output, (long)mpack_tag_uint_value(&tag)); break; + } case mpack_type_str: { uint32_t length = mpack_tag_str_length(&tag); diff --git a/appsec/src/extension/request_lifecycle.c b/appsec/src/extension/request_lifecycle.c index 0ed9d1d7d8..3aa826d903 100644 --- a/appsec/src/extension/request_lifecycle.c +++ b/appsec/src/extension/request_lifecycle.c @@ -47,6 +47,9 @@ static THREAD_LOCAL_ON_ZTS zend_array *nullable _superglob_equiv; static THREAD_LOCAL_ON_ZTS zend_string *nullable _client_ip; static THREAD_LOCAL_ON_ZTS zval _blocking_function; static THREAD_LOCAL_ON_ZTS bool _shutdown_done_on_commit; +#define MAX_LENGTH_OF_REM_CFG_PATH 31 +static THREAD_LOCAL_ON_ZTS char + _last_rem_cfg_path[MAX_LENGTH_OF_REM_CFG_PATH + 1]; #define CLIENT_IP_LOOKUP_FAILED ((zend_string *)-1) bool dd_req_is_user_req() { return _enabled_user_req; } @@ -125,6 +128,31 @@ static zend_array *nullable _do_request_begin_user_req(zval *nullable rbe_zv) return _do_request_begin(rbe_zv, true); } +static bool _rem_cfg_path_changed() +{ + const char *cur_path = dd_trace_remote_config_get_path(); + if (!cur_path) { + cur_path = ""; + } + if (strcmp(cur_path, _last_rem_cfg_path) == 0) { + return false; + } + + if (strlen(cur_path) > MAX_LENGTH_OF_REM_CFG_PATH) { + mlog(dd_log_warning, "Remote config path too long: %s", cur_path); + return false; + } + + mlog(dd_log_info, "Remote config path changed from %s to %s", + _last_rem_cfg_path[0] ? _last_rem_cfg_path : "(none)", + cur_path[0] ? cur_path : "(none)"); + + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) + strcpy(_last_rem_cfg_path, cur_path); + + return true; +} + static zend_array *nullable _do_request_begin( zval *nullable rbe_zv /* needs free */, bool user_req) { @@ -156,16 +184,17 @@ static zend_array *nullable _do_request_begin( } int res = dd_success; - if (DDAPPSEC_G(active)) { - // request_init - res = dd_request_init(conn, &req_info); - } else if (DDAPPSEC_G(enabled) == APPSEC_ENABLED_VIA_REMCFG) { - // config_sync - res = dd_config_sync(conn); - if (res == SUCCESS && DDAPPSEC_G(active)) { - // Since it came as enabled, lets proceed + if (_rem_cfg_path_changed() || + (!DDAPPSEC_G(active) && + DDAPPSEC_G(enabled) == APPSEC_ENABLED_VIA_REMCFG)) { + res = dd_config_sync(conn, + &(struct config_sync_data){.rem_cfg_path = _last_rem_cfg_path}); + if (res == dd_success && DDAPPSEC_G(active)) { res = dd_request_init(conn, &req_info); } + } else if (DDAPPSEC_G(active)) { + // request_init + res = dd_request_init(conn, &req_info); } if (rbe) { diff --git a/appsec/src/helper/client.cpp b/appsec/src/helper/client.cpp index 078dffd224..17b9470176 100644 --- a/appsec/src/helper/client.cpp +++ b/appsec/src/helper/client.cpp @@ -5,6 +5,7 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include +#include #include #include #include @@ -146,12 +147,11 @@ bool handle_message(client &client, const network::base_broker &broker, bool client::handle_command(const network::client_init::request &command) { SPDLOG_DEBUG("Got client_id with pid={}, client_version={}, " - "runtime_version={}, service={}, engine_settings={}, " + "runtime_version={}, engine_settings={}, " "remote_config_settings={}", command.pid, command.client_version, command.runtime_version, - command.service, command.engine_settings, command.rc_settings); + command.engine_settings, command.rc_settings); - auto service_id = command.service; auto &&eng_settings = command.engine_settings; DD_STDLOG(DD_STDLOG_STARTUP); @@ -162,19 +162,15 @@ bool client::handle_command(const network::client_init::request &command) bool has_errors = false; client_enabled_conf = command.enabled_configuration; - if (service_id.runtime_id.empty()) { - service_id.runtime_id = generate_random_uuid(); - } - runtime_id_ = service_id.runtime_id; try { - service_ = service_manager_->create_service(std::move(service_id), - eng_settings, command.rc_settings, meta, metrics, - !client_enabled_conf.has_value()); - if (service_) { - // This null check is only needed due to some tests - service_->register_runtime_id(runtime_id_); - } + service_ = + service_manager_->create_service(eng_settings, command.rc_settings, + meta, metrics, !client_enabled_conf.has_value()); + + // save engine settings so we can recreate the service if rc path + // changes + engine_settings_ = eng_settings; } catch (std::system_error &e) { // TODO: logging should happen at WAF impl DD_STDLOG(DD_STDLOG_RULES_FILE_NOT_FOUND, @@ -340,13 +336,22 @@ bool client::compute_client_status() return request_enabled_; } -bool client::handle_command(network::config_sync::request & /* command */) +bool client::handle_command(network::config_sync::request &command) { if (!service_guard()) { return false; } - SPDLOG_DEBUG("received command config_sync"); + SPDLOG_DEBUG( + "received command config_sync with path {}", command.rem_cfg_path); + + std::map meta; + std::map metrics; + update_remote_config_path(command.rem_cfg_path, meta, metrics); + + // TODO: meta/metrics not transmitted fwd + // wait for new interface; see + // https://github.com/DataDog/dd-trace-php/pull/2735 if (compute_client_status()) { auto response_cf = @@ -363,7 +368,7 @@ bool client::handle_command(network::config_sync::request & /* command */) return true; } - SPDLOG_DEBUG("sending config_sync to config_sync"); + SPDLOG_DEBUG("sending response to config_sync"); try { return broker_->send( std::make_shared()); @@ -434,6 +439,24 @@ bool client::handle_command(network::request_shutdown::request &command) return send_message(response); } +void client::update_remote_config_path(std::string_view path, + std::map &meta, + std::map &metrics) +{ + if (service_->is_remote_config_shmem_path(path) || + !engine_settings_.has_value()) { + return; + } + + SPDLOG_INFO("Remote config path changed to {}, recreating service", path); + remote_config::settings rc_settings; + rc_settings.enabled = true; + rc_settings.shmem_path = path; + + service_ = service_manager_->create_service( + *engine_settings_, rc_settings, meta, metrics, true); +} + bool client::run_client_init() { static constexpr auto client_init_timeout{std::chrono::milliseconds{500}}; @@ -457,12 +480,6 @@ bool client::run_request() void client::run(worker::queue_consumer &q) { - const defer on_exit{[this]() { - if (this->service_) { - this->service_->unregister_runtime_id(this->runtime_id_); - } - }}; - if (q.running()) { if (!run_client_init()) { SPDLOG_DEBUG("Finished handling client (client_init failed)"); diff --git a/appsec/src/helper/client.hpp b/appsec/src/helper/client.hpp index 81722deb63..aa5f8a15b7 100644 --- a/appsec/src/helper/client.hpp +++ b/appsec/src/helper/client.hpp @@ -61,6 +61,10 @@ class client { void run(worker::queue_consumer &q); bool compute_client_status(); + void update_remote_config_path(std::string_view path, + std::map &meta, + std::map &metrics); + protected: template std::shared_ptr publish(typename T::request &command); @@ -71,11 +75,11 @@ class client { uint32_t version{}; network::base_broker::ptr broker_; std::shared_ptr service_manager_; + std::optional engine_settings_; std::shared_ptr service_ = {nullptr}; std::optional context_; std::optional client_enabled_conf; bool request_enabled_ = {false}; - std::string runtime_id_; }; } // namespace dds diff --git a/appsec/src/helper/engine.cpp b/appsec/src/helper/engine.cpp index 52d5f7c1b7..e9b6fcabfb 100644 --- a/appsec/src/helper/engine.cpp +++ b/appsec/src/helper/engine.cpp @@ -4,9 +4,7 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include -#include -#include -#include +#include #include #include "engine.hpp" @@ -19,17 +17,16 @@ namespace dds { -void engine::subscribe(const subscriber::ptr &sub) +void engine::subscribe(std::unique_ptr sub) { - auto common = std::atomic_load(&common_); - common->subscribers.emplace_back(sub); + common_->subscribers.emplace_back(std::move(sub)); } void engine::update(engine_ruleset &ruleset, std::map &meta, std::map &metrics) { - std::vector new_subscribers; + std::vector> new_subscribers; new_subscribers.reserve(common_->subscribers.size()); dds::parameter param = json_to_parameter(ruleset.get_document()); for (auto &sub : common_->subscribers) { @@ -38,18 +35,17 @@ void engine::update(engine_ruleset &ruleset, } catch (const std::exception &e) { SPDLOG_WARN("Failed to update subscriber {}: {}", sub->get_name(), e.what()); - new_subscribers.emplace_back(sub); + return; // no partial updates } catch (...) { SPDLOG_WARN("Failed to update subscriber {}: unknown reason", sub->get_name()); - new_subscribers.emplace_back(sub); + return; } } - std::shared_ptr const new_common( - new shared_state{std::move(new_subscribers)}); - - std::atomic_store(&common_, new_common); + auto new_common = std::make_shared( + shared_state{std::move(new_subscribers)}); + std::atomic_store_explicit(&common_, new_common, std::memory_order_release); } std::optional engine::context::publish(parameter &¶m) @@ -70,12 +66,18 @@ std::optional engine::context::publish(parameter &¶m) event event_; for (auto &sub : common_->subscribers) { - auto it = listeners_.find(sub); + auto it = listeners_.find(sub.get()); if (it == listeners_.end()) { - it = listeners_.emplace(sub, sub->get_listener()).first; + auto listener = sub->get_listener(); + assert(listener.get() != nullptr); + auto &&[iterator, inserted] = + listeners_.emplace(sub.get(), std::move(listener)); + assert(inserted == true); + it = iterator; } try { - it->second->call(data, event_); + const auto &listener = it->second; + listener->call(data, event_); } catch (std::exception &e) { SPDLOG_ERROR("subscriber failed: {}", e.what()); } @@ -116,21 +118,22 @@ void engine::context::get_meta_and_metrics( } } -engine::ptr engine::from_settings(const dds::engine_settings &eng_settings, +std::unique_ptr engine::from_settings( + const dds::engine_settings &eng_settings, std::map &meta, std::map &metrics) - { auto &&rules_path = eng_settings.rules_file_or_default(); auto ruleset = engine_ruleset::from_path(rules_path); - std::shared_ptr engine_ptr{engine::create(eng_settings.trace_rate_limit)}; + std::unique_ptr engine_ptr{ + engine::create(eng_settings.trace_rate_limit)}; try { SPDLOG_DEBUG("Will load WAF rules from {}", rules_path); // may throw std::exception - const subscriber::ptr waf = + auto waf = waf::instance::from_settings(eng_settings, ruleset, meta, metrics); - engine_ptr->subscribe(waf); + engine_ptr->subscribe(std::move(waf)); } catch (...) { DD_STDLOG(DD_STDLOG_WAF_INIT_FAILED, rules_path); throw; diff --git a/appsec/src/helper/engine.hpp b/appsec/src/helper/engine.hpp index b42600a59b..a42797e8db 100644 --- a/appsec/src/helper/engine.hpp +++ b/appsec/src/helper/engine.hpp @@ -12,6 +12,7 @@ #include "parameter.hpp" #include "rate_limit.hpp" #include "subscriber/base.hpp" +#include #include #include #include @@ -34,10 +35,6 @@ namespace dds { **/ class engine { public: - using ptr = std::shared_ptr; - using subscription_map = - std::map>; - using action_map = std::unordered_map; struct result { @@ -48,7 +45,7 @@ class engine { protected: struct shared_state { - std::vector subscribers; + std::vector> subscribers; }; public: @@ -58,8 +55,9 @@ class engine { class context { public: explicit context(engine &engine) - : common_(std::atomic_load(&engine.common_)), - limiter_(engine.limiter_) + : common_{std::atomic_load_explicit( + &engine.common_, std::memory_order_acquire)}, + limiter_{engine.limiter_} {} context(const context &) = delete; context &operator=(const context &) = delete; @@ -73,10 +71,12 @@ class engine { std::map &metrics); protected: - std::vector prev_published_params_; - std::map listeners_; std::shared_ptr common_; - rate_limiter &limiter_; + std::map> + listeners_; + std::vector prev_published_params_; + rate_limiter & + limiter_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) }; engine(const engine &) = delete; @@ -85,30 +85,32 @@ class engine { engine &operator=(engine &&) = delete; virtual ~engine() = default; - static engine::ptr from_settings(const dds::engine_settings &eng_settings, + static std::unique_ptr from_settings( + const dds::engine_settings &eng_settings, std::map &meta, std::map &metrics); static auto create( uint32_t trace_rate_limit = engine_settings::default_trace_rate_limit) { - return std::shared_ptr(new engine(trace_rate_limit)); + return std::unique_ptr(new engine(trace_rate_limit)); } context get_context() { return context{*this}; } - void subscribe(const subscriber::ptr &sub); - // Update is not thread-safe, although only one remote config client should - // be able to update it so in practice it should not be a problem. + // Not thread-safe, should only be called after construction + void subscribe(std::unique_ptr sub); + virtual void update(engine_ruleset &ruleset, std::map &meta, std::map &metrics); protected: - explicit engine(uint32_t trace_rate_limit, action_map &&actions = {}) + explicit engine(uint32_t trace_rate_limit) : limiter_(trace_rate_limit), common_(new shared_state{{}}) {} + // in practice: the current ddwaf_handle, atomically swapped in update std::shared_ptr common_; rate_limiter limiter_; }; diff --git a/appsec/src/helper/engine_settings.hpp b/appsec/src/helper/engine_settings.hpp index e9bdf91012..b61fc52b41 100644 --- a/appsec/src/helper/engine_settings.hpp +++ b/appsec/src/helper/engine_settings.hpp @@ -39,6 +39,11 @@ struct engine_settings { std::string obfuscator_value_regex; schema_extraction_settings schema_extraction; + engine_settings() = default; + engine_settings(const engine_settings &) = default; + engine_settings(engine_settings &&) = default; + engine_settings &operator=(const engine_settings &) = default; + engine_settings &operator=(engine_settings &&) = default; virtual ~engine_settings() = default; static const std::string &default_rules_file(); @@ -79,14 +84,16 @@ struct engine_settings { << ", schema_extraction.sample_rate=" << std::fixed << c.schema_extraction.sample_rate << "}"; } - - struct settings_hash { - std::size_t operator()(const engine_settings &s) const noexcept - { - return hash(s.rules_file, s.waf_timeout_us, s.trace_rate_limit, - s.obfuscator_key_regex, s.obfuscator_value_regex, - s.schema_extraction.enabled, s.schema_extraction.sample_rate); - } - }; }; } // namespace dds + +namespace std { +template <> struct hash { + std::size_t operator()(const dds::engine_settings &s) const noexcept + { + return dds::hash(s.rules_file, s.waf_timeout_us, s.trace_rate_limit, + s.obfuscator_key_regex, s.obfuscator_value_regex, + s.schema_extraction.enabled, s.schema_extraction.sample_rate); + } +}; +} // namespace std diff --git a/appsec/src/helper/json_helper.cpp b/appsec/src/helper/json_helper.cpp index 45d0c54f8c..9a785ddc7e 100644 --- a/appsec/src/helper/json_helper.cpp +++ b/appsec/src/helper/json_helper.cpp @@ -200,19 +200,10 @@ json_helper::get_field_of_type( return get_field_of_type(*parent_field, key, type); } -bool json_helper::get_json_base64_encoded_content( - const std::string &content, rapidjson::Document &output) +bool json_helper::parse_json( + std::string_view content, rapidjson::Document &output) { - std::string base64_decoded; - try { - base64_decoded = base64_decode(content, true); - } catch (const std::runtime_error &error) { - SPDLOG_DEBUG( - "Invalid base64 encoded content: " + std::string(error.what())); - return false; - } - - if (output.Parse(base64_decoded).HasParseError()) { + if (output.Parse(content.data(), content.size()).HasParseError()) { SPDLOG_DEBUG("Invalid json: " + std::string(rapidjson::GetParseError_En( output.GetParseError()))); return false; diff --git a/appsec/src/helper/json_helper.hpp b/appsec/src/helper/json_helper.hpp index 8124628cbf..42e07c046e 100644 --- a/appsec/src/helper/json_helper.hpp +++ b/appsec/src/helper/json_helper.hpp @@ -63,8 +63,7 @@ std::optional get_field_of_type( std::optional get_field_of_type( rapidjson::Value::ConstValueIterator parent_field, std::string_view key, rapidjson::Type type); -bool get_json_base64_encoded_content( - const std::string &content, rapidjson::Document &output); +bool parse_json(std::string_view content, rapidjson::Document &output); // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) void merge_arrays(rapidjson::Value &destination, rapidjson::Value &source, rapidjson::Value::AllocatorType &allocator); diff --git a/appsec/src/helper/main.cpp b/appsec/src/helper/main.cpp index ec4155704d..68a8f69166 100644 --- a/appsec/src/helper/main.cpp +++ b/appsec/src/helper/main.cpp @@ -9,7 +9,6 @@ #include "config.hpp" #include "runner.hpp" -#include "subscriber/waf.hpp" #include #include #include @@ -111,7 +110,10 @@ int appsec_helper_main_impl() return 1; } - auto runner = std::make_unique(config, interrupted); + dds::remote_config::resolve_symbols(); + dds::runner::resolve_symbols(); + + auto runner = std::make_shared(config, interrupted); SPDLOG_INFO("starting runner on new thread"); std::thread thr{[runner = std::move(runner)]() { #ifdef __linux__ @@ -119,6 +121,8 @@ int appsec_helper_main_impl() #elif defined(__APPLE__) pthread_setname_np("appsec_helper runner"); #endif + runner->register_for_rc_notifications(); + runner->run(); finished.store(true, std::memory_order_release); diff --git a/appsec/src/helper/network/proto.hpp b/appsec/src/helper/network/proto.hpp index 819d321cd2..3c629b4363 100644 --- a/appsec/src/helper/network/proto.hpp +++ b/appsec/src/helper/network/proto.hpp @@ -5,15 +5,14 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "engine_settings.hpp" +#include "../engine_settings.hpp" +#include "../remote_config/settings.hpp" +#include "../version.hpp" #include "msgpack_helpers.hpp" -#include "remote_config/settings.hpp" -#include "service_identifier.hpp" #include #include #include #include -#include using stream_packer = msgpack::packer; @@ -101,7 +100,6 @@ struct client_init { std::string runtime_version; std::optional enabled_configuration; - dds::service_identifier service; dds::engine_settings engine_settings; dds::remote_config::settings rc_settings; @@ -113,7 +111,7 @@ struct client_init { ~request() override = default; MSGPACK_DEFINE(pid, client_version, runtime_version, - enabled_configuration, service, engine_settings, rc_settings); + enabled_configuration, engine_settings, rc_settings); }; struct response : base_response_generic { @@ -223,7 +221,10 @@ struct config_sync { request(request &&) = default; request &operator=(request &&) = default; ~request() override = default; - MSGPACK_DEFINE() + + std::string rem_cfg_path; + + MSGPACK_DEFINE(rem_cfg_path) }; struct response : base_response_generic { diff --git a/appsec/src/helper/rate_limit.hpp b/appsec/src/helper/rate_limit.hpp index c9ac7671b6..7797582852 100644 --- a/appsec/src/helper/rate_limit.hpp +++ b/appsec/src/helper/rate_limit.hpp @@ -12,10 +12,6 @@ #include #include "timer.hpp" -using std::chrono::duration_cast; -using std::chrono::microseconds; -using std::chrono::milliseconds; -using std::chrono::seconds; namespace dds { @@ -25,6 +21,11 @@ template class rate_limiter { : max_per_second_(max_per_second){}; bool allow() { + using std::chrono::duration_cast; + using std::chrono::microseconds; + using std::chrono::milliseconds; + using std::chrono::seconds; + if (max_per_second_ == 0) { return true; } diff --git a/appsec/src/helper/remote_config/client.cpp b/appsec/src/helper/remote_config/client.cpp index 26ab48c948..5b851b4bb3 100644 --- a/appsec/src/helper/remote_config/client.cpp +++ b/appsec/src/helper/remote_config/client.cpp @@ -6,240 +6,216 @@ #include "client.hpp" #include "exception.hpp" #include "product.hpp" -#include "protocol/tuf/parser.hpp" -#include "protocol/tuf/serializer.hpp" #include #include +#include #include -#include +#include +#include + +extern "C" { +#include +} + +namespace { +struct ddog_CharSlice { + const char *ptr; + uintptr_t len; +}; +ddog_RemoteConfigReader *(*ddog_remote_config_reader_for_path)( + const char *path); +bool (*ddog_remote_config_read)( + ddog_RemoteConfigReader *reader, ddog_CharSlice *data); +void (*ddog_remote_config_reader_drop)(struct ddog_RemoteConfigReader *); + +bool sets_are_indentical_for_subbed_products( + const std::unordered_set &products, + const std::set &before, + const std::set &after) +{ + auto set_is_subset_of = [&products](auto &set1, auto &set2) { + for (auto &&elem_set_1 : set1) { // NOLINT(readability-use-anyofallof) + if (!products.contains(elem_set_1.get_product())) { + continue; + } + if (!set2.contains(elem_set_1)) { + return false; + } + } + return true; + }; + + return set_is_subset_of(before, after) && set_is_subset_of(after, before); +} +} // namespace namespace dds::remote_config { -config_path config_path::from_path(const std::string &path) +void resolve_symbols() { - static const std::regex regex( - "^(datadog/\\d+|employee)/([^/]+)/([^/]+)/[^/]+$"); + ddog_remote_config_reader_for_path = + // NOLINTNEXTLINE + reinterpret_cast( + dlsym(RTLD_DEFAULT, "ddog_remote_config_reader_for_path")); + if (ddog_remote_config_reader_for_path == nullptr) { + throw std::runtime_error{ + "Failed to resolve ddog_remote_config_reader_for_path"}; + } - std::smatch base_match; - if (!std::regex_match(path, base_match, regex) || base_match.size() < 4) { - throw invalid_path(); + ddog_remote_config_read = + // NOLINTNEXTLINE + reinterpret_cast( + dlsym(RTLD_DEFAULT, "ddog_remote_config_read")); + if (ddog_remote_config_read == nullptr) { + throw std::runtime_error{"Failed to resolve ddog_remote_config_read"}; } - return config_path{base_match[3].str(), base_match[2].str()}; + ddog_remote_config_reader_drop = + // NOLINTNEXTLINE + reinterpret_cast( + dlsym(RTLD_DEFAULT, "ddog_remote_config_reader_drop")); + if (ddog_remote_config_reader_drop == nullptr) { + throw std::runtime_error{ + "Failed to resolve ddog_remote_config_reader_drop"}; + } } -client::client(std::unique_ptr &&arg_api, service_identifier &&sid, - remote_config::settings settings, - std::vector listeners) - : api_(std::move(arg_api)), id_(dds::generate_random_uuid()), - sid_(std::move(sid)), settings_(std::move(settings)), - listeners_(std::move(listeners)) +client::client(remote_config::settings settings, + std::vector> listeners) + : reader_{ddog_remote_config_reader_for_path(settings.shmem_path.c_str()), + ddog_remote_config_reader_drop}, + settings_{std::move(settings)}, listeners_{std::move(listeners)} { + assert(settings_.enabled == true); // NOLINT + for (auto const &listener : listeners_) { - const auto &supported_products = listener->get_supported_products(); - for (const auto &[name, capabilities] : supported_products) { - products_.insert(std::pair( - name, product(name, listener))); - capabilities_ |= capabilities; + for (const product p : listener->get_supported_products()) { + std::vector &vec_listeners = + listeners_per_product_[p]; + vec_listeners.push_back(listener.get()); + all_products_.insert(p); } } } -client::ptr client::from_settings(service_identifier &&sid, +std::unique_ptr client::from_settings( const remote_config::settings &settings, - std::vector listeners) + std::vector> listeners) { - return std::make_unique(std::make_unique(settings.host, - std::to_string(settings.port)), - std::move(sid), settings, std::move(listeners)); + return std::unique_ptr{new client(settings, std::move(listeners))}; } -[[nodiscard]] protocol::get_configs_request client::generate_request() const +bool client::poll() { - std::vector config_states; - std::vector files; - - for (const auto &[product_name, product] : products_) { - // State - const auto configs_on_product = product.get_configs(); - for (const auto &[id, config] : configs_on_product) { - config_states.push_back({config.id, config.version, config.product, - config.apply_state, config.apply_error}); - - std::vector hashes; - hashes.reserve(config.hashes.size()); - for (auto const &[algo, hash_sting] : config.hashes) { - hashes.push_back({algo, hash_sting}); - } - files.push_back({config.path, config.length, std::move(hashes)}); - } - } + const std::lock_guard lock{mutex_}; - const protocol::client_tracer ct{std::move(ids_.get()), sid_.tracer_version, - sid_.service, sid_.extra_services, sid_.env, sid_.app_version}; + SPDLOG_DEBUG("Polling remote config"); - const protocol::client_state cs{targets_version_, config_states, - !last_poll_error_.empty(), last_poll_error_, opaque_backend_state_}; - std::vector products_str; - products_str.reserve(products_.size()); - for (const auto &[product_name, product] : products_) { - products_str.push_back(product_name); + ddog_CharSlice slice{}; + const bool has_update = ddog_remote_config_read(reader_.get(), &slice); + if (!has_update) { + SPDLOG_DEBUG("No update available for {}", settings_.shmem_path); + return false; } - protocol::client protocol_client = { - id_, products_str, ct, cs, capabilities_}; - - return {std::move(protocol_client), std::move(files)}; -}; -bool client::process_response(const protocol::get_configs_response &response) -{ - if (!response.targets.has_value()) { - return true; + std::set new_configs; + // NOLINTNEXTLINE + std::string_view resp{reinterpret_cast(slice.ptr), slice.len}; + auto pos_lf = resp.find('\n'); + if (pos_lf == std::string_view::npos) { + throw std::runtime_error{ + "Invalid response from remote config (no newline)"}; + return false; } + SPDLOG_DEBUG("Runtime id is {}", resp.substr(0, pos_lf)); - const std::unordered_map paths_on_targets = - response.targets->paths; - const std::unordered_map target_files = - response.target_files; - std::unordered_map> - configs; - for (const std::string &path : response.client_configs) { - try { - auto cp = config_path::from_path(path); - - // Is path on targets? - auto path_itr = paths_on_targets.find(path); - if (path_itr == paths_on_targets.end()) { - // Not found - last_poll_error_ = "missing config " + path + " in targets"; - return false; - } - auto length = path_itr->second.length; - std::unordered_map hashes = - path_itr->second.hashes; - int custom_v = path_itr->second.custom_v; - - // Is product on the requested ones? - auto product = products_.find(cp.product); - if (product == products_.end()) { - // Not found - last_poll_error_ = "received config " + path + - " for a product that was not requested"; - return false; - } - - // Is path on target_files? - auto path_in_target_files = target_files.find(path); - std::string raw; - if (path_in_target_files == target_files.end()) { - // Check if file in cache - auto configs_on_product = product->second.get_configs(); - auto config_itr = std::find_if(configs_on_product.begin(), - configs_on_product.end(), [&path, &hashes](auto &pair) { - return pair.second.path == path && - pair.second.hashes == hashes; - }); - - if (config_itr == configs_on_product.end()) { - // Not found - last_poll_error_ = "missing config " + path + - " in target files and in cache files"; - return false; - } - - raw = config_itr->second.contents; - length = config_itr->second.length; - custom_v = config_itr->second.version; - } else { - raw = path_in_target_files->second.raw; - } - - const std::string path_c = path; - config const config_ = { - cp.product, cp.id, raw, path_c, hashes, custom_v, length}; - auto configs_itr = configs.find(cp.product); - if (configs_itr == - configs.end()) { // Product not in configs yet. Create entry - std::unordered_map configs_on_product; - configs_on_product.emplace(cp.id, config_); - configs.insert(std::pair>( - cp.product, configs_on_product)); - } else { // Product already exists in configs. Add new config - configs_itr->second.emplace(cp.id, config_); - } - } catch (invalid_path &e) { - last_poll_error_ = "error parsing path " + path; - return false; + std::string_view configs = resp.substr(pos_lf + 1); + while (!configs.empty()) { + auto pos_lf = configs.find('\n'); + if (pos_lf == std::string_view::npos) { + break; } + new_configs.emplace(config::from_line(configs.substr(0, pos_lf))); + configs = configs.substr(pos_lf + 1); } - // Since there have not been errors, we can now update product configs - // First initialise the listener + if (sets_are_indentical_for_subbed_products( + all_products_, new_configs, last_configs_)) { + SPDLOG_DEBUG("Configuration is identical for the subscribed products. " + "Skipping update"); + return false; + } - for (auto &listener : listeners_) { listener->init(); } + return process_response(std::move(new_configs)); +} - for (auto &[name, product] : products_) { - const auto product_configs = configs.find(name); - if (product_configs != configs.end()) { - product.assign_configs(product_configs->second); - } else { - product.assign_configs({}); +bool client::process_response(std::set new_configs) +{ + for (auto &listener : listeners_) { + try { + listener->init(); + } catch (const std::exception &e) { + SPDLOG_ERROR("Failed to init listener: {}", e.what()); } } - for (auto &listener : listeners_) { listener->commit(); } - - targets_version_ = response.targets->version; - opaque_backend_state_ = response.targets->opaque_backend_state; + // unapply should happen first, because asm_dd aggregator ignores the key... + for (const auto &cfg : last_configs_) { + if (new_configs.contains(cfg)) { + continue; + } - return true; -} + const product p = cfg.get_product(); + auto it = listeners_per_product_.find(p); + if (it == listeners_per_product_.end()) { + continue; + } -bool client::is_remote_config_available() -{ - auto response_body = api_->get_info(); - try { - SPDLOG_TRACE("Received info response: {}", response_body); - auto response = protocol::parse_info(response_body); - - return std::find(response.endpoints.begin(), response.endpoints.end(), - "/v0.7/config") != response.endpoints.end(); - } catch (protocol::parser_exception &e) { - SPDLOG_ERROR("Error parsing info response - {}", e.what()); - return false; + SPDLOG_DEBUG("Unapplying config {}", cfg); + for (listener_base *listener : it->second) { + try { + listener->on_unapply(cfg); + } catch (const std::exception &e) { + SPDLOG_ERROR("Failed to unapply config {}: {}", cfg, e.what()); + } + } } -} -bool client::poll() -{ - // Wait until we have a valid runtime ID, once this ID is available, - // it'll always have a value, even if all extensions have disconnected - if (api_ == nullptr || !ids_.has_value()) { - return false; - } + for (const auto &cfg : new_configs) { + const product p = cfg.get_product(); + if (p == known_products::UNKNOWN) { + SPDLOG_INFO("Ignoring config with key {}; unsupported product", + cfg.rc_path); + continue; + } + auto it = listeners_per_product_.find(p); + if (it == listeners_per_product_.end()) { + SPDLOG_INFO( + "No listeners for product {}; skipping key {}", p, cfg.rc_path); + continue; + } - auto request = generate_request(); + SPDLOG_DEBUG("Applying config {}", cfg); + for (listener_base *listener : it->second) { + try { + listener->on_update(cfg); + } catch (const std::exception &e) { + SPDLOG_ERROR("Failed to apply config {}: {}", cfg, e.what()); + } + } + } - std::string serialized_request; - try { - serialized_request = protocol::serialize(request); - SPDLOG_TRACE("Sending request: {}", serialized_request); - } catch (protocol::serializer_exception &e) { - return false; + for (auto &listener : listeners_) { + try { + listener->commit(); + } catch (const std::exception &e) { + SPDLOG_ERROR("Failed to commit listener: {}", e.what()); + } } - auto response_body = api_->get_configs(std::move(serialized_request)); + last_configs_ = std::move(new_configs); - try { - SPDLOG_TRACE("Received response: {}", response_body); - auto response = protocol::parse(response_body); - last_poll_error_.clear(); - return process_response(response); - } catch (protocol::parser_exception &e) { - SPDLOG_ERROR("Error parsing remote config response - {}", e.what()); - return false; - } + return true; } } // namespace dds::remote_config diff --git a/appsec/src/helper/remote_config/client.hpp b/appsec/src/helper/remote_config/client.hpp index 542d6ff216..7d03c0d94d 100644 --- a/appsec/src/helper/remote_config/client.hpp +++ b/appsec/src/helper/remote_config/client.hpp @@ -6,26 +6,28 @@ #pragma once #include +#include +#include #include #include #include -#include "../service_identifier.hpp" -#include "engine.hpp" -#include "engine_settings.hpp" -#include "http_api.hpp" +#include "../engine.hpp" +#include "../engine_settings.hpp" +#include "../service_config.hpp" +#include "../utils.hpp" #include "listeners/listener.hpp" #include "product.hpp" -#include "protocol/client.hpp" -#include "protocol/tuf/get_configs_request.hpp" -#include "protocol/tuf/get_configs_response.hpp" -#include "runtime_id_pool.hpp" -#include "service_config.hpp" #include "settings.hpp" -#include "utils.hpp" + +extern "C" { +struct ddog_RemoteConfigReader; +} namespace dds::remote_config { +void resolve_symbols(); + struct config_path { static config_path from_path(const std::string &path); @@ -34,63 +36,38 @@ struct config_path { }; class client { + client(remote_config::settings settings, + std::vector> listeners); + public: - using ptr = std::unique_ptr; - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - client(std::unique_ptr &&arg_api, service_identifier &&sid, - remote_config::settings settings, - std::vector listeners = {}); - virtual ~client() = default; + ~client() = default; client(const client &) = delete; client(client &&) = delete; client &operator=(const client &) = delete; client &operator=(client &&) = delete; - static client::ptr from_settings(service_identifier &&sid, + static std::unique_ptr from_settings( const remote_config::settings &settings, - std::vector listeners); - - virtual bool poll(); - virtual bool is_remote_config_available(); - [[nodiscard]] virtual const std::unordered_map & - get_products() - { - return products_; - } - - [[nodiscard]] const service_identifier &get_service_identifier() - { - return sid_; - } - - virtual void register_runtime_id(const std::string &id) { ids_.add(id); } - virtual void unregister_runtime_id(const std::string &id) - { - ids_.remove(id); - } + std::vector> listeners); -protected: - [[nodiscard]] protocol::get_configs_request generate_request() const; - bool process_response(const protocol::get_configs_response &response); - - std::unique_ptr api_; + bool poll(); - std::string id_; - runtime_id_pool ids_; - const service_identifier sid_; - const remote_config::settings settings_; +protected: + bool process_response(std::set new_configs); - // remote config state - std::string last_poll_error_; - std::string opaque_backend_state_; - int targets_version_{0}; + std::unique_ptr + reader_; + remote_config::settings settings_; // just for logging - // supported products - std::vector listeners_; - std::unordered_map products_; + std::vector> listeners_; + std::unordered_map> + listeners_per_product_; // non-owning index of listeners_ + std::unordered_set all_products_; // keys of listeners_per_product_ - protocol::capabilities_e capabilities_ = {protocol::capabilities_e::NONE}; + std::set last_configs_; + std::mutex mutex_; }; } // namespace dds::remote_config diff --git a/appsec/src/helper/remote_config/client_handler.cpp b/appsec/src/helper/remote_config/client_handler.cpp index 4e38f3a38a..d61f999255 100644 --- a/appsec/src/helper/remote_config/client_handler.cpp +++ b/appsec/src/helper/remote_config/client_handler.cpp @@ -13,30 +13,17 @@ namespace dds::remote_config { static constexpr std::chrono::milliseconds default_max_interval = 5min; -client_handler::client_handler(remote_config::client::ptr &&rc_client, - std::shared_ptr service_config, - const std::chrono::milliseconds &poll_interval) - : service_config_(std::move(service_config)), - rc_client_(std::move(rc_client)), poll_interval_(poll_interval), - interval_(poll_interval), max_interval(default_max_interval) -{ - // It starts checking if rc is available - rc_action_ = [this] { discover(); }; -} - -client_handler::~client_handler() -{ - if (handler_.joinable()) { - exit_.set_value(true); - handler_.join(); - } -} +client_handler::client_handler(std::unique_ptr &&rc_client, + std::shared_ptr service_config) + : rc_client_{std::move(rc_client)}, + service_config_{std::move(service_config)} +{} -client_handler::ptr client_handler::from_settings(service_identifier &&id, +std::unique_ptr client_handler::from_settings( const dds::engine_settings &eng_settings, std::shared_ptr service_config, - const remote_config::settings &rc_settings, const engine::ptr &engine_ptr, - bool dynamic_enablement) + const remote_config::settings &rc_settings, + const std::shared_ptr &engine_ptr, bool dynamic_enablement) { if (!rc_settings.enabled) { return {}; @@ -46,7 +33,7 @@ client_handler::ptr client_handler::from_settings(service_identifier &&id, return {}; } - std::vector listeners = {}; + std::vector> listeners = {}; if (dynamic_enablement) { listeners.emplace_back( std::make_shared( @@ -59,41 +46,18 @@ client_handler::ptr client_handler::from_settings(service_identifier &&id, } if (listeners.empty()) { + SPDLOG_DEBUG( + "Not enabling remote config for this service as no " + "listeners are available (no dynamic enablement and no rules " + "file set)"); return {}; } - auto rc_client = remote_config::client::from_settings(std::move(id), - remote_config::settings(rc_settings), std::move(listeners)); + auto rc_client = + remote_config::client::from_settings(rc_settings, std::move(listeners)); - return std::make_shared(std::move(rc_client), - std::move(service_config), - std::chrono::milliseconds{rc_settings.poll_interval}); -} - -bool client_handler::start() -{ - if (rc_client_) { - handler_ = std::thread(&client_handler::run, this, exit_.get_future()); - return true; - } - - return false; -} - -void client_handler::handle_error() -{ - rc_action_ = [this] { discover(); }; - - if (errors_ < std::numeric_limits::max() - 1) { - errors_++; - } - - if (interval_ < max_interval) { - auto new_interval = - std::chrono::duration_cast( - poll_interval_ * pow(2, errors_)); - interval_ = std::min(max_interval, new_interval); - } + return std::make_unique( + std::move(rc_client), std::move(service_config)); } void client_handler::poll() @@ -102,42 +66,6 @@ void client_handler::poll() rc_client_->poll(); } catch (const std::exception &e) { SPDLOG_WARN("Error polling remote config: {}", e.what()); - handle_error(); - } -} -void client_handler::discover() -{ - try { - if (rc_client_->is_remote_config_available()) { - // Remote config is available. Start polls - rc_action_ = [this] { poll(); }; - errors_ = 0; - interval_ = poll_interval_; - return; - } - } catch (const std::exception &e) { - SPDLOG_WARN("Error discovering remote config: {}", e.what()); - } - handle_error(); -} - -void client_handler::tick() { rc_action_(); } - -// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) -void client_handler::run(std::future &&exit_signal) -{ - std::chrono::time_point before{0s}; - std::future_status fs = exit_signal.wait_for(0s); - while (fs == std::future_status::timeout) { - // If the thread is interrupted somehow, make sure to check that - // the polling interval has actually elapsed. - auto now = std::chrono::steady_clock::now(); - if ((now - before) >= interval_) { - tick(); - before = now; - } - - fs = exit_signal.wait_for(interval_); } } } // namespace dds::remote_config diff --git a/appsec/src/helper/remote_config/client_handler.hpp b/appsec/src/helper/remote_config/client_handler.hpp index 4fea0e2a06..2a88649b00 100644 --- a/appsec/src/helper/remote_config/client_handler.hpp +++ b/appsec/src/helper/remote_config/client_handler.hpp @@ -5,17 +5,11 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "engine.hpp" -#include "remote_config/client.hpp" -#include "remote_config/settings.hpp" -#include "service_config.hpp" -#include "service_identifier.hpp" -#include "std_logging.hpp" -#include "utils.hpp" -#include +#include "../engine.hpp" +#include "../service_config.hpp" +#include "client.hpp" +#include "settings.hpp" #include -#include -#include namespace dds::remote_config { @@ -23,12 +17,9 @@ using namespace std::chrono_literals; class client_handler { public: - using ptr = std::shared_ptr; - - client_handler(remote_config::client::ptr &&rc_client, - std::shared_ptr service_config, - const std::chrono::milliseconds &poll_interval = 1s); - ~client_handler(); + client_handler(std::unique_ptr &&rc_client, + std::shared_ptr service_config); + ~client_handler() = default; client_handler(const client_handler &) = delete; client_handler &operator=(const client_handler &) = delete; @@ -36,48 +27,17 @@ class client_handler { client_handler(client_handler &&) = delete; client_handler &operator=(client_handler &&) = delete; - static client_handler::ptr from_settings(service_identifier &&id, + static std::unique_ptr from_settings( const dds::engine_settings &eng_settings, std::shared_ptr service_config, const remote_config::settings &rc_settings, - const engine::ptr &engine_ptr, bool dynamic_enablement); - - bool start(); - - remote_config::client *get_client() { return rc_client_.get(); } + const std::shared_ptr &engine_ptr, bool dynamic_enablement); - void register_runtime_id(const std::string &id) - { - if (rc_client_) { - rc_client_->register_runtime_id(id); - } - } - void unregister_runtime_id(const std::string &id) - { - if (rc_client_) { - rc_client_->unregister_runtime_id(id); - } - } + void poll(); protected: - void run(std::future &&exit_signal); - void handle_error(); - - remote_config::client::ptr rc_client_; std::shared_ptr service_config_; - - std::chrono::milliseconds poll_interval_; - std::chrono::milliseconds interval_; - std::chrono::milliseconds max_interval; - void poll(); - void discover(); - void tick(); - std::function rc_action_; - - std::uint16_t errors_ = {0}; - - std::promise exit_; - std::thread handler_; + std::unique_ptr rc_client_; }; } // namespace dds::remote_config diff --git a/appsec/src/helper/remote_config/config.cpp b/appsec/src/helper/remote_config/config.cpp new file mode 100644 index 0000000000..852c63615d --- /dev/null +++ b/appsec/src/helper/remote_config/config.cpp @@ -0,0 +1,105 @@ +#include "config.hpp" +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +} + +using namespace std::literals; + +namespace dds::remote_config { + +[[nodiscard]] product config::get_product() const +{ + // A configuration key has the form: + // (datadog/ | employee)///" + std::string_view sv{rc_path}; + if (sv.starts_with("datadog/"sv)) { + sv.remove_prefix("datadog/"sv.length()); + auto org_id_end = sv.find('/'); + if (org_id_end != std::string_view::npos) { + sv.remove_prefix(org_id_end + 1); + auto product_end = sv.find('/'); + if (product_end != std::string_view::npos) { + return known_products::for_name(sv.substr(0, product_end)); + } + } + } else if (sv.starts_with("employee/"sv)) { + sv.remove_prefix("employee/"sv.length()); + auto product_end = sv.find('/'); + if (product_end != std::string::npos) { + return known_products::for_name(sv.substr(0, product_end)); + } + } + + return known_products::UNKNOWN; +} + +config config::from_line(std::string_view line) +{ + // split by : + auto pos = line.find(':'); + if (pos == std::string_view::npos) { + throw std::runtime_error{"invalid shmem config line (no colon)"}; + } + + const std::string_view shm_path{line.substr(0, pos)}; + auto pos2 = line.find(':', pos + 1); + if (pos2 == std::string_view::npos) { + throw std::runtime_error{"invalid shmem config line (no second colon)"}; + } + + std::uint32_t limiter_idx; + auto res = + std::from_chars(line.data() + pos + 1, line.data() + pos2, limiter_idx); + if (res.ec != std::errc{} || res.ptr != line.data() + pos2) { + throw std::runtime_error{"invalid shmem config line (limiter_idx)"}; + } + + const std::string_view rc_path_encoded{line.substr(pos2 + 1)}; + // base64 decode rc_path (no padding): + std::string rc_path = base64_decode(rc_path_encoded); + + return {std::string{shm_path}, std::move(rc_path)}; +} + +mapped_memory config::read() const +{ + // open shared memory segment at rc_path: + const int fd = ::shm_open(shm_path.c_str(), O_RDONLY, 0); + if (fd == -1) { + throw std::runtime_error{ + "shm_open failed for " + shm_path + " : " + strerror_ts(errno)}; + } + + auto close_fs = defer{[fd]() { ::close(fd); }}; + + // check that the uid of the shared memory segment is the same as ours + struct ::stat shm_stat {}; + if (::fstat(fd, &shm_stat) == -1) { + throw std::runtime_error{ + "Call to fstat on memory segment failed: " + strerror_ts(errno)}; + } + if (shm_stat.st_uid != ::geteuid()) { + throw std::runtime_error{"Shared memory segment does not have the " + "expected owner. Expect our uid " + + std::to_string(::geteuid()) + " but found " + + std::to_string(shm_stat.st_uid)}; + } + + void *shm_ptr = + ::mmap(nullptr, shm_stat.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (shm_ptr == MAP_FAILED) { // NOLINT + throw std::runtime_error( + "Failed to map shared memory: " + std::string{strerror_ts(errno)}); + } + + return mapped_memory{shm_ptr, static_cast(shm_stat.st_size)}; +} +} // namespace dds::remote_config diff --git a/appsec/src/helper/remote_config/config.hpp b/appsec/src/helper/remote_config/config.hpp index 839c51e6f3..05e5e226c7 100644 --- a/appsec/src/helper/remote_config/config.hpp +++ b/appsec/src/helper/remote_config/config.hpp @@ -5,48 +5,98 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "protocol/config_state.hpp" -#include +#include "../utils.hpp" +#include "product.hpp" #include -#include +#include #include +extern "C" { +#include +} + namespace dds::remote_config { -struct config { - std::string product; - std::string id; - std::string contents; - std::string path; - std::unordered_map hashes; - int version; - int length; - protocol::config_state::applied_state apply_state; - std::string apply_error; - - friend auto &operator<<( - std::ostream &os, const dds::remote_config::config &c) - { - os << "Product: " << c.product << std::endl; - os << "id: " << c.id << std::endl; - os << "contents: " << c.contents << std::endl; - os << "path: " << c.path << std::endl; - // os << "hashes: " << c.hashes << std::endl; - os << "version: " << c.version << std::endl; - os << "length: " << c.length << std::endl; - os << "apply_state: " << (int)c.apply_state << std::endl; - os << "apply_error: " << c.apply_error << std::endl; - return os; +class mapped_memory { +public: + mapped_memory(void *ptr, std::size_t size) : ptr_{ptr}, size_{size} {} + mapped_memory(const mapped_memory &) = delete; + mapped_memory(mapped_memory &&mm) noexcept : ptr_{mm.ptr_}, size_{mm.size_} + { + mm.ptr_ = nullptr; + mm.size_ = 0; + } + mapped_memory &operator=(const mapped_memory &) = delete; + mapped_memory &operator=(mapped_memory &&mm) noexcept + { + ptr_ = mm.ptr_; + size_ = mm.size_; + mm.ptr_ = nullptr; + mm.size_ = 0; + return *this; + } + ~mapped_memory() noexcept + { + if (ptr_ != nullptr) { + if (::munmap(ptr_, size_) == -1) { + SPDLOG_WARN( + "Failed to unmap shared memory: {}", strerror_ts(errno)); + }; + } } + + operator std::string_view() const // NOLINT + { + return std::string_view{static_cast(ptr_), size_}; + } + +private: + void *ptr_; + std::size_t size_; }; -inline bool operator==(const config &rhs, const config &lhs) -{ - return rhs.product == lhs.product && rhs.id == lhs.id && - rhs.contents == lhs.contents && rhs.hashes == lhs.hashes && - rhs.version == lhs.version && rhs.path == lhs.path && - rhs.length == lhs.length && rhs.apply_state == lhs.apply_state && - rhs.apply_error == lhs.apply_error; -} +struct config { + // from a line provided by the RC config reader + static config from_line(std::string_view line); + + std::string shm_path; + std::string rc_path; + + [[nodiscard]] mapped_memory read() const; + + [[nodiscard]] product get_product() const; + + bool operator==(const config &b) const + { + return shm_path == b.shm_path && rc_path == b.rc_path; + } + + friend std::ostream &operator<<(std::ostream &os, const config &c) + { + return os << c.shm_path << ":" << c.rc_path; + } +}; } // namespace dds::remote_config + +namespace std { +template <> struct hash { + std::size_t operator()(const dds::remote_config::config &key) const + { + return dds::hash(key.shm_path, key.rc_path); + } +}; +template <> struct less { + bool operator()(const dds::remote_config::config &lhs, + const dds::remote_config::config &rhs) const + { + if (lhs.rc_path < rhs.rc_path) { + return true; + }; + if (lhs.rc_path > rhs.rc_path) { + return false; + } + return lhs.shm_path < rhs.shm_path; + } +}; +} // namespace std diff --git a/appsec/src/helper/remote_config/http_api.cpp b/appsec/src/helper/remote_config/http_api.cpp deleted file mode 100644 index 70044c8f9b..0000000000 --- a/appsec/src/helper/remote_config/http_api.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#include "http_api.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast = boost::beast; // from -namespace http = beast::http; // from -namespace net = boost::asio; // from -using tcp = net::ip::tcp; // from - -namespace { -constexpr auto timeout = - std::chrono::duration_cast( - std::chrono::seconds{60}); -const int version = 11; - -// NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) -net::awaitable execute_request(const std::string &host, - // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) - const std::string &port, const http::request &request) -{ - std::string result; - - try { - auto exec = co_await net::this_coro::executor; - - // These objects perform our I/O - tcp::resolver resolver(exec); - beast::tcp_stream stream(exec); - - // Look up the domain name - auto const results = - co_await resolver.async_resolve(host, port, net::use_awaitable); - - // Make the connection on the IP address we get from a lookup - beast::get_lowest_layer(stream).expires_after(timeout); - co_await stream.async_connect( - results.begin(), results.end(), net::use_awaitable); - - // Send the HTTP request to the remote host - co_await http::async_write(stream, request, net::use_awaitable); - - // This buffer is used for reading and must be persisted - beast::flat_buffer buffer; - - // Declare a container to hold the response - http::response res; - - // Receive the HTTP response - co_await http::async_read(stream, buffer, res, net::use_awaitable); - - // Write the message to standard out - result = boost::beast::buffers_to_string(res.body().data()); - - // Gracefully close the socket - beast::error_code ec; - // NOLINTNEXTLINE(bugprone-unused-return-value,cert-err33-c) - stream.socket().shutdown(tcp::socket::shutdown_both, ec); - - // not_connected happens sometimes - // so don't bother reporting it. - // - if (ec && ec != beast::errc::not_connected) { - throw beast::system_error{ec}; - } - - // If we get here then the connection is closed gracefully - } catch (std::exception const &e) { - auto sv = request.target(); - const std::string err{sv.data(), sv.size()}; - SPDLOG_ERROR("Connection error - {} - {}", err, e.what()); - throw dds::remote_config::network_exception( - "Connection error - " + err + " - " + e.what()); - } - - co_return result; -} - -std::string execute_request_sync(const std::string &host, - const std::string &port, const http::request &req) -{ - - net::io_context ioc; - net::awaitable client_coroutine = - execute_request(host, port, req); - - std::promise promise; - auto fut = promise.get_future(); - - net::co_spawn(ioc, std::move(client_coroutine), - [&](const std::exception_ptr &eptr, std::string body) { - if (eptr) { - promise.set_exception(eptr); - } else { - promise.set_value(std::move(body)); - } - }); - - ioc.run(); - return fut.get(); -} -} // namespace - -std::string dds::remote_config::http_api::get_info() const -{ - http::request req{http::verb::get, "/info", version}; - req.set(http::field::host, host_); - req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); - - return execute_request_sync(host_, port_, req); -} - -std::string dds::remote_config::http_api::get_configs( - std::string &&request) const -{ - // Set up an HTTP POST request message - http::request req; - req.method(http::verb::post); - req.target("/v0.7/config"); - req.version(version); - req.set(http::field::host, host_); - req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); - req.set(http::field::content_length, std::to_string(request.size())); - req.set(http::field::accept, "*/*"); - req.set(http::field::content_type, "application/x-www-form-urlencoded"); - req.body() = std::move(request); - req.keep_alive(true); - - return execute_request_sync(host_, port_, req); -}; diff --git a/appsec/src/helper/remote_config/http_api.hpp b/appsec/src/helper/remote_config/http_api.hpp deleted file mode 100644 index f83fb2e527..0000000000 --- a/appsec/src/helper/remote_config/http_api.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include - -namespace dds::remote_config { - -class network_exception : public std::exception { -public: - explicit network_exception(std::string what) : what_(std::move(what)) {} - [[nodiscard]] const char *what() const noexcept override - { - return what_.c_str(); - } - -protected: - const std::string what_; -}; - -class http_api { -public: - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - http_api(std::string host, std::string port) - : host_(std::move(host)), port_(std::move(port)){}; - - http_api(const http_api &) = delete; - http_api(http_api &&) = delete; - - http_api &operator=(const http_api &) = delete; - http_api &operator=(http_api &&) = delete; - - virtual ~http_api() = default; - - virtual std::string get_info() const; - virtual std::string get_configs(std::string &&request) const; - -protected: - std::string host_; - std::string port_; -}; - -} // namespace dds::remote_config diff --git a/appsec/src/helper/remote_config/listeners/asm_features_listener.cpp b/appsec/src/helper/remote_config/listeners/asm_features_listener.cpp index edd8ae0f66..71c293a04d 100644 --- a/appsec/src/helper/remote_config/listeners/asm_features_listener.cpp +++ b/appsec/src/helper/remote_config/listeners/asm_features_listener.cpp @@ -4,19 +4,21 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "asm_features_listener.hpp" -#include "exception.hpp" -#include "json_helper.hpp" -#include "remote_config/exception.hpp" -#include "utils.hpp" +#include "../../json_helper.hpp" +#include "../../utils.hpp" +#include "../exception.hpp" #include #include void dds::remote_config::asm_features_listener::on_update(const config &config) { rapidjson::Document serialized_doc; - if (!json_helper::get_json_base64_encoded_content( - config.contents, serialized_doc)) { - throw error_applying_config("Invalid config contents"); + + { + const mapped_memory contents{config.read()}; + if (!json_helper::parse_json(contents, serialized_doc)) { + throw error_applying_config("Invalid config contents"); + } } auto asm_itr = json_helper::get_field_of_type( diff --git a/appsec/src/helper/remote_config/listeners/asm_features_listener.hpp b/appsec/src/helper/remote_config/listeners/asm_features_listener.hpp index ab91cc42be..9ca500549c 100644 --- a/appsec/src/helper/remote_config/listeners/asm_features_listener.hpp +++ b/appsec/src/helper/remote_config/listeners/asm_features_listener.hpp @@ -5,9 +5,10 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "config.hpp" +#include "../../config.hpp" +#include "../../service_config.hpp" +#include "../product.hpp" #include "listener.hpp" -#include "service_config.hpp" namespace dds::remote_config { @@ -22,10 +23,9 @@ class asm_features_listener : public listener_base { service_config_->unset_asm(); } - [[nodiscard]] std::unordered_map - get_supported_products() override + [[nodiscard]] std::unordered_set get_supported_products() override { - return {{"ASM_FEATURES", protocol::capabilities_e::ASM_ACTIVATION}}; + return {known_products::ASM_FEATURES}; } void init() override {} diff --git a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.cpp b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.cpp index 0fb56fd106..f8d5f3b9b1 100644 --- a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.cpp +++ b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.cpp @@ -4,12 +4,11 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "asm_aggregator.hpp" -#include "exception.hpp" -#include "remote_config/exception.hpp" -#include "spdlog/spdlog.h" +#include "../../exception.hpp" #include #include #include +#include namespace dds::remote_config { @@ -31,7 +30,7 @@ void asm_aggregator::init(rapidjson::Document::AllocatorType *allocator) void asm_aggregator::add(const config &config) { rapidjson::Document doc(&ruleset_.GetAllocator()); - if (!json_helper::get_json_base64_encoded_content(config.contents, doc)) { + if (!json_helper::parse_json(config.read(), doc)) { throw error_applying_config("Invalid config contents"); } diff --git a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.hpp b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.hpp index 46dfdf4b6a..d4998f3836 100644 --- a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.hpp +++ b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_aggregator.hpp @@ -5,11 +5,11 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "config.hpp" +#include "../../../config.hpp" +#include "../../../engine.hpp" +#include "../../../json_helper.hpp" +#include "../../../parameter.hpp" #include "config_aggregator.hpp" -#include "engine.hpp" -#include "json_helper.hpp" -#include "parameter.hpp" #include #include #include diff --git a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.cpp b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.cpp index 90ef911524..b0f6805872 100644 --- a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.cpp +++ b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.cpp @@ -4,13 +4,12 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "asm_data_aggregator.hpp" -#include "exception.hpp" -#include "json_helper.hpp" -#include "remote_config/exception.hpp" -#include "spdlog/spdlog.h" +#include "../../../json_helper.hpp" +#include "../../exception.hpp" #include #include #include +#include namespace dds::remote_config { @@ -81,8 +80,7 @@ void extract_data( void asm_data_aggregator::add(const config &config) { rapidjson::Document serialized_doc; - if (!json_helper::get_json_base64_encoded_content( - config.contents, serialized_doc)) { + if (!json_helper::parse_json(config.read(), serialized_doc)) { throw error_applying_config("Invalid config contents"); } diff --git a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.hpp b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.hpp index 4dba85460f..a570c6eea4 100644 --- a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.hpp +++ b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_data_aggregator.hpp @@ -5,10 +5,10 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "config.hpp" +#include "../../../engine.hpp" +#include "../../../parameter.hpp" +#include "../../config.hpp" #include "config_aggregator.hpp" -#include "engine.hpp" -#include "parameter.hpp" #include #include #include diff --git a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.cpp b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.cpp index ab5c8ace09..ec0458b34a 100644 --- a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.cpp +++ b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.cpp @@ -4,14 +4,14 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "asm_dd_aggregator.hpp" -#include "exception.hpp" -#include "remote_config/exception.hpp" +#include "../../../exception.hpp" +#include "../../exception.hpp" #include void dds::remote_config::asm_dd_aggregator::add(const config &config) { rapidjson::Document doc(&ruleset_.GetAllocator()); - if (!json_helper::get_json_base64_encoded_content(config.contents, doc)) { + if (!json_helper::parse_json(config.read(), doc)) { throw error_applying_config("Invalid config contents"); } diff --git a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.hpp b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.hpp index 937a4d1c69..2951005e71 100644 --- a/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.hpp +++ b/appsec/src/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator.hpp @@ -5,10 +5,10 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "config.hpp" +#include "../../../config.hpp" +#include "../../../json_helper.hpp" +#include "../../../parameter.hpp" #include "config_aggregator.hpp" -#include "json_helper.hpp" -#include "parameter.hpp" #include #include #include diff --git a/appsec/src/helper/remote_config/listeners/config_aggregators/config_aggregator.hpp b/appsec/src/helper/remote_config/listeners/config_aggregators/config_aggregator.hpp index 52bcd41e08..381eed0bb0 100644 --- a/appsec/src/helper/remote_config/listeners/config_aggregators/config_aggregator.hpp +++ b/appsec/src/helper/remote_config/listeners/config_aggregators/config_aggregator.hpp @@ -5,10 +5,10 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "config.hpp" -#include "engine.hpp" -#include "parameter.hpp" -#include "remote_config/listeners/listener.hpp" +#include "../../../config.hpp" +#include "../../../engine.hpp" +#include "../../../parameter.hpp" +#include "../listener.hpp" #include #include #include diff --git a/appsec/src/helper/remote_config/listeners/engine_listener.cpp b/appsec/src/helper/remote_config/listeners/engine_listener.cpp index 761f6be0e8..29d92f1c6e 100644 --- a/appsec/src/helper/remote_config/listeners/engine_listener.cpp +++ b/appsec/src/helper/remote_config/listeners/engine_listener.cpp @@ -4,29 +4,30 @@ // This product includes software developed at Datadog // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "engine_listener.hpp" +#include "../../json_helper.hpp" +#include "../exception.hpp" +#include "../product.hpp" #include "config_aggregators/asm_aggregator.hpp" #include "config_aggregators/asm_data_aggregator.hpp" #include "config_aggregators/asm_dd_aggregator.hpp" -#include "exception.hpp" -#include "json_helper.hpp" -#include "remote_config/exception.hpp" -#include "spdlog/spdlog.h" #include #include #include +#include #include namespace dds::remote_config { engine_listener::engine_listener( - engine::ptr engine, const std::string &rules_file) + std::shared_ptr engine, const std::string &rules_file) : engine_(std::move(engine)) { - aggregators_.emplace(asm_product, std::make_unique()); aggregators_.emplace( - asm_dd_product, std::make_unique(rules_file)); + known_products::ASM, std::make_unique()); + aggregators_.emplace(known_products::ASM_DD, + std::make_unique(rules_file)); aggregators_.emplace( - asm_data_product, std::make_unique()); + known_products::ASM_DATA, std::make_unique()); } void engine_listener::init() @@ -37,9 +38,10 @@ void engine_listener::init() void engine_listener::on_update(const config &config) { - auto it = aggregators_.find(config.product); + auto it = aggregators_.find(config.get_product()); if (it == aggregators_.end()) { - throw error_applying_config("unknown product: " + config.product); + throw error_applying_config( + "unknown product: " + std::string{config.get_product().name()}); } auto &aggregator = it->second; @@ -53,9 +55,10 @@ void engine_listener::on_update(const config &config) void engine_listener::on_unapply(const config &config) { - auto it = aggregators_.find(config.product); + auto it = aggregators_.find(config.get_product()); if (it == aggregators_.end()) { - throw error_applying_config("unknown product: " + config.product); + throw error_applying_config( + "unknown product: " + std::string{config.get_product().name()}); } auto &aggregator = it->second; diff --git a/appsec/src/helper/remote_config/listeners/engine_listener.hpp b/appsec/src/helper/remote_config/listeners/engine_listener.hpp index 1ef7847ed5..6f961b6c2c 100644 --- a/appsec/src/helper/remote_config/listeners/engine_listener.hpp +++ b/appsec/src/helper/remote_config/listeners/engine_listener.hpp @@ -5,12 +5,12 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "config.hpp" +#include "../../config.hpp" +#include "../../engine.hpp" +#include "../../parameter.hpp" +#include "../product.hpp" #include "config_aggregators/config_aggregator.hpp" -#include "engine.hpp" #include "listener.hpp" -#include "parameter.hpp" -#include "remote_config/protocol/client.hpp" #include #include #include @@ -21,7 +21,7 @@ namespace dds::remote_config { class engine_listener : public listener_base { public: explicit engine_listener( - engine::ptr engine, const std::string &rules_file = {}); + std::shared_ptr engine, const std::string &rules_file = {}); engine_listener(const engine_listener &) = delete; engine_listener(engine_listener &&) = default; engine_listener &operator=(const engine_listener &) = delete; @@ -34,30 +34,16 @@ class engine_listener : public listener_base { void on_unapply(const config &config) override; void commit() override; - [[nodiscard]] std::unordered_map - get_supported_products() override + [[nodiscard]] std::unordered_set get_supported_products() override { - return {{asm_product, - protocol::capabilities_e::ASM_EXCLUSIONS | - protocol::capabilities_e::ASM_CUSTOM_BLOCKING_RESPONSE | - protocol::capabilities_e::ASM_REQUEST_BLOCKING | - protocol::capabilities_e::ASM_RESPONSE_BLOCKING | - protocol::capabilities_e::ASM_CUSTOM_RULES | - protocol::capabilities_e::ASM_TRUSTED_IPS}, - {asm_dd_product, protocol::capabilities_e::ASM_DD_RULES}, - {asm_data_product, - protocol::capabilities_e::ASM_IP_BLOCKING | - protocol::capabilities_e::ASM_USER_BLOCKING}}; + return {known_products::ASM, known_products::ASM_DD, + known_products::ASM_DATA}; } protected: - static constexpr std::string_view asm_product = "ASM"; - static constexpr std::string_view asm_dd_product = "ASM_DD"; - static constexpr std::string_view asm_data_product = "ASM_DATA"; - - std::unordered_map + std::unordered_map aggregators_; - engine::ptr engine_; + std::shared_ptr engine_; rapidjson::Document ruleset_; std::unordered_set to_commit_; }; diff --git a/appsec/src/helper/remote_config/listeners/listener.hpp b/appsec/src/helper/remote_config/listeners/listener.hpp index df17d900e5..91bb93af1a 100644 --- a/appsec/src/helper/remote_config/listeners/listener.hpp +++ b/appsec/src/helper/remote_config/listeners/listener.hpp @@ -5,17 +5,17 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "remote_config/config.hpp" -#include "remote_config/protocol/client.hpp" +#include "../config.hpp" +#include "../product.hpp" #include +#include +#include #include namespace dds::remote_config { class listener_base { public: - using shared_ptr = std::shared_ptr; - listener_base() = default; listener_base(const listener_base &) = default; listener_base(listener_base &&) = default; @@ -25,8 +25,7 @@ class listener_base { virtual void on_update(const config &config) = 0; virtual void on_unapply(const config &config) = 0; - [[nodiscard]] virtual std::unordered_map + [[nodiscard]] virtual std::unordered_set get_supported_products() = 0; // Stateful listeners need to override these methods diff --git a/appsec/src/helper/remote_config/product.cpp b/appsec/src/helper/remote_config/product.cpp deleted file mode 100644 index 2a604c62d0..0000000000 --- a/appsec/src/helper/remote_config/product.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#include "product.hpp" -#include "exception.hpp" - -void dds::remote_config::product::update_configs( - std::unordered_map &to_update) -{ - for (auto &[name, config] : to_update) { - try { - listener_->on_update(config); - config.apply_state = dds::remote_config::protocol::config_state:: - applied_state::ACKNOWLEDGED; - config.apply_error = ""; - } catch (dds::remote_config::error_applying_config &e) { - config.apply_state = dds::remote_config::protocol::config_state:: - applied_state::ERROR; - config.apply_error = e.what(); - } - } -} - -void dds::remote_config::product::unapply_configs( - std::unordered_map &to_unapply) -{ - for (auto &[path, conf] : to_unapply) { - try { - listener_->on_unapply(conf); - conf.apply_state = dds::remote_config::protocol::config_state:: - applied_state::ACKNOWLEDGED; - conf.apply_error = ""; - } catch (dds::remote_config::error_applying_config &e) { - conf.apply_state = dds::remote_config::protocol::config_state:: - applied_state::ERROR; - conf.apply_error = e.what(); - } - } -} - -void dds::remote_config::product::assign_configs( - const std::unordered_map &configs) -{ - std::unordered_map to_update; - bool changes = false; - - // determine what each config given is - for (const auto &[name, config] : configs) { - auto previous_config = configs_.find(name); - if (previous_config == configs_.end()) { // New config - changes = true; - auto config_to_update = config; - config_to_update.apply_state = dds::remote_config::protocol:: - config_state::applied_state::UNACKNOWLEDGED; - to_update.emplace(name, config_to_update); - } else { // Already existed - if (config.hashes == - previous_config->second.hashes) { // No changes in config - to_update.emplace(name, previous_config->second); - } else { // Config updated - changes = true; - auto config_to_update = config; - config_to_update.apply_state = dds::remote_config::protocol:: - config_state::applied_state::UNACKNOWLEDGED; - to_update.emplace(name, config_to_update); - } - // configs_ at the end of this loop will contain only configs - // which have to be unapply. This one has been classified as - // something else and therefore, it has to be removed - configs_.erase(previous_config); - } - } - - if (changes || !configs_.empty()) { - update_configs(to_update); - unapply_configs(configs_); - } - - // Save new state of configs - configs_ = std::move(to_update); -}; diff --git a/appsec/src/helper/remote_config/product.hpp b/appsec/src/helper/remote_config/product.hpp index 5c04f14904..a9b70fe60f 100644 --- a/appsec/src/helper/remote_config/product.hpp +++ b/appsec/src/helper/remote_config/product.hpp @@ -5,50 +5,62 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "config.hpp" -#include "listeners/listener.hpp" -#include "remote_config/protocol/client.hpp" -#include -#include -#include -#include -#include -#include +#include "../config.hpp" +#include "../utils.hpp" +#include namespace dds::remote_config { class product { public: - explicit product(std::string_view name, listener_base::shared_ptr listener) - : name_(name), listener_(std::move(listener)) + explicit constexpr product(std::string_view name) : name_{name} {} + + [[nodiscard]] const std::string_view &name() const { return name_; } + + bool operator==(const product &other) const { return name_ == other.name_; } + + friend std::ostream &operator<<(std::ostream &os, const product &p) { - if (listener_ == nullptr) { - throw std::runtime_error("invalid listener"); - } + return os << p.name_; } - void assign_configs(const std::unordered_map &configs); - [[nodiscard]] const std::unordered_map & - get_configs() const - { - return configs_; - }; - bool operator==(product const &b) const +private: + std::string_view name_; +}; + +struct known_products { + static inline constexpr product ASM{std::string_view{"ASM"}}; + static inline constexpr product ASM_DD{std::string_view{"ASM_DD"}}; + static inline constexpr product ASM_DATA{std::string_view{"ASM_DATA"}}; + static inline constexpr product ASM_FEATURES{ + std::string_view{"ASM_FEATURES"}}; + static inline constexpr product UNKNOWN{std::string_view{"UNKOWN"}}; + + static product for_name(std::string_view name) { - return name_ == b.name_ && configs_ == b.configs_; + if (name == ASM.name()) { + return ASM; + } + if (name == ASM_DD.name()) { + return ASM_DD; + } + if (name == ASM_DATA.name()) { + return ASM_DATA; + } + if (name == ASM_FEATURES.name()) { + return ASM_FEATURES; + } + + return UNKNOWN; } - [[nodiscard]] const std::string &get_name() const { return name_; } - -protected: - void update_configs( - std::unordered_map &to_update); - void unapply_configs( - std::unordered_map - &to_unapply); - - std::string name_; - std::unordered_map configs_; - std::shared_ptr listener_; }; - } // namespace dds::remote_config + +namespace std { +template <> struct hash { + std::size_t operator()(const dds::remote_config::product &product) const + { + return dds::hash(product.name()); + } +}; +} // namespace std diff --git a/appsec/src/helper/remote_config/protocol/cached_target_file_hash.hpp b/appsec/src/helper/remote_config/protocol/cached_target_file_hash.hpp deleted file mode 100644 index 84a4b6dc2e..0000000000 --- a/appsec/src/helper/remote_config/protocol/cached_target_file_hash.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include - -namespace dds::remote_config::protocol { - -struct cached_target_files_hash { - std::string algorithm; - std::string hash; -}; - -inline bool operator==( - const cached_target_files_hash &rhs, const cached_target_files_hash &lhs) -{ - return rhs.algorithm == lhs.algorithm && rhs.hash == lhs.hash; -} -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/cached_target_files.hpp b/appsec/src/helper/remote_config/protocol/cached_target_files.hpp deleted file mode 100644 index dfda0c496b..0000000000 --- a/appsec/src/helper/remote_config/protocol/cached_target_files.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include - -#include "cached_target_file_hash.hpp" - -namespace dds::remote_config::protocol { - -struct cached_target_files { - std::string path; - int length; - std::vector hashes; -}; - -inline bool operator==( - const cached_target_files &rhs, const cached_target_files &lhs) -{ - return rhs.path == lhs.path && rhs.length == lhs.length && - rhs.hashes == lhs.hashes; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/client.hpp b/appsec/src/helper/remote_config/protocol/client.hpp deleted file mode 100644 index 1e13d08a7c..0000000000 --- a/appsec/src/helper/remote_config/protocol/client.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include -#include - -#include "client_state.hpp" -#include "client_tracer.hpp" - -namespace dds::remote_config::protocol { - -enum class capabilities_e : uint16_t { - NONE = 0, - RESERVED = 1, - ASM_ACTIVATION = 1 << 1, - ASM_IP_BLOCKING = 1 << 2, - ASM_DD_RULES = 1 << 3, - ASM_EXCLUSIONS = 1 << 4, - ASM_REQUEST_BLOCKING = 1 << 5, - ASM_RESPONSE_BLOCKING = 1 << 6, - ASM_USER_BLOCKING = 1 << 7, - ASM_CUSTOM_RULES = 1 << 8, - ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9, - ASM_TRUSTED_IPS = 1 << 10, -}; - -constexpr capabilities_e operator|( - const capabilities_e &lhs, capabilities_e rhs) -{ - return static_cast( - static_cast::type>(lhs) | - static_cast::type>(rhs)); -} - -constexpr capabilities_e &operator|=( - capabilities_e &lhs, const capabilities_e rhs) -{ - lhs = lhs | rhs; - return lhs; -} - -constexpr capabilities_e operator&(capabilities_e lhs, capabilities_e rhs) -{ - return static_cast( - static_cast::type>(lhs) & - static_cast::type>(rhs)); -} - -struct client { - std::string id; - std::vector products; - protocol::client_tracer client_tracer; - protocol::client_state client_state; - capabilities_e capabilities; -}; - -inline bool operator==(const client &rhs, const client &lhs) -{ - return rhs.id == lhs.id && rhs.products == lhs.products && - rhs.client_tracer == lhs.client_tracer && - rhs.client_state == lhs.client_state && - rhs.capabilities == lhs.capabilities; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/client_state.hpp b/appsec/src/helper/remote_config/protocol/client_state.hpp deleted file mode 100644 index 369736ce8e..0000000000 --- a/appsec/src/helper/remote_config/protocol/client_state.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include "config_state.hpp" -#include -#include - -namespace dds::remote_config::protocol { - -struct client_state { - int targets_version; - std::vector config_states; - bool has_error; - std::string error; - std::string backend_client_state; -}; - -inline bool operator==(const client_state &rhs, const client_state &lhs) -{ - return rhs.targets_version == lhs.targets_version && - rhs.config_states == lhs.config_states && - rhs.has_error == lhs.has_error && rhs.error == lhs.error && - rhs.backend_client_state == lhs.backend_client_state; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/client_tracer.hpp b/appsec/src/helper/remote_config/protocol/client_tracer.hpp deleted file mode 100644 index 8fb4313583..0000000000 --- a/appsec/src/helper/remote_config/protocol/client_tracer.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include - -namespace dds::remote_config::protocol { - -struct client_tracer { - std::string runtime_id; - std::string tracer_version; - std::string service; - std::vector extra_services; - std::string env; - std::string app_version; -}; - -inline bool operator==(const client_tracer &rhs, const client_tracer &lhs) -{ - return rhs.runtime_id == lhs.runtime_id && - rhs.tracer_version == lhs.tracer_version && - rhs.service == lhs.service && - rhs.extra_services == lhs.extra_services && rhs.env == lhs.env && - rhs.app_version == lhs.app_version; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/config_state.hpp b/appsec/src/helper/remote_config/protocol/config_state.hpp deleted file mode 100644 index 791a895668..0000000000 --- a/appsec/src/helper/remote_config/protocol/config_state.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include - -namespace dds::remote_config::protocol { - -struct config_state { - std::string id; - int version; - std::string product; - enum class applied_state : int { - UNKNOWN = 0, - UNACKNOWLEDGED = 1, - ACKNOWLEDGED = 2, - ERROR = 3 - } apply_state; - std::string apply_error; -}; - -inline bool operator==(const config_state &rhs, const config_state &lhs) -{ - return rhs.id == lhs.id && rhs.version == lhs.version && - rhs.product == lhs.product && rhs.apply_state == lhs.apply_state && - rhs.apply_error == lhs.apply_error; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/path.hpp b/appsec/src/helper/remote_config/protocol/path.hpp deleted file mode 100644 index a902efa0ae..0000000000 --- a/appsec/src/helper/remote_config/protocol/path.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include -#include - -namespace dds::remote_config::protocol { - -struct path { - int custom_v; - std::unordered_map hashes; - int length; -}; - -inline bool operator==(const path &rhs, const path &lhs) -{ - return rhs.custom_v == lhs.custom_v && rhs.hashes == lhs.hashes && - rhs.length == lhs.length; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/target_file.hpp b/appsec/src/helper/remote_config/protocol/target_file.hpp deleted file mode 100644 index 170f742ddd..0000000000 --- a/appsec/src/helper/remote_config/protocol/target_file.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include - -namespace dds::remote_config::protocol { - -struct target_file { - std::string path; - std::string raw; -}; - -inline bool operator==(const target_file &rhs, const target_file &lhs) -{ - return rhs.path == lhs.path && rhs.raw == lhs.raw; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/targets.hpp b/appsec/src/helper/remote_config/protocol/targets.hpp deleted file mode 100644 index 2844c25954..0000000000 --- a/appsec/src/helper/remote_config/protocol/targets.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include - -#include "path.hpp" - -namespace dds::remote_config::protocol { - -struct targets { - int version; - std::string opaque_backend_state; - std::unordered_map paths; -}; - -inline bool operator==(const targets &rhs, const targets &lhs) -{ - return rhs.version == lhs.version && - rhs.opaque_backend_state == lhs.opaque_backend_state && - std::equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin()); -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/tuf/get_configs_request.hpp b/appsec/src/helper/remote_config/protocol/tuf/get_configs_request.hpp deleted file mode 100644 index dd7a6523f2..0000000000 --- a/appsec/src/helper/remote_config/protocol/tuf/get_configs_request.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include - -#include "../cached_target_files.hpp" -#include "../client.hpp" - -namespace dds::remote_config::protocol { - -struct get_configs_request { -public: - protocol::client client; - std::vector cached_target_files; -}; - -inline bool operator==( - const get_configs_request &rhs, const get_configs_request &lhs) -{ - return rhs.client == lhs.client && - rhs.cached_target_files == lhs.cached_target_files; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/tuf/get_configs_response.hpp b/appsec/src/helper/remote_config/protocol/tuf/get_configs_response.hpp deleted file mode 100644 index 19a68e37f9..0000000000 --- a/appsec/src/helper/remote_config/protocol/tuf/get_configs_response.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include - -#include "../cached_target_files.hpp" -#include "../client.hpp" -#include "../target_file.hpp" -#include "../targets.hpp" - -namespace dds::remote_config::protocol { - -struct get_configs_response { - std::unordered_map target_files; - std::vector client_configs; - std::optional targets; -}; - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/tuf/info_response.hpp b/appsec/src/helper/remote_config/protocol/tuf/info_response.hpp deleted file mode 100644 index 5e47531f62..0000000000 --- a/appsec/src/helper/remote_config/protocol/tuf/info_response.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include - -#include "../cached_target_files.hpp" -#include "../client.hpp" -#include "../target_file.hpp" -#include "../targets.hpp" - -namespace dds::remote_config::protocol { - -struct info_response { - std::vector endpoints; -}; - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/tuf/parser.cpp b/appsec/src/helper/remote_config/protocol/tuf/parser.cpp deleted file mode 100644 index 4e327fb5f1..0000000000 --- a/appsec/src/helper/remote_config/protocol/tuf/parser.cpp +++ /dev/null @@ -1,357 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -#include -#include -#include - -#include "parser.hpp" -#include -#include - -using namespace std::literals; - -namespace dds::remote_config::protocol { - -bool validate_field_is_present(const rapidjson::Value &parent_field, - const char *key, rapidjson::Type type, - rapidjson::Value::ConstMemberIterator &output_itr, - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - const remote_config_parser_result missing, - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - const remote_config_parser_result invalid) -{ - output_itr = parent_field.FindMember(key); - - if (output_itr == parent_field.MemberEnd()) { - if (missing == remote_config_parser_result::allow_missing) { - return false; - } - throw parser_exception(missing); - } - - if (type == output_itr->value.GetType()) { - return true; - } - throw parser_exception(invalid); -} - -bool validate_field_is_present( - rapidjson::Value::ConstMemberIterator &parent_field, const char *key, - rapidjson::Type type, rapidjson::Value::ConstMemberIterator &output_itr, - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - const remote_config_parser_result &missing, - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - const remote_config_parser_result &invalid) -{ - output_itr = parent_field->value.FindMember(key); - - if (output_itr == parent_field->value.MemberEnd()) { - throw parser_exception(missing); - } - - if (type == output_itr->value.GetType()) { - return true; - } - - throw parser_exception(invalid); -} - -std::unordered_map parse_target_files( - rapidjson::Value::ConstMemberIterator target_files_itr) -{ - std::unordered_map result; - - for (rapidjson::Value::ConstValueIterator itr = - target_files_itr->value.Begin(); - itr != target_files_itr->value.End(); ++itr) { - if (!itr->IsObject()) { - throw parser_exception( - remote_config_parser_result::target_files_object_invalid); - } - - // Path checks - rapidjson::Value::ConstMemberIterator const path_itr = - itr->GetObject().FindMember("path"); - if (path_itr == itr->GetObject().MemberEnd()) { - throw parser_exception( - remote_config_parser_result::target_files_path_field_missing); - } - if (!path_itr->value.IsString()) { - throw parser_exception(remote_config_parser_result:: - target_files_path_field_invalid_type); - } - - // Raw checks - rapidjson::Value::ConstMemberIterator const raw_itr = - itr->GetObject().FindMember("raw"); - if (raw_itr == itr->GetObject().MemberEnd()) { - throw parser_exception( - remote_config_parser_result::target_files_raw_field_missing); - } - if (!raw_itr->value.IsString()) { - throw parser_exception(remote_config_parser_result:: - target_files_raw_field_invalid_type); - } - result.insert({path_itr->value.GetString(), - {path_itr->value.GetString(), raw_itr->value.GetString()}}); - } - - return result; -} - -std::vector parse_client_configs( - rapidjson::Value::ConstMemberIterator client_configs_itr) -{ - std::vector result; - - for (rapidjson::Value::ConstValueIterator itr = - client_configs_itr->value.Begin(); - itr != client_configs_itr->value.End(); ++itr) { - if (!itr->IsString()) { - throw parser_exception( - remote_config_parser_result::client_config_field_invalid_entry); - } - - result.emplace_back(itr->GetString()); - } - - return result; -} - -std::pair parse_target( - rapidjson::Value::ConstMemberIterator target_itr) -{ - rapidjson::Value::ConstMemberIterator custom_itr; - validate_field_is_present(target_itr, "custom", rapidjson::kObjectType, - custom_itr, - remote_config_parser_result::custom_path_targets_field_missing, - remote_config_parser_result::custom_path_targets_field_invalid); - rapidjson::Value::ConstMemberIterator v_itr; - validate_field_is_present(custom_itr, "v", rapidjson::kNumberType, v_itr, - remote_config_parser_result::v_path_targets_field_missing, - remote_config_parser_result::v_path_targets_field_invalid); - - rapidjson::Value::ConstMemberIterator hashes_itr; - validate_field_is_present(target_itr, "hashes", rapidjson::kObjectType, - hashes_itr, - remote_config_parser_result::hashes_path_targets_field_missing, - remote_config_parser_result::hashes_path_targets_field_invalid); - - std::unordered_map hashes_mapped; - auto hashes_object = hashes_itr->value.GetObject(); - for (rapidjson::Value::ConstMemberIterator itr = - hashes_object.MemberBegin(); - itr != hashes_object.MemberEnd(); ++itr) { - if (itr->value.GetType() != rapidjson::kStringType) { - throw parser_exception(remote_config_parser_result:: - hash_hashes_path_targets_field_invalid); - } - - std::pair const hash_pair( - itr->name.GetString(), itr->value.GetString()); - hashes_mapped.insert(hash_pair); - } - - if (hashes_mapped.empty()) { - throw parser_exception( - remote_config_parser_result::hashes_path_targets_field_empty); - } - - rapidjson::Value::ConstMemberIterator length_itr; - validate_field_is_present(target_itr, "length", rapidjson::kNumberType, - length_itr, - remote_config_parser_result::length_path_targets_field_missing, - remote_config_parser_result::length_path_targets_field_invalid); - - std::string const target_name(target_itr->name.GetString()); - path const path_object = { - v_itr->value.GetInt(), hashes_mapped, length_itr->value.GetInt()}; - - return {target_name, path_object}; -} - -targets parse_targets_signed( - rapidjson::Value::ConstMemberIterator targets_signed_itr) -{ - rapidjson::Value::ConstMemberIterator version_itr; - validate_field_is_present(targets_signed_itr, "version", - rapidjson::kNumberType, version_itr, - remote_config_parser_result::version_signed_targets_field_missing, - remote_config_parser_result::version_signed_targets_field_invalid); - - rapidjson::Value::ConstMemberIterator targets_itr; - validate_field_is_present(targets_signed_itr, "targets", - rapidjson::kObjectType, targets_itr, - remote_config_parser_result::targets_signed_targets_field_missing, - remote_config_parser_result::targets_signed_targets_field_invalid); - - std::vector> paths; - for (rapidjson::Value::ConstMemberIterator current_target = - targets_itr->value.MemberBegin(); - current_target != targets_itr->value.MemberEnd(); ++current_target) { - auto path = parse_target(current_target); - paths.push_back(path); - } - - rapidjson::Value::ConstMemberIterator type_itr; - validate_field_is_present(targets_signed_itr, "_type", - rapidjson::kStringType, type_itr, - remote_config_parser_result::type_signed_targets_field_missing, - remote_config_parser_result::type_signed_targets_field_invalid); - if ("targets"sv != type_itr->value.GetString()) { - throw parser_exception(remote_config_parser_result:: - type_signed_targets_field_invalid_type); - } - - rapidjson::Value::ConstMemberIterator custom_itr; - validate_field_is_present(targets_signed_itr, "custom", - rapidjson::kObjectType, custom_itr, - remote_config_parser_result::custom_signed_targets_field_missing, - remote_config_parser_result::custom_signed_targets_field_invalid); - - rapidjson::Value::ConstMemberIterator opaque_backend_state_itr; - validate_field_is_present(custom_itr, "opaque_backend_state", - rapidjson::kStringType, opaque_backend_state_itr, - remote_config_parser_result::obs_custom_signed_targets_field_missing, - remote_config_parser_result::obs_custom_signed_targets_field_invalid); - std::unordered_map final_paths; - for (auto &[path_str, path] : paths) { - final_paths.emplace(path_str, path); - } - return {version_itr->value.GetInt(), - opaque_backend_state_itr->value.GetString(), final_paths}; -} - -targets parse_targets(rapidjson::Value::ConstMemberIterator targets_itr) -{ - std::string const targets_encoded_content = targets_itr->value.GetString(); - - if (targets_encoded_content.empty()) { - throw parser_exception( - remote_config_parser_result::targets_field_empty); - } - - std::string base64_decoded; - try { - base64_decoded = base64_decode(targets_encoded_content, true); - } catch (std::runtime_error &error) { - throw parser_exception( - remote_config_parser_result::targets_field_invalid_base64); - } - - rapidjson::Document serialized_doc; - if (serialized_doc.Parse(base64_decoded).HasParseError()) { - throw parser_exception( - remote_config_parser_result::targets_field_invalid_json); - } - - rapidjson::Value::ConstMemberIterator signed_itr; - - // Lets validate the data and since we are there we get the iterators - validate_field_is_present(serialized_doc, "signed", rapidjson::kObjectType, - signed_itr, remote_config_parser_result::signed_targets_field_missing, - remote_config_parser_result::signed_targets_field_invalid); - - return parse_targets_signed(signed_itr); -} - -get_configs_response parse(const std::string &body) -{ - rapidjson::Document serialized_doc; - if (serialized_doc.Parse(body).HasParseError()) { - throw parser_exception(remote_config_parser_result::invalid_json); - } - if (!serialized_doc.IsObject()) { - throw parser_exception(remote_config_parser_result::invalid_response); - } - - rapidjson::Value::ConstMemberIterator target_files_itr; - rapidjson::Value::ConstMemberIterator client_configs_itr; - rapidjson::Value::ConstMemberIterator targets_itr; - - // Lets validate the data and since we are here as we get the iterators - auto validated_target_files = validate_field_is_present(serialized_doc, - "target_files", rapidjson::kArrayType, target_files_itr, - remote_config_parser_result::allow_missing, - remote_config_parser_result::target_files_field_invalid_type); - - auto validated_client_configs = validate_field_is_present(serialized_doc, - "client_configs", rapidjson::kArrayType, client_configs_itr, - remote_config_parser_result::allow_missing, - remote_config_parser_result::client_config_field_invalid_type); - - auto validated_targets = validate_field_is_present(serialized_doc, - "targets", rapidjson::kStringType, targets_itr, - remote_config_parser_result::allow_missing, - remote_config_parser_result::targets_field_invalid_type); - - std::unordered_map target_files; - if (validated_target_files) { - target_files = parse_target_files(target_files_itr); - } - std::vector client_configs; - if (validated_client_configs) { - client_configs = parse_client_configs(client_configs_itr); - } - - std::optional targets; - if (validated_targets) { - targets = parse_targets(targets_itr); - } - - return {target_files, client_configs, targets}; -} - -info_response parse_info(const std::string &body) -{ - info_response response; - rapidjson::Document serialized_doc; - if (serialized_doc.Parse(body).HasParseError()) { - throw parser_exception(remote_config_parser_result::invalid_json); - } - if (!serialized_doc.IsObject()) { - throw parser_exception(remote_config_parser_result::invalid_response); - } - - rapidjson::Value::ConstMemberIterator endpoints_itr; - validate_field_is_present(serialized_doc, "endpoints", - rapidjson::kArrayType, endpoints_itr, - remote_config_parser_result::endpoints_field_missing, - remote_config_parser_result::endpoints_field_invalid); - - for (rapidjson::Value::ConstValueIterator itr = - endpoints_itr->value.Begin(); - itr != endpoints_itr->value.End(); ++itr) { - if (itr->GetType() != rapidjson::kStringType) { - throw parser_exception( - remote_config_parser_result::invalid_endpoint); - } - - response.endpoints.emplace_back(itr->GetString()); - } - - return response; -} - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define RESULT_AS_STR(entry) #entry, -namespace { -constexpr std::array - results_as_str = {PARSER_RESULTS(RESULT_AS_STR)}; -} // anonymous namespace -std::string_view remote_config_parser_result_to_str( - const remote_config_parser_result &result) -{ - if (result == remote_config_parser_result::num_of_values) { - return ""; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) - return results_as_str[(size_t)result]; -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/tuf/parser.hpp b/appsec/src/helper/remote_config/protocol/tuf/parser.hpp deleted file mode 100644 index 4693199654..0000000000 --- a/appsec/src/helper/remote_config/protocol/tuf/parser.hpp +++ /dev/null @@ -1,86 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include -#include - -#include "get_configs_response.hpp" -#include "info_response.hpp" - -namespace dds::remote_config::protocol { -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define PARSER_RESULTS(X) \ - X(success) \ - X(allow_missing) \ - X(invalid_json) \ - X(targets_field_empty) \ - X(targets_field_invalid_base64) \ - X(targets_field_invalid_json) \ - X(targets_field_invalid_type) \ - X(signed_targets_field_invalid) \ - X(signed_targets_field_missing) \ - X(type_signed_targets_field_invalid) \ - X(type_signed_targets_field_invalid_type) \ - X(type_signed_targets_field_missing) \ - X(version_signed_targets_field_invalid) \ - X(version_signed_targets_field_missing) \ - X(custom_signed_targets_field_invalid) \ - X(custom_signed_targets_field_missing) \ - X(obs_custom_signed_targets_field_invalid) \ - X(obs_custom_signed_targets_field_missing) \ - X(target_files_object_invalid) \ - X(target_files_field_invalid_type) \ - X(target_files_path_field_missing) \ - X(target_files_path_field_invalid_type) \ - X(target_files_raw_field_missing) \ - X(target_files_raw_field_invalid_type) \ - X(client_config_field_invalid_type) \ - X(client_config_field_invalid_entry) \ - X(targets_signed_targets_field_invalid) \ - X(targets_signed_targets_field_missing) \ - X(custom_path_targets_field_invalid) \ - X(custom_path_targets_field_missing) \ - X(v_path_targets_field_invalid) \ - X(v_path_targets_field_missing) \ - X(hashes_path_targets_field_invalid) \ - X(hashes_path_targets_field_missing) \ - X(hashes_path_targets_field_empty) \ - X(hash_hashes_path_targets_field_invalid) \ - X(length_path_targets_field_invalid) \ - X(length_path_targets_field_missing) \ - X(invalid_response) \ - X(endpoints_field_missing) \ - X(endpoints_field_invalid) \ - X(invalid_endpoint) - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define RESULT_AS_ENUM_ENTRY(entry) entry, -enum class remote_config_parser_result : size_t { - PARSER_RESULTS(RESULT_AS_ENUM_ENTRY) num_of_values -}; - -std::string_view remote_config_parser_result_to_str( - const remote_config_parser_result &result); - -class parser_exception : public std::exception { -public: - explicit parser_exception(remote_config_parser_result error) - : message_(remote_config_parser_result_to_str(error)), error_(error) - {} - virtual const char *what() { return message_.c_str(); } - remote_config_parser_result get_error() { return error_; } - -protected: - std::string message_; - remote_config_parser_result error_; -}; - -get_configs_response parse(const std::string &body); -info_response parse_info(const std::string &body); - -} // namespace dds::remote_config::protocol \ No newline at end of file diff --git a/appsec/src/helper/remote_config/protocol/tuf/serializer.cpp b/appsec/src/helper/remote_config/protocol/tuf/serializer.cpp deleted file mode 100644 index ec69b5d3ae..0000000000 --- a/appsec/src/helper/remote_config/protocol/tuf/serializer.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -#include -#include - -#include "../../../json_helper.hpp" -#include "../cached_target_files.hpp" -#include "base64.h" -#include "exception.hpp" -#include "serializer.hpp" - -namespace dds::remote_config::protocol { - -void serialize_client_tracer(rapidjson::Document::AllocatorType &alloc, - rapidjson::Value &client_field, const client_tracer &client_tracer) -{ - rapidjson::Value tracer_object(rapidjson::kObjectType); - - tracer_object.AddMember("language", "php", alloc); - tracer_object.AddMember("runtime_id", client_tracer.runtime_id, alloc); - tracer_object.AddMember( - "tracer_version", client_tracer.tracer_version, alloc); - tracer_object.AddMember("service", client_tracer.service, alloc); - tracer_object.AddMember("env", client_tracer.env, alloc); - tracer_object.AddMember("app_version", client_tracer.app_version, alloc); - - rapidjson::Value extra_services_array(rapidjson::kArrayType); - - for (auto const &service_name : client_tracer.extra_services) { - rapidjson::Value service(rapidjson::kStringType); - service.SetString(service_name.c_str(), service_name.length(), alloc); - extra_services_array.PushBack(service, alloc); - } - - tracer_object.AddMember("extra_services", extra_services_array, alloc); - client_field.AddMember("client_tracer", tracer_object, alloc); -} - -void serialize_config_states(rapidjson::Document::AllocatorType &alloc, - rapidjson::Value &client_field, - const std::vector &config_states) -{ - rapidjson::Value config_states_object(rapidjson::kArrayType); - - for (const auto &config_state : config_states) { - rapidjson::Value config_state_object(rapidjson::kObjectType); - config_state_object.AddMember("id", config_state.id, alloc); - config_state_object.AddMember("version", config_state.version, alloc); - config_state_object.AddMember("product", config_state.product, alloc); - config_state_object.AddMember( - "apply_state", static_cast(config_state.apply_state), alloc); - config_state_object.AddMember( - "apply_error", config_state.apply_error, alloc); - config_states_object.PushBack(config_state_object, alloc); - } - - client_field.AddMember("config_states", config_states_object, alloc); -} - -void serialize_client_state(rapidjson::Document::AllocatorType &alloc, - rapidjson::Value &client_field, const client_state &client_state) -{ - rapidjson::Value client_state_object(rapidjson::kObjectType); - - client_state_object.AddMember( - "targets_version", client_state.targets_version, alloc); - client_state_object.AddMember("root_version", 1, alloc); - client_state_object.AddMember("has_error", client_state.has_error, alloc); - client_state_object.AddMember("error", client_state.error, alloc); - client_state_object.AddMember( - "backend_client_state", client_state.backend_client_state, alloc); - - serialize_config_states( - alloc, client_state_object, client_state.config_states); - - client_field.AddMember("state", client_state_object, alloc); -} - -void serialize_client(rapidjson::Document::AllocatorType &alloc, - rapidjson::Document &document, const client &client) -{ - rapidjson::Value client_object(rapidjson::kObjectType); - - client_object.AddMember("id", client.id, alloc); - client_object.AddMember("is_tracer", true, alloc); - - // NOLINTBEGIN - auto capabilities_int = - static_cast::type>( - client.capabilities); - char bytes[2] = {static_cast(capabilities_int >> 8), - static_cast(capabilities_int & 0x00FF)}; - - client_object.AddMember("capabilities", - base64_encode(std::string_view(bytes, 2), false), alloc); - // NOLINTEND - - rapidjson::Value products(rapidjson::kArrayType); - for (const std::string &product_str : client.products) { - products.PushBack(rapidjson::Value(product_str, alloc).Move(), alloc); - } - client_object.AddMember("products", products, alloc); - - serialize_client_tracer(alloc, client_object, client.client_tracer); - serialize_client_state(alloc, client_object, client.client_state); - - document.AddMember("client", client_object, alloc); -} - -void serialize_cached_target_files_hashes( - rapidjson::Document::AllocatorType &alloc, rapidjson::Value &parent, - const std::vector &cached_target_files_hash_list) -{ - rapidjson::Value cached_target_files_array(rapidjson::kArrayType); - - for (const cached_target_files_hash &ctfh : cached_target_files_hash_list) { - rapidjson::Value cached_target_file_hash_object(rapidjson::kObjectType); - cached_target_file_hash_object.AddMember( - "algorithm", ctfh.algorithm, alloc); - cached_target_file_hash_object.AddMember("hash", ctfh.hash, alloc); - cached_target_files_array.PushBack( - cached_target_file_hash_object, alloc); - } - - parent.AddMember("hashes", cached_target_files_array, alloc); -} - -void serialize_cached_target_files(rapidjson::Document::AllocatorType &alloc, - rapidjson::Document &document, - const std::vector &cached_target_files_list) -{ - rapidjson::Value cached_target_files_array(rapidjson::kArrayType); - - for (const cached_target_files &ctf : cached_target_files_list) { - rapidjson::Value cached_target_file_object(rapidjson::kObjectType); - cached_target_file_object.AddMember("path", ctf.path, alloc); - cached_target_file_object.AddMember("length", ctf.length, alloc); - serialize_cached_target_files_hashes( - alloc, cached_target_file_object, ctf.hashes); - cached_target_files_array.PushBack(cached_target_file_object, alloc); - } - - document.AddMember("cached_target_files", cached_target_files_array, alloc); -} - -std::string serialize(const get_configs_request &request) -{ - rapidjson::Document document; - rapidjson::Document::AllocatorType &alloc = document.GetAllocator(); - - document.SetObject(); - - serialize_client(alloc, document, request.client); - serialize_cached_target_files(alloc, document, request.cached_target_files); - - dds::string_buffer buffer; - rapidjson::Writer writer(buffer); - - // This has to be tested - if (!document.Accept(writer)) { - throw serializer_exception(); - } - - return buffer.get_string_ref(); -} - -} // namespace dds::remote_config::protocol diff --git a/appsec/src/helper/remote_config/protocol/tuf/serializer.hpp b/appsec/src/helper/remote_config/protocol/tuf/serializer.hpp deleted file mode 100644 index 4e0bf009ef..0000000000 --- a/appsec/src/helper/remote_config/protocol/tuf/serializer.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include -#include - -#include "get_configs_request.hpp" - -namespace dds::remote_config::protocol { - -class serializer_exception : public std::exception {}; - -std::string serialize(const get_configs_request &request); - -} // namespace dds::remote_config::protocol \ No newline at end of file diff --git a/appsec/src/helper/remote_config/settings.hpp b/appsec/src/helper/remote_config/settings.hpp index 955ed14a5b..43a7b8340d 100644 --- a/appsec/src/helper/remote_config/settings.hpp +++ b/appsec/src/helper/remote_config/settings.hpp @@ -6,7 +6,7 @@ #pragma once -#include "utils.hpp" +#include "../utils.hpp" #include #include #include @@ -15,45 +15,31 @@ namespace dds::remote_config { -/* client_settings are currently the same for the whole client session. - * If this changes in the future, it will make sense to create a separation - * between 1) settings used for creating the engine and 2) settings used - * after, possibly when creating the subscriber listeners on every request - */ struct settings { - static constexpr uint32_t default_poll_interval{1000}; - static constexpr unsigned default_port{8126}; - // Remote config settings - bool enabled{false}; - std::string host; - unsigned port = default_port; - std::uint32_t poll_interval = default_poll_interval; - - // these two are specified in RCTE1 - // std::string targets_key; - // std::string targets_key_id; - // bool integrity_check_enabled{false}; - - MSGPACK_DEFINE_MAP(enabled, host, port, poll_interval); + bool enabled{}; + std::string shmem_path; bool operator==(const settings &oth) const noexcept { - return enabled == oth.enabled && host == oth.host && port == oth.port && - poll_interval == oth.poll_interval; + return enabled == oth.enabled && shmem_path == oth.shmem_path; } friend auto &operator<<(std::ostream &os, const settings &c) { return os << "{enabled=" << std::boolalpha << c.enabled - << ", host=" << c.host << ", port=" << c.port - << ", poll_interval=" << c.poll_interval << "}"; + << ", shmem_path=" << c.shmem_path << "}"; } - struct settings_hash { - std::size_t operator()(const settings &s) const noexcept - { - return hash(s.enabled, s.host, s.port, s.poll_interval); - } - }; + MSGPACK_DEFINE_MAP(enabled, shmem_path); }; } // namespace dds::remote_config + +namespace std { +template <> struct hash { + std::size_t operator()(const dds::remote_config::settings &s) const noexcept + { + return dds::hash(s.enabled, s.shmem_path); + } +}; + +} // namespace std diff --git a/appsec/src/helper/runner.cpp b/appsec/src/helper/runner.cpp index 58322b6ded..dee62304fd 100644 --- a/appsec/src/helper/runner.cpp +++ b/appsec/src/helper/runner.cpp @@ -13,6 +13,22 @@ #include #include +extern "C" { +#include +} + +namespace { +struct ConfigInvariants; +struct Arc_Target; + +using in_proc_notify_fn = void (*)( + const ConfigInvariants *invariants, const Arc_Target *target); + +void (*ddog_set_rc_notify_fn)(in_proc_notify_fn notify_fn); +char *(*ddog_remote_config_path)(const ConfigInvariants *, const Arc_Target *); +void (*ddog_remote_config_path_free)(char *); +} // namespace + namespace dds { namespace { @@ -85,6 +101,54 @@ runner::runner(const config::config &cfg, } } +// NOLINTNEXTLINE +std::shared_ptr runner::RUNNER_FOR_NOTIFICATIONS{nullptr}; + +void runner::register_for_rc_notifications() +{ + SPDLOG_INFO("Register RC update callback"); + std::atomic_store(&runner::RUNNER_FOR_NOTIFICATIONS, shared_from_this()); + + ddog_set_rc_notify_fn( + [](const ConfigInvariants *invariants, const Arc_Target *target) { + char *path = ddog_remote_config_path(invariants, target); + + if (path == nullptr) { + // NOLINTNEXTLINE(bugprone-lambda-function-name) + SPDLOG_ERROR("Failed to get remote config path"); + return; + } + + const std::shared_ptr runner = + std::atomic_load(&RUNNER_FOR_NOTIFICATIONS); + if (!runner) { + // NOLINTNEXTLINE(bugprone-lambda-function-name) + SPDLOG_ERROR("No runner to notify of remote config updates"); + ddog_remote_config_path_free(path); + return; + } + + // NOLINTNEXTLINE(bugprone-lambda-function-name) + SPDLOG_INFO("Remote config updated notification for {}", path); + // TODO: move the updates to a separate thread + runner->service_manager_->notify_of_rc_updates(path); + ddog_remote_config_path_free(path); + }); +} + +runner::~runner() noexcept +{ + try { + std::shared_ptr expected = shared_from_this(); + std::atomic_compare_exchange_strong(&RUNNER_FOR_NOTIFICATIONS, + &expected, std::shared_ptr(nullptr)); + } catch (...) { + // can only happened if there is no shared_ptr for the runner + // in this case a std::bad_weak_ptr is thrown + std::abort(); + } +} + void runner::run() { try { @@ -121,4 +185,31 @@ void runner::run() SPDLOG_INFO("Pool stopped"); } +void runner::resolve_symbols() +{ + // NOLINTNEXTLINE + ddog_set_rc_notify_fn = reinterpret_cast( + dlsym(RTLD_DEFAULT, "ddog_set_rc_notify_fn")); + if (ddog_set_rc_notify_fn == nullptr) { + throw std::runtime_error{"Failed to resolve ddog_set_rc_notify_fn"}; + } + + ddog_remote_config_path = + // NOLINTNEXTLINE + reinterpret_cast( + dlsym(RTLD_DEFAULT, "ddog_remote_config_path")); + if (ddog_remote_config_path == nullptr) { + throw std::runtime_error{"Failed to resolve ddog_remote_config_path"}; + } + + ddog_remote_config_path_free = + // NOLINTNEXTLINE + reinterpret_cast( + dlsym(RTLD_DEFAULT, "ddog_remote_config_path_free")); + if (ddog_remote_config_path_free == nullptr) { + throw std::runtime_error{ + "Failed to resolve ddog_remote_config_path_free"}; + } +} + } // namespace dds diff --git a/appsec/src/helper/runner.hpp b/appsec/src/helper/runner.hpp index bde65b0edf..2c495b3910 100644 --- a/appsec/src/helper/runner.hpp +++ b/appsec/src/helper/runner.hpp @@ -17,7 +17,7 @@ namespace dds { -class runner { +class runner : public std::enable_shared_from_this { public: runner(const config::config &cfg, std::atomic &interrupted); runner(const config::config &cfg, network::base_acceptor::ptr &&acceptor, @@ -26,16 +26,22 @@ class runner { runner &operator=(const runner &) = delete; runner(runner &&) = delete; runner &operator=(runner &&) = delete; - ~runner() = default; + ~runner() noexcept; + + static void resolve_symbols(); void run() noexcept(false); + void register_for_rc_notifications(); + [[nodiscard]] bool interrupted() const { return interrupted_.load(std::memory_order_acquire); } private: + static std::shared_ptr RUNNER_FOR_NOTIFICATIONS; + const config::config &cfg_; // NOLINT std::shared_ptr service_manager_; worker::pool worker_pool_; diff --git a/appsec/src/helper/service.cpp b/appsec/src/helper/service.cpp index 561675deb3..bedb5e1e2d 100644 --- a/appsec/src/helper/service.cpp +++ b/appsec/src/helper/service.cpp @@ -10,20 +10,17 @@ namespace dds { service::service(std::shared_ptr engine, std::shared_ptr service_config, - dds::remote_config::client_handler::ptr &&client_handler, + std::unique_ptr &&client_handler, + std::string rc_path, const schema_extraction_settings &schema_extraction_settings) - : engine_(std::move(engine)), service_config_(std::move(service_config)), - client_handler_(std::move(client_handler)) + : engine_{std::move(engine)}, service_config_{std::move(service_config)}, + client_handler_{std::move(client_handler)}, rc_path_{std::move(rc_path)} { // The engine should always be valid if (!engine_) { throw std::runtime_error("invalid engine"); } - if (client_handler_) { - client_handler_->start(); - } - double sample_rate = schema_extraction_settings.sample_rate; if (!schema_extraction_settings.enabled) { @@ -31,23 +28,29 @@ service::service(std::shared_ptr engine, } schema_sampler_ = std::make_shared(sample_rate); + + if (client_handler_) { + client_handler_->poll(); + } } -service::ptr service::from_settings(service_identifier &&id, +std::shared_ptr service::from_settings( const dds::engine_settings &eng_settings, const remote_config::settings &rc_settings, std::map &meta, std::map &metrics, bool dynamic_enablement) { - auto engine_ptr = engine::from_settings(eng_settings, meta, metrics); + const std::shared_ptr engine_ptr = + engine::from_settings(eng_settings, meta, metrics); auto service_config = std::make_shared(); - auto client_handler = remote_config::client_handler::from_settings( - std::move(id), eng_settings, service_config, rc_settings, engine_ptr, - dynamic_enablement); + auto client_handler = + remote_config::client_handler::from_settings(eng_settings, + service_config, rc_settings, engine_ptr, dynamic_enablement); return std::make_shared(engine_ptr, std::move(service_config), - std::move(client_handler), eng_settings.schema_extraction); + std::move(client_handler), rc_settings.shmem_path, + eng_settings.schema_extraction); } } // namespace dds diff --git a/appsec/src/helper/service.hpp b/appsec/src/helper/service.hpp index 72e3a8a5dd..233926367d 100644 --- a/appsec/src/helper/service.hpp +++ b/appsec/src/helper/service.hpp @@ -10,7 +10,6 @@ #include "remote_config/client_handler.hpp" #include "sampler.hpp" #include "service_config.hpp" -#include "service_identifier.hpp" #include "std_logging.hpp" #include "utils.hpp" #include @@ -23,11 +22,10 @@ using namespace std::chrono_literals; class service { public: - using ptr = std::shared_ptr; - service(std::shared_ptr engine, std::shared_ptr service_config, - dds::remote_config::client_handler::ptr &&client_handler, + std::unique_ptr &&client_handler, + std::string rc_path, const schema_extraction_settings &schema_extraction_settings = {}); service(const service &) = delete; @@ -38,26 +36,12 @@ class service { virtual ~service() = default; - static service::ptr from_settings(service_identifier &&id, + static std::shared_ptr from_settings( const dds::engine_settings &eng_settings, const remote_config::settings &rc_settings, std::map &meta, std::map &metrics, bool dynamic_enablement); - virtual void register_runtime_id(const std::string &id) - { - if (client_handler_) { - client_handler_->register_runtime_id(id); - } - } - - virtual void unregister_runtime_id(const std::string &id) - { - if (client_handler_) { - client_handler_->unregister_runtime_id(id); - } - } - [[nodiscard]] std::shared_ptr get_engine() const { // TODO make access atomic? @@ -75,11 +59,19 @@ class service { return schema_sampler_; } + [[nodiscard]] bool is_remote_config_shmem_path(std::string_view path) + { + return rc_path_ == path; + } + + void notify_of_rc_updates() { client_handler_->poll(); } + protected: std::shared_ptr engine_{}; std::shared_ptr service_config_{}; - dds::remote_config::client_handler::ptr client_handler_{}; + std::unique_ptr client_handler_{}; std::shared_ptr schema_sampler_; + std::string rc_path_; }; } // namespace dds diff --git a/appsec/src/helper/service_identifier.hpp b/appsec/src/helper/service_identifier.hpp deleted file mode 100644 index fb967cf820..0000000000 --- a/appsec/src/helper/service_identifier.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. -#pragma once - -#include "utils.hpp" -#include -#include - -namespace dds { -struct service_identifier { - std::string service; - std::vector extra_services; - std::string env; - std::string tracer_version; - std::string app_version; - std::string runtime_id; - - MSGPACK_DEFINE_MAP( - service, extra_services, env, tracer_version, app_version, runtime_id); - - bool operator==(const service_identifier &oth) const noexcept - { - return service == oth.service && env == oth.env; - } - - friend auto &operator<<(std::ostream &os, const service_identifier &id) - { - os << "{service=" << id.service << ", env=" << id.env - << ", tracer_version=" << id.tracer_version - << ", app_version=" << id.app_version - << ", runtime_id=" << id.runtime_id; - - os << ", extra_services=["; - for (int i = 0; i < id.extra_services.size(); i++) { - os << id.extra_services[i]; - if (i + 1 < id.extra_services.size()) { - os << ", "; - } - } - os << "]}"; - - return os; - } - - struct hash { - std::size_t operator()(const service_identifier &id) const noexcept - { - return dds::hash(id.service, id.env); - } - }; -}; -} // namespace dds diff --git a/appsec/src/helper/service_manager.cpp b/appsec/src/helper/service_manager.cpp index 6bcf70b2d3..d4c058265d 100644 --- a/appsec/src/helper/service_manager.cpp +++ b/appsec/src/helper/service_manager.cpp @@ -8,28 +8,31 @@ namespace dds { std::shared_ptr service_manager::create_service( - service_identifier &&id, const engine_settings &settings, - const remote_config::settings &rc_settings, + const engine_settings &settings, const remote_config::settings &rc_settings, std::map &meta, std::map &metrics, bool dynamic_enablement) { - const std::lock_guard guard{mutex_}; + const cache_key key{settings, rc_settings}; - auto hit = cache_.find(id); + const std::lock_guard guard{mutex_}; + auto hit = cache_.find(key); if (hit != cache_.end()) { auto service_ptr = hit->second.lock(); if (service_ptr) { // not expired SPDLOG_DEBUG( - "Found an existing service for {}::{}", id.service, id.env); + "Found an existing service for settings={} rc_settings={}", + settings, rc_settings); return service_ptr; } } - SPDLOG_DEBUG("Creating a service for {}::{}", id.service, id.env); + SPDLOG_DEBUG("Creating a service for settings={} rc_settings={}", settings, + rc_settings); + + auto service_ptr = service::from_settings( + settings, rc_settings, meta, metrics, dynamic_enablement); + cache_.emplace(key, std::move(service_ptr)); - auto service_ptr = service::from_settings(service_identifier(id), settings, - rc_settings, meta, metrics, dynamic_enablement); - cache_.emplace(std::move(id), std::move(service_ptr)); last_service_ = service_ptr; cleanup_cache(); @@ -37,6 +40,27 @@ std::shared_ptr service_manager::create_service( return service_ptr; } +void service_manager::notify_of_rc_updates(std::string_view shmem_path) +{ + std::vector> services_to_notify; + { + const std::lock_guard guard{mutex_}; + for (auto &[key, service_ptr] : cache_) { + if (key.get_shmem_path() == shmem_path) { + if (std::shared_ptr service = service_ptr.lock()) { + services_to_notify.emplace_back(std::move(service)); + } + } + } + } // release lock + + SPDLOG_DEBUG( + "Notifying {} services of RC updates", services_to_notify.size()); + for (auto &service : services_to_notify) { + service->notify_of_rc_updates(); + } +} + void service_manager::cleanup_cache() { for (auto it = cache_.begin(); it != cache_.end();) { diff --git a/appsec/src/helper/service_manager.hpp b/appsec/src/helper/service_manager.hpp index ce0561b282..d4cb7a2186 100644 --- a/appsec/src/helper/service_manager.hpp +++ b/appsec/src/helper/service_manager.hpp @@ -6,6 +6,7 @@ #pragma once #include "engine.hpp" +#include "engine_settings.hpp" #include "exception.hpp" #include "network/proto.hpp" #include "service.hpp" @@ -21,25 +22,63 @@ namespace dds { class service_manager { public: - virtual ~service_manager() = default; service_manager() = default; + service_manager(const service_manager &) = delete; + service_manager &operator=(const service_manager &) = delete; + service_manager(service_manager &&) = delete; + service_manager &operator=(service_manager &&) = delete; + virtual ~service_manager() = default; - virtual std::shared_ptr create_service(service_identifier &&id, + virtual std::shared_ptr create_service( const engine_settings &settings, const remote_config::settings &rc_settings, std::map &meta, std::map &metrics, bool dynamic_enablement); + void notify_of_rc_updates(std::string_view shmem_path); + protected: - using cache_t = std::unordered_map, service_identifier::hash>; + class cache_key { + public: + cache_key(engine_settings engine_settings, + remote_config::settings config_settings) + : engine_settings_{std::move(engine_settings)}, + config_settings_{std::move(config_settings)}, + hash_{dds::hash(engine_settings_, config_settings_)} + {} + + bool operator==(const cache_key &other) const + { + return engine_settings_ == other.engine_settings_ && + config_settings_ == other.config_settings_; + } + + struct hash { + std::size_t operator()(const cache_key &key) const + { + return key.hash_; + } + }; + + [[nodiscard]] const std::string &get_shmem_path() const + { + return config_settings_.shmem_path; + } + + private: + engine_settings engine_settings_; + remote_config::settings config_settings_; + std::size_t hash_; + }; + + using cache_t = + std::unordered_map, cache_key::hash>; void cleanup_cache(); // mutex_ must be held when calling this - // TODO this should be some sort of time-based LRU cache - service::ptr last_service_; std::mutex mutex_; cache_t cache_; + std::shared_ptr last_service_; // keep always one }; } // namespace dds diff --git a/appsec/src/helper/subscriber/base.hpp b/appsec/src/helper/subscriber/base.hpp index 1ae415b4a9..addfc6752c 100644 --- a/appsec/src/helper/subscriber/base.hpp +++ b/appsec/src/helper/subscriber/base.hpp @@ -17,12 +17,8 @@ namespace dds { class subscriber { public: - using ptr = std::shared_ptr; - class listener { public: - using ptr = std::shared_ptr; - listener() = default; listener(const listener &) = default; listener &operator=(const listener &) = delete; @@ -49,8 +45,8 @@ class subscriber { virtual std::string_view get_name() = 0; virtual std::unordered_set get_subscriptions() = 0; - virtual listener::ptr get_listener() = 0; - virtual subscriber::ptr update(parameter &rule, + virtual std::unique_ptr get_listener() = 0; + virtual std::unique_ptr update(parameter &rule, std::map &meta, std::map &metrics) = 0; }; diff --git a/appsec/src/helper/subscriber/waf.cpp b/appsec/src/helper/subscriber/waf.cpp index 03970a1920..4a5062c006 100644 --- a/appsec/src/helper/subscriber/waf.cpp +++ b/appsec/src/helper/subscriber/waf.cpp @@ -15,13 +15,14 @@ #include #include +#include "../compression.hpp" #include "../json_helper.hpp" #include "../std_logging.hpp" #include "../tags.hpp" -#include "base64.h" -#include "compression.hpp" -#include "ddwaf.h" +#include "base.hpp" #include "waf.hpp" +#include +#include namespace dds::waf { @@ -337,10 +338,10 @@ instance::~instance() } } -instance::listener::ptr instance::get_listener() +std::unique_ptr instance::get_listener() { - return listener::ptr(new listener( - ddwaf_context_init(handle_), waf_timeout_, ruleset_version_)); + return std::make_unique( + ddwaf_context_init(handle_), waf_timeout_, ruleset_version_); } instance::instance( @@ -355,7 +356,7 @@ instance::instance( for (uint32_t i = 0; i < size; i++) { addresses_.emplace(addrs[i]); } } -subscriber::ptr instance::update(parameter &rule, +std::unique_ptr instance::update(parameter &rule, std::map &meta, std::map &metrics) { @@ -376,28 +377,29 @@ subscriber::ptr instance::update(parameter &rule, throw invalid_object(); } - return subscriber::ptr( + return std::unique_ptr( new instance(new_handle, waf_timeout_, std::move(version))); } -instance::ptr instance::from_settings(const engine_settings &settings, - const engine_ruleset &ruleset, std::map &meta, +std::unique_ptr instance::from_settings( + const engine_settings &settings, const engine_ruleset &ruleset, + std::map &meta, std::map &metrics) { dds::parameter param = json_to_parameter(ruleset.get_document()); - return std::make_shared(param, meta, metrics, + return std::make_unique(param, meta, metrics, settings.waf_timeout_us, settings.obfuscator_key_regex, settings.obfuscator_value_regex); } -instance::ptr instance::from_string(std::string_view rule, +std::unique_ptr instance::from_string(std::string_view rule, std::map &meta, std::map &metrics, std::uint64_t waf_timeout_us, std::string_view key_regex, std::string_view value_regex) { engine_ruleset const ruleset{rule}; dds::parameter param = json_to_parameter(ruleset.get_document()); - return std::make_shared( + return std::make_unique( param, meta, metrics, waf_timeout_us, key_regex, value_regex); } diff --git a/appsec/src/helper/subscriber/waf.hpp b/appsec/src/helper/subscriber/waf.hpp index cafc5922df..513855ddf6 100644 --- a/appsec/src/helper/subscriber/waf.hpp +++ b/appsec/src/helper/subscriber/waf.hpp @@ -24,7 +24,7 @@ class instance : public dds::subscriber { static constexpr int default_waf_timeout_us = 10000; static constexpr int max_plain_schema_allowed = 260; static constexpr int max_schema_size = 25000; - using ptr = std::shared_ptr; + class listener : public dds::subscriber::listener { public: listener(ddwaf_context ctx, std::chrono::microseconds waf_timeout, @@ -68,18 +68,19 @@ class instance : public dds::subscriber { return addresses_; } - listener::ptr get_listener() override; + std::unique_ptr get_listener() override; - subscriber::ptr update(parameter &rule, + std::unique_ptr update(parameter &rule, std::map &meta, std::map &metrics) override; - static instance::ptr from_settings(const engine_settings &settings, - const engine_ruleset &ruleset, std::map &meta, + static std::unique_ptr from_settings( + const engine_settings &settings, const engine_ruleset &ruleset, + std::map &meta, std::map &metrics); // testing only - static instance::ptr from_string(std::string_view rule, + static std::unique_ptr from_string(std::string_view rule, std::map &meta, std::map &metrics, std::uint64_t waf_timeout_us = default_waf_timeout_us, diff --git a/appsec/src/helper/utils.hpp b/appsec/src/helper/utils.hpp index ad657beae0..5d07ae5544 100644 --- a/appsec/src/helper/utils.hpp +++ b/appsec/src/helper/utils.hpp @@ -52,4 +52,22 @@ inline std::string dd_tolower(std::string string) std::string read_file(std::string_view filename); +#ifdef __linux__ +extern "C" int __xpg_strerror_r(int, char *, size_t); +#endif +inline std::string strerror_ts(int errnum) +{ + std::string buf(256, '\0'); // NOLINT + +#ifdef __linux__ + (void)__xpg_strerror_r(errnum, buf.data(), buf.size()); +#else + (void)strerror_r(errnum, buf.data(), buf.size()); +#endif + + buf.resize(std::strlen(buf.data())); + + return buf; +} + } // namespace dds diff --git a/appsec/tests/extension/report_backtrace_01.phpt b/appsec/tests/extension/report_backtrace_01.phpt index d39c19746c..8be9d133c9 100644 --- a/appsec/tests/extension/report_backtrace_01.phpt +++ b/appsec/tests/extension/report_backtrace_01.phpt @@ -68,4 +68,4 @@ array(1) { } } } -} \ No newline at end of file +} diff --git a/appsec/tests/extension/rinit_agent_host_port_01.phpt b/appsec/tests/extension/rinit_agent_host_port_01.phpt deleted file mode 100644 index 2d4d297c62..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_01.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -Agent host and port are taken from ENV ---ENV-- -DD_AGENT_HOST=1.2.3.4 -DD_TRACE_AGENT_PORT=567 ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["host"]); -var_dump($commands[0][1][6]["port"]); - -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -string(%d) "1.2.3.4" -int(567) diff --git a/appsec/tests/extension/rinit_agent_host_port_02.phpt b/appsec/tests/extension/rinit_agent_host_port_02.phpt deleted file mode 100644 index 963b8bd136..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_02.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -Fallback to default port if given not valid ---ENV-- -DD_TRACE_AGENT_PORT=99999 ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["port"]); - -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -int(8126) diff --git a/appsec/tests/extension/rinit_agent_host_port_03.phpt b/appsec/tests/extension/rinit_agent_host_port_03.phpt deleted file mode 100644 index 4390878fa2..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_03.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -Fallback to default port if given not valid ---ENV-- -DD_TRACE_AGENT_PORT=0 ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["port"]); - -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -int(8126) diff --git a/appsec/tests/extension/rinit_agent_host_port_04.phpt b/appsec/tests/extension/rinit_agent_host_port_04.phpt deleted file mode 100644 index 38623573bf..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_04.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -Agent host and port are taken from INI ---INI-- -datadog.agent_host=1.2.3.4 -datadog.trace.agent_port=567 ---ENV-- -DD_TRACE_AGENT_PORT= -DD_AGENT_HOST= ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["host"]); -var_dump($commands[0][1][6]["port"]); -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -string(%d) "1.2.3.4" -int(567) diff --git a/appsec/tests/extension/rinit_agent_host_port_05.phpt b/appsec/tests/extension/rinit_agent_host_port_05.phpt deleted file mode 100644 index a00a4d6b07..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_05.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -Agent host and port can be taken from agent url on INI ---INI-- -datadog.trace.agent_url=http://1.2.3.4:567 ---ENV-- -DD_AGENT_HOST= ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["host"]); -var_dump($commands[0][1][6]["port"]); -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -string(%d) "1.2.3.4" -int(567) diff --git a/appsec/tests/extension/rinit_agent_host_port_06.phpt b/appsec/tests/extension/rinit_agent_host_port_06.phpt deleted file mode 100644 index 3e0f33105a..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_06.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -Agent host and port can be taken from agent url on ENV ---ENV-- -DD_TRACE_AGENT_URL=http://1.2.3.4:567 -DD_AGENT_HOST= ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["host"]); -var_dump($commands[0][1][6]["port"]); -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -string(%d) "1.2.3.4" -int(567) diff --git a/appsec/tests/extension/rinit_agent_host_port_07.phpt b/appsec/tests/extension/rinit_agent_host_port_07.phpt deleted file mode 100644 index 51825dc6a7..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_07.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -Agent host and port fallback to default when agent url ones are not valid ---ENV-- -DD_TRACE_AGENT_URL=http://:99999 -DD_TRACE_AGENT_PORT= ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["host"]); -var_dump($commands[0][1][6]["port"]); -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -string(%d) "127.0.0.1" -int(8126) diff --git a/appsec/tests/extension/rinit_agent_host_port_08.phpt b/appsec/tests/extension/rinit_agent_host_port_08.phpt deleted file mode 100644 index 7b1876d3ee..0000000000 --- a/appsec/tests/extension/rinit_agent_host_port_08.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -DD_AGENT_HOST and DD_TRACE_AGENT_PORT take priority over DD_TRACE_AGENT_URL ---ENV-- -DD_TRACE_AGENT_URL=http://2.2.2.2:1234 -DD_AGENT_HOST=1.2.3.4 -DD_TRACE_AGENT_PORT=567 ---FILE-- -get_commands(); -var_dump($commands[0][0]); //Command name - client_init -var_dump($commands[0][1][6]["host"]); -var_dump($commands[0][1][6]["port"]); -?> ---EXPECTF-- -bool(true) -bool(true) -string(%d) "client_init" -string(%d) "1.2.3.4" -int(567) diff --git a/appsec/tests/extension/rinit_rshutdown_basic.phpt b/appsec/tests/extension/rinit_rshutdown_basic.phpt index e26443a5675640653c7429393229d99b30037766..3a85e3a3c73d9c0c6396ea6c7f5557227b84d159 100644 GIT binary patch delta 49 zcmdm`-K@4jf^BmShZ56dO%CVD-JDw7(Zw0Lsk!k5i6t461Nr18=kp6so-fL`c?Gu+ F7XXb750?M{ delta 279 zcmZox+oiohf{i<|pdh|1wWv5VKW}mztIXsB{G1%Ad1bK*wss1WCvwX1q*jy^CB_%0 z7L{ctPrk?nX|d`1vyu=>$O%=HX_&=6#ZXhD8XPJCuw3D7Z#IS_dRgUN+_a{SQ+`9)A2 R7KTP, create_service, - (dds::service_identifier && id, const dds::engine_settings &settings, + (const dds::engine_settings &settings, const dds::remote_config::settings &rc_settings, (std::map & meta), (std::map & metrics), @@ -42,12 +42,8 @@ class service : public dds::service { public: service(std::shared_ptr engine, std::shared_ptr service_config) - : dds::service(engine, service_config, {}) + : dds::service(engine, service_config, {}, "/rc_path") {} - - MOCK_METHOD(void, register_runtime_id, (const std::string &id), (override)); - MOCK_METHOD( - void, unregister_runtime_id, (const std::string &id), (override)); }; } // namespace mock @@ -187,7 +183,6 @@ TEST(ClientTest, ClientInitRegisterRuntimeId) msg.pid = 1729; msg.runtime_version = "1.0"; msg.client_version = "2.0"; - msg.service.runtime_id = "thisisaruntimeid"; msg.engine_settings.rules_file = fn; network::request req(std::move(msg)); @@ -198,17 +193,11 @@ TEST(ClientTest, ClientInitRegisterRuntimeId) send(testing::An &>())) .WillOnce(DoAll(testing::SaveArg<0>(&res), Return(true))); - EXPECT_CALL(*smanager, create_service(_, _, _, _, _, true)) + EXPECT_CALL(*smanager, create_service(_, _, _, _, true)) .Times(1) .WillOnce(Return(service)); - std::string runtime_id; - EXPECT_CALL(*service, register_runtime_id(_)) - .Times(1) - .WillOnce(testing::SaveArg<0>(&runtime_id)); - EXPECT_TRUE(c.run_client_init()); - EXPECT_STREQ(runtime_id.c_str(), "thisisaruntimeid"); } TEST(ClientTest, ClientInitGeneratesRuntimeId) @@ -238,17 +227,11 @@ TEST(ClientTest, ClientInitGeneratesRuntimeId) send(testing::An &>())) .WillOnce(DoAll(testing::SaveArg<0>(&res), Return(true))); - EXPECT_CALL(*smanager, create_service(_, _, _, _, _, true)) + EXPECT_CALL(*smanager, create_service(_, _, _, _, true)) .Times(1) .WillOnce(Return(service)); - std::string runtime_id; - EXPECT_CALL(*service, register_runtime_id(_)) - .Times(1) - .WillOnce(testing::SaveArg<0>(&runtime_id)); - EXPECT_TRUE(c.run_client_init()); - EXPECT_STRNE(runtime_id.c_str(), ""); } TEST(ClientTest, ClientInitInvalidRules) @@ -1847,7 +1830,7 @@ TEST(ClientTest, ServiceIsCreatedDependingOnEnabledConfigurationValue) testing::An &>())) .WillRepeatedly(Return(true)); - EXPECT_CALL(*smanager, create_service(_, _, _, _, _, true)) + EXPECT_CALL(*smanager, create_service(_, _, _, _, true)) .Times(1) .WillOnce(Return(service)); client c(smanager, std::unique_ptr(broker)); @@ -1863,7 +1846,7 @@ TEST(ClientTest, ServiceIsCreatedDependingOnEnabledConfigurationValue) send( testing::An &>())) .WillRepeatedly(Return(true)); - EXPECT_CALL(*smanager, create_service(_, _, _, _, _, false)) + EXPECT_CALL(*smanager, create_service(_, _, _, _, false)) .Times(1) .WillOnce(Return(service)); client c(smanager, std::unique_ptr(broker)); @@ -1879,7 +1862,7 @@ TEST(ClientTest, ServiceIsCreatedDependingOnEnabledConfigurationValue) send( testing::An &>())) .WillRepeatedly(Return(true)); - EXPECT_CALL(*smanager, create_service(_, _, _, _, _, false)) + EXPECT_CALL(*smanager, create_service(_, _, _, _, false)) .Times(1) .WillOnce(Return(service)); client c(smanager, std::unique_ptr(broker)); diff --git a/appsec/tests/helper/engine_test.cpp b/appsec/tests/helper/engine_test.cpp index eaee4d231b..1778e17337 100644 --- a/appsec/tests/helper/engine_test.cpp +++ b/appsec/tests/helper/engine_test.cpp @@ -6,6 +6,7 @@ #include "common.hpp" #include "json_helper.hpp" #include +#include #include #include @@ -19,8 +20,6 @@ namespace dds { namespace mock { class listener : public dds::subscriber::listener { public: - typedef std::shared_ptr ptr; - MOCK_METHOD2(call, void(dds::parameter_view &, dds::event &)); MOCK_METHOD2( get_meta_and_metrics, void(std::map &, @@ -29,12 +28,10 @@ class listener : public dds::subscriber::listener { class subscriber : public dds::subscriber { public: - typedef std::shared_ptr ptr; - MOCK_METHOD0(get_name, std::string_view()); - MOCK_METHOD0(get_listener, dds::subscriber::listener::ptr()); + MOCK_METHOD0(get_listener, std::unique_ptr()); MOCK_METHOD0(get_subscriptions, std::unordered_set()); - MOCK_METHOD3(update, dds::subscriber::ptr(dds::parameter &, + MOCK_METHOD3(update, std::unique_ptr(dds::parameter &, std::map &meta, std::map &metrics)); }; @@ -55,17 +52,18 @@ TEST(EngineTest, SingleSubscriptor) { auto e{engine::create()}; - mock::listener::ptr listener = mock::listener::ptr(new mock::listener()); - EXPECT_CALL(*listener, call(_, _)) - .WillRepeatedly( - Invoke([](dds::parameter_view &data, dds::event &event_) -> void { - event_.actions.push_back({dds::action_type::block, {}}); - })); - - mock::subscriber::ptr sub = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Return(listener)); + auto sub = std::make_unique(); + EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Invoke([]() { + auto listener = std::make_unique(); + EXPECT_CALL(*listener, call(_, _)) + .WillRepeatedly(Invoke( + [](dds::parameter_view &data, dds::event &event_) -> void { + event_.actions.push_back({dds::action_type::block, {}}); + })); + return listener; + })); - e->subscribe(sub); + e->subscribe(std::move(sub)); auto ctx = e->get_context(); @@ -87,7 +85,8 @@ using namespace std::literals; TEST(EngineTest, MultipleSubscriptors) { auto e{engine::create()}; - mock::listener::ptr blocker = mock::listener::ptr(new mock::listener()); + + auto blocker = std::make_unique(); EXPECT_CALL(*blocker, call(_, _)) .WillRepeatedly( Invoke([](dds::parameter_view &data, dds::event &event_) -> void { @@ -98,7 +97,7 @@ TEST(EngineTest, MultipleSubscriptors) } })); - mock::listener::ptr recorder = mock::listener::ptr(new mock::listener()); + auto recorder = std::make_unique(); EXPECT_CALL(*recorder, call(_, _)) .WillRepeatedly( Invoke([](dds::parameter_view &data, dds::event &event_) -> void { @@ -108,21 +107,31 @@ TEST(EngineTest, MultipleSubscriptors) } })); - mock::listener::ptr ignorer = mock::listener::ptr(new mock::listener()); + std::unique_ptr ignorer = + std::unique_ptr(new mock::listener()); EXPECT_CALL(*ignorer, call(_, _)).Times(testing::AnyNumber()); - mock::subscriber::ptr sub1 = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub1, get_listener()).WillRepeatedly(Return(blocker)); + std::unique_ptr sub1 = + std::unique_ptr(new mock::subscriber()); + EXPECT_CALL(*sub1, get_listener()).WillRepeatedly(Invoke([&]() { + return std::move(blocker); + })); - mock::subscriber::ptr sub2 = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub2, get_listener()).WillRepeatedly(Return(recorder)); + std::unique_ptr sub2 = + std::unique_ptr(new mock::subscriber()); + EXPECT_CALL(*sub2, get_listener()).WillRepeatedly(Invoke([&]() { + return std::move(recorder); + })); - mock::subscriber::ptr sub3 = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub3, get_listener()).WillRepeatedly(Return(ignorer)); + std::unique_ptr sub3 = + std::unique_ptr(new mock::subscriber()); + EXPECT_CALL(*sub3, get_listener()).WillRepeatedly(Invoke([&]() { + return std::move(ignorer); + })); - e->subscribe(sub1); - e->subscribe(sub2); - e->subscribe(sub3); + e->subscribe(std::move(sub1)); + e->subscribe(std::move(sub2)); + e->subscribe(std::move(sub3)); auto ctx = e->get_context(); @@ -194,21 +203,23 @@ TEST(EngineTest, StatefulSubscriptor) auto e{engine::create()}; int attempt = 0; - mock::listener::ptr listener = mock::listener::ptr(new mock::listener()); - EXPECT_CALL(*listener, call(_, _)) - .Times(6) - .WillRepeatedly(Invoke( - [&attempt](dds::parameter_view &data, dds::event &event_) -> void { + + auto sub = std::make_unique(); + EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Invoke([&]() { + auto listener = std::make_unique(); + EXPECT_CALL(*listener, call(_, _)) + .Times(3) + .WillRepeatedly(Invoke([&attempt](dds::parameter_view &data, + dds::event &event_) -> void { if (attempt == 2 || attempt == 5) { event_.actions.push_back({dds::action_type::block, {}}); } attempt++; })); + return listener; + })); - mock::subscriber::ptr sub = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Return(listener)); - - e->subscribe(sub); + e->subscribe(std::move(sub)); auto ctx = e->get_context(); @@ -251,7 +262,7 @@ TEST(EngineTest, WafDefaultActions) { auto e{engine::create(engine_settings::default_trace_rate_limit)}; - mock::listener::ptr listener = mock::listener::ptr(new mock::listener()); + auto listener = std::make_unique(); EXPECT_CALL(*listener, call(_, _)) .WillRepeatedly(Invoke([](dds::parameter_view &data, dds::event &event_) -> void { @@ -261,10 +272,12 @@ TEST(EngineTest, WafDefaultActions) event_.actions.push_back({dds::action_type::extract_schema, {}}); })); - mock::subscriber::ptr sub = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Return(listener)); + auto sub = std::make_unique(); + EXPECT_CALL(*sub, get_listener()).WillOnce(Invoke([&]() { + return std::move(listener); + })); - e->subscribe(sub); + e->subscribe(std::move(sub)); auto ctx = e->get_context(); @@ -293,7 +306,7 @@ TEST(EngineTest, InvalidActionsAreDiscarded) { auto e{engine::create(engine_settings::default_trace_rate_limit)}; - mock::listener::ptr listener = mock::listener::ptr(new mock::listener()); + auto listener = std::make_unique(); EXPECT_CALL(*listener, call(_, _)) .WillRepeatedly( Invoke([](dds::parameter_view &data, dds::event &event_) -> void { @@ -301,10 +314,12 @@ TEST(EngineTest, InvalidActionsAreDiscarded) event_.actions.push_back({dds::action_type::block, {}}); })); - mock::subscriber::ptr sub = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Return(listener)); + auto sub = std::make_unique(); + EXPECT_CALL(*sub, get_listener()).WillOnce(Invoke([&]() { + return std::move(listener); + })); - e->subscribe(sub); + e->subscribe(std::move(sub)); auto ctx = e->get_context(); @@ -330,8 +345,10 @@ TEST(EngineTest, WafSubscriptorBasic) auto e{engine::create()}; - auto waf_ptr = waf::instance::from_string(waf_rule, meta, metrics); - e->subscribe(waf_ptr); + auto waf_uniq_ptr = waf::instance::from_string(waf_rule, meta, metrics); + auto *waf_ptr = waf_uniq_ptr.get(); + + e->subscribe(std::move(waf_uniq_ptr)); EXPECT_STREQ(waf_ptr->get_name().data(), "waf"); @@ -390,27 +407,36 @@ TEST(EngineTest, MockSubscriptorsUpdateRuleData) { auto e{engine::create()}; - mock::listener::ptr ignorer = mock::listener::ptr(new mock::listener()); - EXPECT_CALL(*ignorer, call(_, _)).Times(testing::AnyNumber()); - - mock::subscriber::ptr new_sub1 = - mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*new_sub1, get_listener()).WillOnce(Return(ignorer)); - - mock::subscriber::ptr sub1 = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub1, update(_, _, _)).WillOnce(Return(new_sub1)); + auto ignorer = []() { + auto listener = std::make_unique(); + EXPECT_CALL(*listener, call(_, _)).Times(testing::AnyNumber()); + return listener; + }; + + auto new_sub1 = std::make_unique(); + EXPECT_CALL(*new_sub1, get_listener()).WillOnce(Invoke([&]() { + return ignorer(); + })); + + auto sub1 = std::make_unique(); + EXPECT_CALL(*sub1, update(_, _, _)).WillOnce(Invoke([&]() { + return std::move(new_sub1); + })); EXPECT_CALL(*sub1, get_name()).WillRepeatedly(Return("")); - mock::subscriber::ptr new_sub2 = - mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*new_sub2, get_listener()).WillOnce(Return(ignorer)); + auto new_sub2 = std::make_unique(); + EXPECT_CALL(*new_sub2, get_listener()).WillOnce(Invoke([&]() { + return ignorer(); + })); - mock::subscriber::ptr sub2 = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub2, update(_, _, _)).WillOnce(Return(new_sub2)); + auto sub2 = std::make_unique(); + EXPECT_CALL(*sub2, update(_, _, _)).WillOnce(Invoke([&]() { + return std::move(new_sub2); + })); EXPECT_CALL(*sub2, get_name()).WillRepeatedly(Return("")); - e->subscribe(sub1); - e->subscribe(sub2); + e->subscribe(std::move(sub1)); + e->subscribe(std::move(sub2)); std::map meta; std::map metrics; @@ -433,21 +459,28 @@ TEST(EngineTest, MockSubscriptorsInvalidRuleData) { auto e{engine::create()}; - mock::listener::ptr ignorer = mock::listener::ptr(new mock::listener()); - EXPECT_CALL(*ignorer, call(_, _)).Times(testing::AnyNumber()); + auto ignorer = []() { + auto listener = std::make_unique(); + EXPECT_CALL(*listener, call(_, _)).Times(testing::AnyNumber()); + return listener; + }; - mock::subscriber::ptr sub1 = mock::subscriber::ptr(new mock::subscriber()); + auto sub1 = std::make_unique(); EXPECT_CALL(*sub1, update(_, _, _)).WillRepeatedly(Throw(std::exception())); EXPECT_CALL(*sub1, get_name()).WillRepeatedly(Return("")); - EXPECT_CALL(*sub1, get_listener()).WillOnce(Return(ignorer)); + EXPECT_CALL(*sub1, get_listener()).WillOnce(Invoke([&]() { + return ignorer(); + })); - mock::subscriber::ptr sub2 = mock::subscriber::ptr(new mock::subscriber()); + auto sub2 = std::make_unique(); EXPECT_CALL(*sub2, update(_, _, _)).WillRepeatedly(Throw(std::exception())); EXPECT_CALL(*sub2, get_name()).WillRepeatedly(Return("")); - EXPECT_CALL(*sub2, get_listener()).WillOnce(Return(ignorer)); + EXPECT_CALL(*sub2, get_listener()).WillOnce(Invoke([&]() { + return ignorer(); + })); - e->subscribe(sub1); - e->subscribe(sub2); + e->subscribe(std::move(sub1)); + e->subscribe(std::move(sub2)); std::map meta; std::map metrics; @@ -857,17 +890,19 @@ TEST(EngineTest, RateLimiterForceKeep) int rate_limit = 0; auto e{engine::create(rate_limit)}; - mock::listener::ptr listener = mock::listener::ptr(new mock::listener()); + auto listener = std::make_unique(); EXPECT_CALL(*listener, call(_, _)) .WillRepeatedly( Invoke([](dds::parameter_view &data, dds::event &event_) -> void { event_.actions.push_back({dds::action_type::redirect, {}}); })); - mock::subscriber::ptr sub = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Return(listener)); + auto sub = std::make_unique(); + EXPECT_CALL(*sub, get_listener()).WillOnce(Invoke([&]() { + return std::move(listener); + })); - e->subscribe(sub); + e->subscribe(std::move(sub)); parameter p = parameter::map(); p.add("a", parameter::string("value"sv)); @@ -881,17 +916,18 @@ TEST(EngineTest, RateLimiterDoNotForceKeep) int rate_limit = 1; auto e{engine::create(rate_limit)}; - mock::listener::ptr listener = mock::listener::ptr(new mock::listener()); - EXPECT_CALL(*listener, call(_, _)) - .WillRepeatedly( - Invoke([](dds::parameter_view &data, dds::event &event_) -> void { - event_.actions.push_back({dds::action_type::redirect, {}}); - })); - - mock::subscriber::ptr sub = mock::subscriber::ptr(new mock::subscriber()); - EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Return(listener)); - - e->subscribe(sub); + auto sub = std::make_unique(); + EXPECT_CALL(*sub, get_listener()).WillRepeatedly(Invoke([&]() { + auto listener = std::make_unique(); + EXPECT_CALL(*listener, call(_, _)) + .WillOnce(Invoke( + [](dds::parameter_view &data, dds::event &event_) -> void { + event_.actions.push_back({dds::action_type::redirect, {}}); + })); + return listener; + })); + + e->subscribe(std::move(sub)); parameter p = parameter::map(); p.add("a", parameter::string("value"sv)); diff --git a/appsec/tests/helper/remote_config/client_handler_test.cpp b/appsec/tests/helper/remote_config/client_handler_test.cpp deleted file mode 100644 index aefaf40f41..0000000000 --- a/appsec/tests/helper/remote_config/client_handler_test.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -#include "../common.hpp" -#include "mocks.hpp" -#include "remote_config/client_handler.hpp" - -namespace dds { - -namespace mock { - -class client_handler : public remote_config::client_handler { -public: - client_handler(remote_config::client::ptr &&rc_client, - std::shared_ptr service_config, - const std::chrono::milliseconds &poll_interval) - : remote_config::client_handler( - std::move(rc_client), service_config, poll_interval) - {} - void set_max_interval(std::chrono::milliseconds new_interval) - { - max_interval = new_interval; - } - auto get_max_interval() { return max_interval; } - const std::chrono::milliseconds get_current_interval() { return interval_; } - void tick() { remote_config::client_handler::tick(); } - - auto get_errors() { return errors_; } -}; - -} // namespace mock - -ACTION_P(SignalCall, promise) { promise->set_value(true); } - -class ClientHandlerTest : public ::testing::Test { -public: - service_identifier sid{"service", {"extra_service01", "extra_service02"}, - "env", "tracer_version", "app_version", "runtime_id"}; - dds::engine_settings settings; - remote_config::settings rc_settings; - std::shared_ptr service_config; - service_identifier id; - std::shared_ptr engine; - - void SetUp() - { - service_config = std::make_shared(); - id = sid; - engine = engine::create(); - rc_settings.enabled = true; - } -}; - -TEST_F(ClientHandlerTest, IfRemoteConfigDisabledItDoesNotGenerateHandler) -{ - rc_settings.enabled = false; - - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, service_config, rc_settings, - engine, false); - - EXPECT_FALSE(client_handler); -} - -TEST_F(ClientHandlerTest, IfNoServiceConfigProvidedItDoesNotGenerateHandler) -{ - std::shared_ptr null_service_config = {}; - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, null_service_config, rc_settings, - engine, false); - - EXPECT_FALSE(client_handler); -} - -TEST_F(ClientHandlerTest, RuntimeIdIsNotGeneratedIfProvided) -{ - const char *runtime_id = "some runtime id"; - id.runtime_id = runtime_id; - - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, service_config, rc_settings, - engine, false); - - EXPECT_STREQ(runtime_id, client_handler->get_client() - ->get_service_identifier() - .runtime_id.c_str()); -} - -TEST_F(ClientHandlerTest, AsmFeatureProductIsAddeWhenDynamicEnablement) -{ - auto dynamic_enablement = true; - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, service_config, rc_settings, - engine, dynamic_enablement); - - auto products_list = client_handler->get_client()->get_products(); - EXPECT_TRUE(products_list.find("ASM_FEATURES") != products_list.end()); -} - -TEST_F( - ClientHandlerTest, AsmFeatureProductIsNotAddeWhenDynamicEnablementDisabled) -{ - auto dynamic_enablement = false; - - // Clear rules file so at least some other products are added - settings.rules_file.clear(); - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, service_config, rc_settings, - engine, dynamic_enablement); - - auto products_list = client_handler->get_client()->get_products(); - EXPECT_TRUE(products_list.find("ASM_FEATURES") == products_list.end()); -} - -TEST_F(ClientHandlerTest, SomeProductsDependOnDynamicEngineBeingSet) -{ - { // When rules file is not set, products are added - settings.rules_file.clear(); - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, service_config, rc_settings, - engine, true); - - auto products_list = client_handler->get_client()->get_products(); - EXPECT_TRUE(products_list.find("ASM_DATA") != products_list.end()); - EXPECT_TRUE(products_list.find("ASM_DD") != products_list.end()); - EXPECT_TRUE(products_list.find("ASM") != products_list.end()); - } - - { // When rules file is set, products not are added - settings.rules_file = "/some/file"; - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, service_config, rc_settings, - engine, true); - - auto products_list = client_handler->get_client()->get_products(); - EXPECT_TRUE(products_list.find("ASM_DATA") == products_list.end()); - EXPECT_TRUE(products_list.find("ASM_DD") == products_list.end()); - EXPECT_TRUE(products_list.find("ASM") == products_list.end()); - } -} - -TEST_F(ClientHandlerTest, IfNoProductsAreRequiredRemoteClientIsNotGenerated) -{ - settings.rules_file = "/some/file"; - auto dynamic_enablement = false; - auto client_handler = remote_config::client_handler::from_settings( - dds::service_identifier(id), settings, service_config, rc_settings, - engine, dynamic_enablement); - - EXPECT_FALSE(client_handler); -} - -TEST_F(ClientHandlerTest, ValidateRCThread) -{ - std::promise poll_call_promise; - auto poll_call_future = poll_call_promise.get_future(); - std::promise available_call_promise; - auto available_call_future = available_call_promise.get_future(); - - auto rc_client = std::make_unique(sid); - EXPECT_CALL(*rc_client, is_remote_config_available) - .Times(1) - .WillOnce(DoAll(SignalCall(&available_call_promise), Return(true))); - EXPECT_CALL(*rc_client, poll) - .Times(1) - .WillOnce(DoAll(SignalCall(&poll_call_promise), Return(true))); - - auto client_handler = remote_config::client_handler( - std::move(rc_client), service_config, 200ms); - - client_handler.start(); - - // wait a little bit - this might end up being flaky - poll_call_future.wait_for(400ms); - available_call_future.wait_for(200ms); -} - -TEST_F(ClientHandlerTest, WhenRcNotAvailableItKeepsDiscovering) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - EXPECT_CALL(*rc_client, is_remote_config_available) - .Times(2) - .WillOnce(Return(false)) - .WillOnce(Return(false)); - EXPECT_CALL(*rc_client, poll).Times(0); - - auto client_handler = - mock::client_handler(std::move(rc_client), service_config, 500ms); - - client_handler.tick(); - client_handler.tick(); -} - -TEST_F(ClientHandlerTest, WhenPollFailsItGoesBackToDiscovering) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - EXPECT_CALL(*rc_client, is_remote_config_available) - .Times(2) - .WillOnce(Return(true)) - .WillOnce(Return(true)); - EXPECT_CALL(*rc_client, poll) - .Times(1) - .WillOnce(Throw(dds::remote_config::network_exception("some"))); - - auto client_handler = - mock::client_handler(std::move(rc_client), service_config, 500ms); - client_handler.tick(); - client_handler.tick(); - client_handler.tick(); -} - -TEST_F(ClientHandlerTest, WhenDiscoverFailsItStaysOnDiscovering) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - EXPECT_CALL(*rc_client, is_remote_config_available) - .Times(3) - .WillOnce(Return(false)) - .WillOnce(Throw(dds::remote_config::network_exception("some"))) - .WillOnce(Throw(dds::remote_config::network_exception("some"))); - EXPECT_CALL(*rc_client, poll).Times(0); - - auto client_handler = - mock::client_handler(std::move(rc_client), service_config, 50ms); - client_handler.set_max_interval(100ms); - client_handler.tick(); - client_handler.tick(); - client_handler.tick(); -} - -TEST_F(ClientHandlerTest, ItKeepsPollingWhileNoError) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - EXPECT_CALL(*rc_client, is_remote_config_available) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(*rc_client, poll) - .Times(2) - .WillOnce(Return(true)) - .WillOnce(Return(true)); - - auto client_handler = - mock::client_handler(std::move(rc_client), service_config, 500ms); - - client_handler.tick(); - client_handler.tick(); - client_handler.tick(); -} - -TEST_F(ClientHandlerTest, ItDoesNotStartIfNoRcClientGiven) -{ - auto rc_client = nullptr; - auto client_handler = - remote_config::client_handler(rc_client, service_config, 500ms); - - EXPECT_FALSE(client_handler.start()); -} - -TEST_F(ClientHandlerTest, ItDoesNotGoOverMaxIfGivenInitialIntervalIsLower) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - EXPECT_CALL(*rc_client, is_remote_config_available) - .Times(3) - .WillRepeatedly(Return(false)); - - auto max_interval = 300ms; - auto client_handler = - mock::client_handler(std::move(rc_client), service_config, 299ms); - client_handler.set_max_interval(max_interval); - - client_handler.tick(); - client_handler.tick(); - client_handler.tick(); - - EXPECT_EQ(max_interval, client_handler.get_current_interval()); - EXPECT_EQ(3, client_handler.get_errors()); -} - -TEST_F(ClientHandlerTest, IfInitialIntervalIsHigherThanMaxItBecomesNewMax) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - EXPECT_CALL(*rc_client, is_remote_config_available) - .Times(3) - .WillRepeatedly(Return(false)); - - auto interval = 200ms; - auto client_handler = - mock::client_handler(std::move(rc_client), service_config, interval); - client_handler.set_max_interval(100ms); - - client_handler.tick(); - client_handler.tick(); - client_handler.tick(); - - EXPECT_EQ(interval, client_handler.get_current_interval()); - EXPECT_EQ(3, client_handler.get_errors()); -} - -TEST_F(ClientHandlerTest, ByDefaultMaxIntervalisFiveMinutes) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - auto client_handler = - mock::client_handler(std::move(rc_client), service_config, 200ms); - - EXPECT_EQ(5min, client_handler.get_max_interval()); -} - -TEST_F(ClientHandlerTest, RegisterAndUnregisterRuntimeID) -{ - auto rc_client = std::make_unique( - dds::service_identifier(sid)); - EXPECT_CALL(*rc_client, register_runtime_id).Times(1); - EXPECT_CALL(*rc_client, unregister_runtime_id).Times(1); - - auto client_handler = remote_config::client_handler( - std::move(rc_client), service_config, 200ms); - - client_handler.register_runtime_id("something"); - client_handler.unregister_runtime_id("something"); -} - -} // namespace dds diff --git a/appsec/tests/helper/remote_config/client_test.cpp b/appsec/tests/helper/remote_config/client_test.cpp deleted file mode 100644 index 262960d9c6..0000000000 --- a/appsec/tests/helper/remote_config/client_test.cpp +++ /dev/null @@ -1,1348 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../common.hpp" -#include "base64.h" -#include "json_helper.hpp" -#include "remote_config/client.hpp" -#include "remote_config/exception.hpp" -#include "remote_config/listeners/listener.hpp" -#include "remote_config/product.hpp" -#include "remote_config/protocol/client.hpp" -#include "remote_config/protocol/client_state.hpp" -#include "remote_config/protocol/client_tracer.hpp" -#include "remote_config/protocol/config_state.hpp" -#include "remote_config/protocol/tuf/get_configs_request.hpp" -#include "remote_config/protocol/tuf/serializer.hpp" -#include "service_identifier.hpp" -#include "spdlog/fmt/bundled/core.h" - -using capabilities_e = dds::remote_config::protocol::capabilities_e; - -namespace dds { -class dummy_listener : public remote_config::listener_base { -public: - explicit dummy_listener(std::string_view name_ = "MOCK_PRODUCT") - : name(name_) - {} - void on_update(const remote_config::config &config) override {} - void on_unapply(const remote_config::config &config) override {} - void init() override {} - void commit() override {} - - [[nodiscard]] std::unordered_map - get_supported_products() override - { - return { - {name, remote_config::protocol::capabilities_e::ASM_ACTIVATION}}; - } - - std::string name; -}; - -namespace mock { - -// The simple custom action -ACTION_P(set_response_body, response) { arg1.assign(response); } - -ACTION(ThrowErrorApplyingConfig) -{ - throw remote_config::error_applying_config("some error"); -} - -class api : public remote_config::http_api { -public: - api() : http_api("0.0.0.0", "1234"){}; - MOCK_METHOD(std::string, get_configs, (std::string && request), (const)); -}; - -class listener_mock : public remote_config::listener_base { -public: - listener_mock() = default; - listener_mock(std::string_view name_) : name(name_) {} - ~listener_mock() override = default; - MOCK_METHOD( - void, on_update, ((const remote_config::config &config)), (override)); - MOCK_METHOD( - void, on_unapply, ((const remote_config::config &config)), (override)); - [[nodiscard]] std::unordered_map - get_supported_products() override - { - return { - {name, remote_config::protocol::capabilities_e::ASM_ACTIVATION}}; - } - - MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(void, commit, (), (override)); - std::string name{"MOCK_PRODUCT"}; -}; -} // namespace mock - -namespace test_helpers { -std::string sha256_from_path(std::string path) { return path + "_sha256"; } - -std::string raw_from_path(std::string path) { return path + "_raw"; } - -// Just a deterministic way of asserting this and avoid hadcoding much -int version_from_path(std::string path) { return path.length() + 55; } - -int length_from_path(std::string path) { return path.length(); } -} // namespace test_helpers - -class test_client : public remote_config::client { -public: - test_client(std::string id, - std::unique_ptr &&arg_api, - service_identifier &&sid, remote_config::settings &&settings, - std::vector listeners = {}) - : remote_config::client(std::move(arg_api), std::move(sid), - std::move(settings), listeners) - { - id_ = std::move(id); - } -}; - -class RemoteConfigClient : public ::testing::Test { -public: - std::string id; - std::string runtime_id; - std::string tracer_version; - std::string service; - std::vector extra_services; - std::string env; - std::string app_version; - std::string backend_client_state; - int target_version; - std::string asm_features; - std::string asm_dd; - std::string apm_sampling; - std::vector products_str; - std::vector listeners_; - std::string first_product_product; - std::string first_product_id; - std::string second_product_product; - std::string second_product_id; - std::string first_path; - std::string second_path; - std::vector paths; - remote_config::settings settings; - remote_config::protocol::capabilities_e capabilities; - - void SetUp() - { - // Since most values are moved to the classes, they need to be generated - // again on each set up - id = "some id"; - runtime_id = "some runtime id"; - tracer_version = "some tracer version"; - service = "some service"; - extra_services = {"service01", "service02"}; - env = "some env"; - app_version = "some app version"; - backend_client_state = "some backend state here"; - target_version = 123; - asm_features = "ASM_FEATURES"; - asm_dd = "ASM_DD"; - apm_sampling = "APM_SAMPLING"; - products_str = {asm_dd, asm_features}; - - first_product_product = asm_features; - first_product_id = "2.test1.config"; - first_path = "employee/" + first_product_product + "/" + - first_product_id + "/config"; - second_product_product = asm_features; - second_product_id = "luke.steensen"; - second_path = "datadog/2/" + second_product_product + "/" + - second_product_id + "/config"; - paths = {first_path, second_path}; - capabilities = remote_config::protocol::capabilities_e::ASM_ACTIVATION; - generate_listeners(); - } - - void generate_listeners() - { - for (std::string_view p_str : products_str) { - listeners_.push_back(std::make_shared(p_str)); - } - } - - remote_config::protocol::client generate_client(bool generate_state) - { - remote_config::protocol::client_tracer client_tracer = {runtime_id, - tracer_version, service, extra_services, env, app_version}; - - std::vector config_states; - int _target_version; - std::string _backend_client_state; - if (generate_state) { - // All these states are extracted from the harcoded request/response - std::string product00(first_product_product); - std::string product00_id(first_product_id); - remote_config::protocol::config_state cs00 = {product00_id, - test_helpers::version_from_path(first_path), product00, - remote_config::protocol::config_state::applied_state:: - ACKNOWLEDGED, - ""}; - std::string product01(second_product_product); - std::string product01_id(second_product_id); - remote_config::protocol::config_state cs01 = {product01_id, - test_helpers::version_from_path(second_path), product01, - remote_config::protocol::config_state::applied_state:: - ACKNOWLEDGED, - ""}; - - config_states.push_back(cs00); - config_states.push_back(cs01); - _target_version = target_version; - // This field is extracted from the harcoded response - _backend_client_state = backend_client_state; - } else { - _target_version = 0; // Default target version - _backend_client_state = ""; - } - remote_config::protocol::client_state client_state = { - _target_version, config_states, false, "", _backend_client_state}; - - auto products_str_cpy = products_str; - auto id_cpy = id; - remote_config::protocol::client c = {id_cpy, products_str_cpy, - client_tracer, client_state, capabilities}; - - return c; - } - - std::string generate_targets( - std::vector paths, std::string opaque_backend_state) - { - std::string targets_str; - for (int i = 0; i < paths.size(); i++) { - std::string path = paths[i]; - std::string sha256 = test_helpers::sha256_from_path(path); - targets_str.append( - ("\"" + path + "\": {\"custom\": {\"v\": " + - std::to_string(test_helpers::version_from_path(path)) + - " }, \"hashes\": {\"sha256\": \"" + sha256 + - "\"}, \"length\": " + - std::to_string(test_helpers::length_from_path(paths[i])) + - " }")); - if (i + 1 < paths.size()) { - targets_str.append(","); - } - } - - std::string targets_json = - ("{\"signatures\": [], \"signed\": {\"_type\": \"targets\", " - "\"custom\": {\"opaque_backend_state\": \"" + - opaque_backend_state + - "\"}, " - "\"expires\": \"2022-11-04T13:31:59Z\", \"spec_version\": " - "\"1.0.0\", \"targets\": {" + - targets_str + - "}, \"version\": " + std::to_string(target_version) + " } }"); - - return base64_encode(targets_json); - } - - std::string generate_example_response( - std::vector client_configs, - std::vector target_files, - std::vector target_paths) - { - std::string client_configs_str = ""; - std::string target_files_str = ""; - for (int i = 0; i < client_configs.size(); i++) { - client_configs_str.append("\"" + client_configs[i] + "\""); - if (i + 1 < client_configs.size()) { - client_configs_str.append(", "); - } - } - for (int i = 0; i < target_files.size(); i++) { - target_files_str.append( - "{\"path\": \"" + target_files[i] + "\", \"raw\": \"" + - test_helpers::raw_from_path(target_files[i]) + "\"}"); - if (i + 1 < target_files.size()) { - target_files_str.append(","); - } - } - return ("{\"roots\": [], \"targets\": \"" + - generate_targets(target_paths, backend_client_state) + - "\", \"target_files\": [" + target_files_str + - "], " - "\"client_configs\": [" + - client_configs_str + - "] " - "}"); - } - - std::string generate_example_response(std::vector paths) - { - return generate_example_response(paths, paths, paths); - } - - remote_config::protocol::get_configs_request generate_request( - bool generate_state, bool generate_cache) - { - dds::remote_config::protocol::client protocol_client = - generate_client(generate_state); - std::vector files; - if (generate_cache) { - // First cached file - remote_config::protocol::cached_target_files_hash hash01{ - "sha256", test_helpers::sha256_from_path(paths[0])}; - std::string path01 = paths[0]; - remote_config::protocol::cached_target_files file01 = { - path01, test_helpers::length_from_path(path01), {hash01}}; - files.push_back(file01); - - // Second cached file - remote_config::protocol::cached_target_files_hash hash02{ - "sha256", test_helpers::sha256_from_path(paths[1])}; - std::string path02 = paths[1]; - remote_config::protocol::cached_target_files file02{ - path02, test_helpers::length_from_path(path02), {hash02}}; - files.push_back(file02); - } - return {std::move(protocol_client), std::move(files)}; - } - - std::string generate_request_serialized( - bool generate_state, bool generate_cache) - { - std::optional request_serialized; - - request_serialized = remote_config::protocol::serialize( - generate_request(generate_state, generate_cache)); - - return request_serialized.value(); - } - - bool validate_request_has_error( - std::string request_serialized, bool has_error, std::string error_msg) - { - rapidjson::Document serialized_doc; - if (serialized_doc.Parse(request_serialized).HasParseError()) { - return false; - } - - rapidjson::Value::ConstMemberIterator state_itr = - serialized_doc.FindMember("client")->value.FindMember("state"); - - // Has error field - rapidjson::Value::ConstMemberIterator itr = - state_itr->value.FindMember("has_error"); - rapidjson::Type expected_type = - has_error ? rapidjson::kTrueType : rapidjson::kFalseType; - if (itr->value.GetType() != expected_type) { - return false; - } - - // Error field - itr = state_itr->value.FindMember("error"); - if (itr->value.GetType() != rapidjson::kStringType || - error_msg != itr->value.GetString()) { - return false; - } - - return true; - } -}; - -TEST_F(RemoteConfigClient, OnNetworkApiErrorTheExceptionsFlows) -{ - bool exception_thrown = false; - auto api = std::make_unique(); - EXPECT_CALL(*api, get_configs) - .WillOnce(Throw(dds::remote_config::network_exception("some"))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - try { - api_client.poll(); - } catch (dds::remote_config::network_exception & /** e*/) { - exception_thrown = true; - } - - EXPECT_TRUE(exception_thrown); -} - -std::string sort_arrays(std::string json) -{ - rapidjson::Document doc; - doc.Parse(json); - - // Sorting products - auto products = doc.FindMember("client") - ->value.FindMember("products") - ->value.GetArray(); - std::sort(products.begin(), products.end(), - [](const rapidjson::Value &lhs, const rapidjson::Value &rhs) { - return strcmp(lhs.GetString(), rhs.GetString()) < 0; - }); - - // Sorting config_states - auto config_states = doc.FindMember("client") - ->value.FindMember("state") - ->value.FindMember("config_states") - ->value.GetArray(); - std::sort(config_states.begin(), config_states.end(), - [](const rapidjson::Value &lhs, const rapidjson::Value &rhs) { - auto first = lhs.FindMember("id")->value.GetString(); - auto second = rhs.FindMember("id")->value.GetString(); - return strcmp(first, second) < 0; - }); - - // Sorting cached_target_files - auto cached_target_files = - doc.FindMember("cached_target_files")->value.GetArray(); - std::sort(cached_target_files.begin(), cached_target_files.end(), - [](const rapidjson::Value &lhs, const rapidjson::Value &rhs) { - auto first = lhs.FindMember("path")->value.GetString(); - auto second = rhs.FindMember("path")->value.GetString(); - return strcmp(first, second) < 0; - }); - - // Generate string - dds::string_buffer buffer; - rapidjson::Writer writer(buffer); - if (!doc.Accept(writer)) { - return json; - } - - return buffer.get_string_ref(); -} - -TEST_F(RemoteConfigClient, ItCallsToApiOnPoll) -{ - auto api = std::make_unique(); - std::string request_sent = ""; - EXPECT_CALL(*api, get_configs(_)) - .Times(AtLeast(1)) - .WillOnce(DoAll(testing::SaveArg<0>(&request_sent), - Return(generate_example_response(paths)))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_EQ(sort_arrays(generate_request_serialized(false, false)), - sort_arrays(request_sent)); -} - -TEST_F(RemoteConfigClient, PollFailsWithoutRuntimeID) -{ - auto api = std::make_unique(); - EXPECT_CALL(*api, get_configs(_)).Times(0); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - - EXPECT_FALSE(api_client.poll()); -} - -TEST_F(RemoteConfigClient, ReplaceRuntimeID) -{ - auto api = std::make_unique(); - std::string request_sent = ""; - EXPECT_CALL(*api, get_configs(_)) - .Times(AtLeast(1)) - .WillOnce(DoAll(testing::SaveArg<0>(&request_sent), - Return(generate_example_response(paths)))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - // Unregister the old ID and register a new one - api_client.unregister_runtime_id(runtime_id); - runtime_id = "something else"; - api_client.register_runtime_id(runtime_id); - api_client.register_runtime_id("dummy"); - api_client.register_runtime_id("unused"); - api_client.register_runtime_id("irrelevant"); - - EXPECT_TRUE(api_client.poll()); - EXPECT_EQ(sort_arrays(generate_request_serialized(false, false)), - sort_arrays(request_sent)); -} - -TEST_F(RemoteConfigClient, RemoveRuntimeID) -{ - auto api = std::make_unique(); - std::string request_sent = ""; - EXPECT_CALL(*api, get_configs(_)) - .Times(AtLeast(1)) - .WillOnce(DoAll(testing::SaveArg<0>(&request_sent), - Return(generate_example_response(paths)))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - // Unregister the ID, it should still be used - api_client.unregister_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_EQ(sort_arrays(generate_request_serialized(false, false)), - sort_arrays(request_sent)); -} - -TEST_F(RemoteConfigClient, ItReturnErrorWhenApiNotProvided) -{ - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, nullptr, std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_FALSE(api_client.poll()); -} - -TEST_F(RemoteConfigClient, ItReturnErrorWhenResponseIsInvalidJson) -{ - auto api = std::make_unique(); - EXPECT_CALL(*api, get_configs).WillOnce(Return("invalid json here")); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_FALSE(api_client.poll()); -} - -TEST_F(RemoteConfigClient, - ItReturnErrorAndSaveLastErrorWhenClientConfigPathNotInTargetPaths) -{ - std::string response = generate_example_response(paths, paths, {}); - - auto api = std::make_unique(); - std::string request_sent; - EXPECT_CALL(*api, get_configs) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(response))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - // Validate first request does not contain any error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error(request_sent, false, "")); - - // Validate second request contains error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error(request_sent, true, - "missing config " + paths[0] + - " in " - "targets")); -} - -TEST_F(RemoteConfigClient, - ItReturnErrorAndSaveLastErrorWhenClientConfigPathNotInTargetFiles) -{ - std::string response = generate_example_response(paths, {}, paths); - - auto api = std::make_unique(); - std::string request_sent; - EXPECT_CALL(*api, get_configs) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(response))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - // Validate first request does not contain any error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error(request_sent, false, "")); - - // Validate second request contains error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error(request_sent, true, - "missing config " + paths[0] + - " in " - "target files and in cache files")); -} - -TEST(ClientConfig, ItGetGeneratedFromString) -{ - std::string apm_sampling = "APM_SAMPLING"; - auto cp = remote_config::config_path::from_path( - "datadog/2/LIVE_DEBUGGING/9e413cda-647b-335b-adcd-7ce453fc2284/config"); - EXPECT_EQ("LIVE_DEBUGGING", cp.product); - EXPECT_EQ("9e413cda-647b-335b-adcd-7ce453fc2284", cp.id); - - cp = remote_config::config_path::from_path( - "employee/DEBUG_DD/2.test1.config/config"); - EXPECT_EQ("DEBUG_DD", cp.product); - EXPECT_EQ("2.test1.config", cp.id); - - cp = remote_config::config_path::from_path( - "datadog/55/APM_SAMPLING/dynamic_rates/something"); - EXPECT_EQ(apm_sampling, cp.product); - EXPECT_EQ("dynamic_rates", cp.id); -} - -TEST(ClientConfig, ItDoesNotGetGeneratedFromStringIfNotValidMatch) -{ - bool exception = false; - - try { - remote_config::config_path::from_path(""); - } catch (remote_config::invalid_path e) { - exception = true; - } - EXPECT_TRUE(exception); - exception = false; - try { - remote_config::config_path::from_path("invalid"); - } catch (remote_config::invalid_path e) { - exception = true; - } - EXPECT_TRUE(exception); - exception = false; - try { - remote_config::config_path::from_path("datadog/55/APM_SAMPLING/config"); - } catch (remote_config::invalid_path e) { - exception = true; - } - EXPECT_TRUE(exception); - exception = false; - try { - remote_config::config_path::from_path( - "datadog/55/APM_SAMPLING//config"); - } catch (remote_config::invalid_path e) { - exception = true; - } - EXPECT_TRUE(exception); - exception = false; - try { - remote_config::config_path::from_path( - "datadog/aa/APM_SAMPLING/dynamic_rates/config"); - } catch (remote_config::invalid_path e) { - exception = true; - } - EXPECT_TRUE(exception); - exception = false; - try { - remote_config::config_path::from_path( - "something/APM_SAMPLING/dynamic_rates/config"); - } catch (remote_config::invalid_path e) { - exception = true; - } - EXPECT_TRUE(exception); -} - -TEST_F(RemoteConfigClient, ItReturnsErrorWhenClientConfigPathCantBeParsed) -{ - std::string invalid_path = "invalid/path/dynamic_rates/config"; - std::string response = generate_example_response({invalid_path}); - - auto api = std::make_unique(); - std::string request_sent; - EXPECT_CALL(*api, get_configs) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(response))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - // Validate first request does not contain any error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error(request_sent, false, "")); - - // Validate second request contains error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error( - request_sent, true, "error parsing path " + invalid_path)); -} - -TEST_F(RemoteConfigClient, ItReturnsErrorIfProductOnPathNotRequested) -{ - std::string path_of_no_requested_product = - "datadog/2/APM_SAMPLING/dynamic_rates/config"; - std::string response = - generate_example_response({path_of_no_requested_product}); - - auto api = std::make_unique(); - std::string request_sent; - EXPECT_CALL(*api, get_configs) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(response))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings)); - api_client.register_runtime_id(runtime_id); - - // Validate first request does not contain any error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error(request_sent, false, "")); - - // Validate second request contains error - EXPECT_FALSE(api_client.poll()); - EXPECT_TRUE(validate_request_has_error(request_sent, true, - "received config " + path_of_no_requested_product + - " for a " - "product that was not requested")); -} - -TEST_F(RemoteConfigClient, ItGeneratesClientStateAndCacheFromResponse) -{ - auto api = std::make_unique(); - - std::string first_request = ""; - std::string second_request = ""; - - EXPECT_CALL(*api, get_configs(_)) - .Times(2) - .WillOnce(DoAll(testing::SaveArg<0>(&first_request), - Return(generate_example_response(paths)))) - .WillOnce(DoAll(testing::SaveArg<0>(&second_request), - Return(generate_example_response(paths)))) - .RetiresOnSaturation(); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - // First call should not contain state neither cache - EXPECT_EQ(sort_arrays(generate_request_serialized(false, false)), - sort_arrays(first_request)); - // Second call. This should contain state and cache from previous - EXPECT_EQ(sort_arrays(generate_request_serialized(true, true)), - sort_arrays(second_request)); -} - -TEST_F(RemoteConfigClient, WhenANewConfigIsAddedItCallsOnUpdateOnPoll) -{ - auto api = std::make_unique(); - - std::string response = generate_example_response({first_path}); - - EXPECT_CALL(*api, get_configs(_)).Times(1).WillOnce(Return(response)); - - std::string content = test_helpers::raw_from_path(first_path); - std::unordered_map hashes = { - std::pair( - "sha256", test_helpers::sha256_from_path(first_path))}; - remote_config::config expected_config = {first_product_product, - first_product_id, content, first_path, hashes, - test_helpers::version_from_path(first_path), - test_helpers::length_from_path(first_path), - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; - - // Product on response - auto listener01 = std::make_shared(); - EXPECT_CALL(*listener01, init()).Times(1); - EXPECT_CALL(*listener01, on_update(expected_config)).Times(1); - EXPECT_CALL(*listener01, on_unapply(_)).Times(0); - EXPECT_CALL(*listener01, commit()).Times(1); - listener01->name = first_product_product; - - // Product on response - auto listener_called_no_configs01 = std::make_shared(); - EXPECT_CALL(*listener_called_no_configs01, init()).Times(1); - EXPECT_CALL(*listener_called_no_configs01, on_update(_)).Times(0); - EXPECT_CALL(*listener_called_no_configs01, on_unapply(_)).Times(0); - EXPECT_CALL(*listener_called_no_configs01, commit()).Times(1); - std::string product_str_not_in_response = "NOT_IN_RESPONSE"; - listener_called_no_configs01->name = product_str_not_in_response; - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client(id, std::move(api), std::move(sid), - std::move(settings), {listener01, listener_called_no_configs01}); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); -} - -TEST_F(RemoteConfigClient, WhenAConfigDissapearOnFollowingPollsItCallsToUnApply) -{ - auto api = std::make_unique(); - - std::string response01 = generate_example_response({first_path}); - - std::string response02 = generate_example_response({second_path}); - - EXPECT_CALL(*api, get_configs(_)) - .Times(2) - .WillOnce(Return(response01)) - .WillOnce(Return(response02)); - - std::string content01 = test_helpers::raw_from_path(first_path); - std::unordered_map hashes01 = { - std::pair( - "sha256", test_helpers::sha256_from_path(first_path))}; - remote_config::config expected_config01 = {first_product_product, - first_product_id, content01, first_path, hashes01, - test_helpers::version_from_path(first_path), - test_helpers::length_from_path(first_path), - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; - - remote_config::config expected_config01_at_unapply = expected_config01; - expected_config01_at_unapply.apply_state = - remote_config::protocol::config_state::applied_state::ACKNOWLEDGED; - - std::string content02 = test_helpers::raw_from_path(second_path); - std::unordered_map hashes02 = { - std::pair( - "sha256", test_helpers::sha256_from_path(second_path))}; - remote_config::config expected_config02 = {first_product_product, - second_product_id, content02, second_path, hashes02, - test_helpers::version_from_path(second_path), - test_helpers::length_from_path(second_path), - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; - - // Product on response - auto listener01 = std::make_shared(); - EXPECT_CALL(*listener01, init()).Times(2); - // First poll expectations - EXPECT_CALL(*listener01, on_update(expected_config01)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*listener01, on_unapply(_)).Times(0); - // Second poll expectations - EXPECT_CALL(*listener01, on_update(expected_config02)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*listener01, on_unapply(expected_config01_at_unapply)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*listener01, commit()).Times(2); - // First poll expectations - listener01->name = first_product_product; - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), {listener01}); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); -} - -TEST_F( - RemoteConfigClient, WhenAConfigGetsUpdatedOnFollowingPollsItCallsToUnUpdate) -{ - auto api = std::make_unique(); - - std::string response01( - "{\"roots\": [], \"targets\": " - "\"eyAgIAogICAgInNpZ25lZCI6IHsKICAgICAgICAiX3R5cGUiOiAidGFyZ2V0cyIsCiAg" - "ICAgICAgImN1c3RvbSI6IHsKICAgICAgICAgICAgIm9wYXF1ZV9iYWNrZW5kX3N0YXRlIj" - "ogInNvbWV0aGluZyIKICAgICAgICB9LAogICAgICAgICJ0YXJnZXRzIjogewogICAgICAg" - "ICAgICAiZGF0YWRvZy8yL0FQTV9TQU1QTElORy9keW5hbWljX3JhdGVzL2NvbmZpZyI6IH" - "sKICAgICAgICAgICAgICAgICJjdXN0b20iOiB7CiAgICAgICAgICAgICAgICAgICAgInYi" - "OiAzNjc0MAogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJoYXNoZXMiOi" - "B7CiAgICAgICAgICAgICAgICAgICAgInNoYTI1NiI6ICIwNzQ2NWNlY2U0N2U0NTQyYWJj" - "MGRhMDQwZDllYmI0MmVjOTcyMjQ5MjBkNjg3MDY1MWRjMzMxNjUyODYwOWQ1IgogICAgIC" - "AgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJsZW5ndGgiOiA2NjM5OQogICAgICAg" - "ICAgICB9CiAgICAgICAgfSwKICAgICAgICAidmVyc2lvbiI6IDI3NDg3MTU2CiAgICB9Cn" - "0=\", \"target_files\": [{\"path\": " - "\"datadog/2/APM_SAMPLING/dynamic_rates/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"} ], " - "\"client_configs\": " - "[\"datadog/2/APM_SAMPLING/dynamic_rates/config\"] " - "}"); - - std::string response02( - "{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduZWQiOiB7CiAgICAgICAgICAgICAgICAiX3R5cGUiOiAidGFy" - "Z2V0cyIsCiAgICAgICAgICAgICAgICAiY3VzdG9tIjogewogICAgICAgICAgICAgICAgIC" - "AgICAgICAib3BhcXVlX2JhY2tlbmRfc3RhdGUiOiAic29tZXRoaW5nIgogICAgICAgICAg" - "ICAgICAgfSwKICAgICAgICAgICAgICAgICJ0YXJnZXRzIjogewogICAgICAgICAgICAgIC" - "AgICAgICAgICAiZGF0YWRvZy8yL0FQTV9TQU1QTElORy9keW5hbWljX3JhdGVzL2NvbmZp" - "ZyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIjogewogIC" - "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiAzNjc0MAogICAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICJzaGEyNTYiOiAiYW5vdGhlcl9oYXNoX2hlcmUiCiAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAibGVuZ3RoIjogNjYzOTkKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgIC" - "AgICAgICAgfSwKICAgICAgICAgICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgICAg" - "ICB9Cn0=\", \"target_files\": [{\"path\": " - "\"datadog/2/APM_SAMPLING/dynamic_rates/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"} ], " - "\"client_configs\": " - "[\"datadog/2/APM_SAMPLING/dynamic_rates/config\"] " - "}"); - - EXPECT_CALL(*api, get_configs(_)) - .Times(2) - .WillOnce(Return(response01)) - .WillOnce(Return(response02)); - - std::string product_str = "APM_SAMPLING"; - std::string product_str_01 = product_str; - std::string product_str_02 = product_str; - std::string id_product = "dynamic_rates"; - std::string id_product_01 = id_product; - std::string id_product_02 = id_product; - std::string path = "datadog/2/APM_SAMPLING/dynamic_rates/config"; - std::string path_01 = path; - std::string path_02 = path; - std::string content = - "UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo="; - std::string content_01 = content; - std::string content_02 = content; - std::unordered_map hashes_01 = {std::pair< - std::string, std::string>("sha256", - "07465cece47e4542abc0da040d9ebb42ec97224920d6870651dc3316528609d5")}; - remote_config::config expected_config = {product_str_01, id_product_01, - content_01, path_01, hashes_01, 36740, 66399, - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; - - std::unordered_map hashes_02 = { - std::pair("sha256", "another_hash_here")}; - remote_config::config expected_config_02 = {product_str_02, id_product_02, - content_02, path_02, hashes_02, 36740, 66399, - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; - - // Product on response - auto listener01 = std::make_shared(); - EXPECT_CALL(*listener01, init()).Times(2); - // Second poll expectations - EXPECT_CALL(*listener01, on_update(expected_config_02)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*listener01, on_unapply(_)).Times(0); - // First poll expectations - EXPECT_CALL(*listener01, on_update(expected_config)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*listener01, on_unapply(_)).Times(0); - EXPECT_CALL(*listener01, commit()).Times(2); - listener01->name = apm_sampling; - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), {listener01}); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); -} - -TEST_F(RemoteConfigClient, FilesThatAreInCacheAreUsedWhenNotInTargetFiles) -{ - auto api = std::make_unique(); - - std::string first_request = ""; - std::string second_request = ""; - std::string third_request = ""; - - EXPECT_CALL(*api, get_configs(_)) - .Times(3) - .WillOnce(DoAll(testing::SaveArg<0>(&first_request), - Return(generate_example_response(paths)))) - .WillOnce(DoAll(testing::SaveArg<0>(&second_request), - Return(generate_example_response(paths, {}, paths)))) - .WillOnce(DoAll(testing::SaveArg<0>(&third_request), - Return(generate_example_response(paths, {}, paths)))) - .RetiresOnSaturation(); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - - // First call should not contain state neither cache - EXPECT_EQ(sort_arrays(generate_request_serialized(false, false)), - sort_arrays(first_request)); - // Second call. Since this call has cache, response comes without - // target_files - EXPECT_EQ(sort_arrays(generate_request_serialized(true, true)), - sort_arrays(second_request)); - // Third call. Cache and state should be kept even though - // target_files came empty on second - EXPECT_EQ(sort_arrays(generate_request_serialized(true, true)), - sort_arrays(third_request)); -} - -TEST_F(RemoteConfigClient, NotTrackedFilesAreDeletedFromCache) -{ - auto api = std::make_unique(); - - std::string request_sent; - EXPECT_CALL(*api, get_configs(_)) - .Times(3) - .WillOnce(Return(generate_example_response(paths))) - .WillOnce(Return(generate_example_response({}))) - .WillOnce(DoAll(testing::SaveArg<0>(&request_sent), - Return(generate_example_response({})))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - - // Lets validate cached_target_files is empty - rapidjson::Document serialized_doc; - serialized_doc.Parse(request_sent); - auto output_itr = serialized_doc.FindMember("cached_target_files"); - - EXPECT_FALSE(output_itr == serialized_doc.MemberEnd()); - EXPECT_TRUE(rapidjson::kArrayType == output_itr->value.GetType()); - EXPECT_EQ(0, output_itr->value.GetArray().Size()); -} - -TEST_F(RemoteConfigClient, TestHashIsDifferentFromTheCache) -{ - auto api = std::make_unique(); - - std::string first_response = - "{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICAg" - "ICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZG" - "Y3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAg" - "ICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZDdkMj" - "JiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjliMDI2" - "NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAgICAgIC" - "AgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgICAgICAg" - "ICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImN1c3RvbSI6IH" - "sKICAgICAgICAgICAgICAgICAgICAgICAgIm9wYXF1ZV9iYWNrZW5kX3N0YXRlIjogInNv" - "bWV0aGluZyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZXhwaXJlcy" - "I6ICIyMDIyLTExLTA0VDEzOjMxOjU5WiIsCiAgICAgICAgICAgICAgICAic3BlY192ZXJz" - "aW9uIjogIjEuMC4wIiwKICAgICAgICAgICAgICAgICJ0YXJnZXRzIjogewogICAgICAgIC" - "AgICAgICAgICAgICAgICAiZW1wbG95ZWUvQVNNX0ZFQVRVUkVTLzIudGVzdDEuY29uZmln" - "L2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIj" - "ogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiAxCiAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgInNoYTI1NiI6ICJzb21lX2hhc2giCiAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGVu" - "Z3RoIjogNDEKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfS" - "wKICAgICAgICAgICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgICAgICB9Cn0=\", " - "\"target_files\": [{\"path\": " - "\"employee/ASM_FEATURES/2.test1.config/config\", \"raw\": " - "\"some_raw=\"}" - "], \"client_configs\": " - "[\"employee/ASM_FEATURES/2.test1.config/config\"]" - "}"; - - // This response has a cached file with different hash, it should not be - // used - std::string second_response = - "{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICAg" - "ICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZG" - "Y3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAg" - "ICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZDdkMj" - "JiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjliMDI2" - "NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAgICAgIC" - "AgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgICAgICAg" - "ICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImN1c3RvbSI6IH" - "sKICAgICAgICAgICAgICAgICAgICAgICAgIm9wYXF1ZV9iYWNrZW5kX3N0YXRlIjogInNv" - "bWV0aGluZyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZXhwaXJlcy" - "I6ICIyMDIyLTExLTA0VDEzOjMxOjU5WiIsCiAgICAgICAgICAgICAgICAic3BlY192ZXJz" - "aW9uIjogIjEuMC4wIiwKICAgICAgICAgICAgICAgICJ0YXJnZXRzIjogewogICAgICAgIC" - "AgICAgICAgICAgICAgICAiZW1wbG95ZWUvQVNNX0ZFQVRVUkVTLzIudGVzdDEuY29uZmln" - "L2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIj" - "ogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiAxCiAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgInNoYTI1NiI6ICJzb21lX290aGVyX2hhc2giCiAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAibGVuZ3RoIjogNDEKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgIC" - "AgICAgfSwKICAgICAgICAgICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgICAgICB9" - "Cn0=" - "\", \"target_files\": [], \"client_configs\": " - "[\"employee/ASM_FEATURES/2.test1.config/config\"] }"; - - std::string request_sent; - EXPECT_CALL(*api, get_configs(_)) - .Times(3) - .WillOnce(Return(first_response)) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(second_response))) - .RetiresOnSaturation(); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_FALSE(api_client.poll()); - EXPECT_FALSE(api_client.poll()); - - EXPECT_TRUE(validate_request_has_error(request_sent, true, - "missing config employee/ASM_FEATURES/2.test1.config/config in " - "target files and in cache files")); -} - -TEST_F(RemoteConfigClient, TestWhenFileGetsFromCacheItsCachedLenUsed) -{ - auto api = std::make_unique(); - - std::string first_response = - "{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICAg" - "ICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZG" - "Y3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAg" - "ICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZDdkMj" - "JiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjliMDI2" - "NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAgICAgIC" - "AgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgICAgICAg" - "ICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImN1c3RvbSI6IH" - "sKICAgICAgICAgICAgICAgICAgICAgICAgIm9wYXF1ZV9iYWNrZW5kX3N0YXRlIjogInNv" - "bWV0aGluZyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZXhwaXJlcy" - "I6ICIyMDIyLTExLTA0VDEzOjMxOjU5WiIsCiAgICAgICAgICAgICAgICAic3BlY192ZXJz" - "aW9uIjogIjEuMC4wIiwKICAgICAgICAgICAgICAgICJ0YXJnZXRzIjogewogICAgICAgIC" - "AgICAgICAgICAgICAgICAiZW1wbG95ZWUvQVNNX0ZFQVRVUkVTLzIudGVzdDEuY29uZmln" - "L2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIj" - "ogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiAxCiAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgInNoYTI1NiI6ICJzb21lX2hhc2giCiAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGVu" - "Z3RoIjogNDEKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfS" - "wKICAgICAgICAgICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgICAgICB9Cn0=\", " - "\"target_files\": [{\"path\": " - "\"employee/ASM_FEATURES/2.test1.config/config\", \"raw\": " - "\"some_raw=\"}" - "], \"client_configs\": " - "[\"employee/ASM_FEATURES/2.test1.config/config\"]" - "}"; - - // This response has a cached file with different len and version, it - // should not be used - std::string second_response = - "{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICAg" - "ICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZG" - "Y3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAg" - "ICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZDdkMj" - "JiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjliMDI2" - "NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAgICAgIC" - "AgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgICAgICAg" - "ICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImN1c3RvbSI6IH" - "sKICAgICAgICAgICAgICAgICAgICAgICAgIm9wYXF1ZV9iYWNrZW5kX3N0YXRlIjogInNv" - "bWV0aGluZyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZXhwaXJlcy" - "I6ICIyMDIyLTExLTA0VDEzOjMxOjU5WiIsCiAgICAgICAgICAgICAgICAic3BlY192ZXJz" - "aW9uIjogIjEuMC4wIiwKICAgICAgICAgICAgICAgICJ0YXJnZXRzIjogewogICAgICAgIC" - "AgICAgICAgICAgICAgICAiZW1wbG95ZWUvQVNNX0ZFQVRVUkVTLzIudGVzdDEuY29uZmln" - "L2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIj" - "ogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiA0CiAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgInNoYTI1NiI6ICJzb21lX2hhc2giCiAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGVu" - "Z3RoIjogNTUKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfS" - "wKICAgICAgICAgICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgICAgICB9Cn0=\", " - "\"target_files\": [], \"client_configs\": " - "[\"employee/ASM_FEATURES/2.test1.config/config\"] }"; - - std::string request_sent; - EXPECT_CALL(*api, get_configs(_)) - .Times(3) - .WillOnce(Return(first_response)) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(second_response))) - .RetiresOnSaturation(); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - - // Lets validate cached_target_files is empty - rapidjson::Document serialized_doc; - serialized_doc.Parse(request_sent); - auto output_itr = serialized_doc.FindMember("cached_target_files"); - - auto files_cached = output_itr->value.GetArray(); - EXPECT_FALSE(output_itr == serialized_doc.MemberEnd()); - EXPECT_TRUE(rapidjson::kArrayType == output_itr->value.GetType()); - EXPECT_EQ(1, files_cached.Size()); - - auto len_itr = files_cached[0].FindMember("length"); - EXPECT_FALSE(len_itr == files_cached[0].MemberEnd()); - EXPECT_TRUE(rapidjson::kNumberType == len_itr->value.GetType()); - EXPECT_EQ(41, len_itr->value.GetInt()); -} - -rapidjson::GenericArray>::ValueType> -get_config_states(const rapidjson::Document &serialized_doc) -{ - return serialized_doc.FindMember("client") - ->value.FindMember("state") - ->value.FindMember("config_states") - ->value.GetArray(); -} - -TEST_F(RemoteConfigClient, ProductsWithAListenerAcknowledgeUpdates) -{ - auto api = std::make_unique(); - - std::string response01 = generate_example_response({first_path}); - - std::string request_sent; - EXPECT_CALL(*api, get_configs(_)) - .Times(2) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(response01))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - - rapidjson::Document serialized_doc; - serialized_doc.Parse(request_sent); - - auto config_states_arr = get_config_states(serialized_doc); - - EXPECT_EQ(1, config_states_arr.Size()); - EXPECT_EQ( - (int)remote_config::protocol::config_state::applied_state::ACKNOWLEDGED, - config_states_arr[0].FindMember("apply_state")->value.GetInt()); - EXPECT_EQ("", - std::string( - config_states_arr[0].FindMember("apply_error")->value.GetString())); -} - -TEST_F(RemoteConfigClient, WhenAListerCanProccesAnUpdateTheConfigStateGetsError) -{ - auto api = std::make_unique(); - - std::string response01 = generate_example_response({first_path}); - - std::string request_sent; - EXPECT_CALL(*api, get_configs(_)) - .Times(2) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(response01))); - - auto listener = std::make_shared(); - EXPECT_CALL(*listener, init()).Times(2); - EXPECT_CALL(*listener, on_update(_)) - .WillRepeatedly(mock::ThrowErrorApplyingConfig()); - EXPECT_CALL(*listener, commit()).Times(2); - - listener->name = first_product_product; - std::vector listeners = { - listener}; - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client(id, std::move(api), std::move(sid), - std::move(settings), std::move(listeners)); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - EXPECT_TRUE(api_client.poll()); - - rapidjson::Document serialized_doc; - serialized_doc.Parse(request_sent); - - auto config_states_arr = get_config_states(serialized_doc); - - EXPECT_EQ(1, config_states_arr.Size()); - EXPECT_EQ((int)remote_config::protocol::config_state::applied_state::ERROR, - config_states_arr[0].FindMember("apply_state")->value.GetInt()); - EXPECT_EQ("some error", - std::string( - config_states_arr[0].FindMember("apply_error")->value.GetString())); -} - -TEST_F(RemoteConfigClient, OneClickActivationIsSetAsCapability) -{ - auto api = std::make_unique(); - - std::string response01 = generate_example_response({first_path}); - - std::string request_sent; - EXPECT_CALL(*api, get_configs(_)) - .Times(1) - .WillRepeatedly( - DoAll(testing::SaveArg<0>(&request_sent), Return(response01))); - - service_identifier sid{ - service, extra_services, env, tracer_version, app_version, runtime_id}; - dds::test_client api_client( - id, std::move(api), std::move(sid), std::move(settings), listeners_); - api_client.register_runtime_id(runtime_id); - - EXPECT_TRUE(api_client.poll()); - - rapidjson::Document serialized_doc; - serialized_doc.Parse(request_sent); - auto capabilities = - serialized_doc.FindMember("client")->value.FindMember("capabilities"); - - EXPECT_STREQ("AAI=", capabilities->value.GetString()); -} -} // namespace dds diff --git a/appsec/tests/helper/remote_config/listeners/asm_features_listener_test.cpp b/appsec/tests/helper/remote_config/listeners/asm_features_listener_test.cpp index 5d1af34b73..35c6e249ba 100644 --- a/appsec/tests/helper/remote_config/listeners/asm_features_listener_test.cpp +++ b/appsec/tests/helper/remote_config/listeners/asm_features_listener_test.cpp @@ -5,6 +5,7 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "../../common.hpp" +#include "../mocks.hpp" #include "base64.h" #include "remote_config/exception.hpp" #include "remote_config/listeners/asm_features_listener.hpp" @@ -12,22 +13,12 @@ namespace dds { -remote_config::config get_config(const std::string &content, bool encode = true) -{ - std::string encoded_content = content; - if (encode) { - encoded_content = base64_encode(content); - } - - return {"some product", "some id", encoded_content, "some path", {}, 123, - 321, - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; -} +namespace mock = remote_config::mock; remote_config::config get_config_with_status(std::string status) { - return get_config("{\"asm\":{\"enabled\":" + status + "}}"); + return mock::get_config( + "ASM_FEATURES", "{\"asm\":{\"enabled\":" + status + "}}"); } remote_config::config get_enabled_config(bool as_string = true) @@ -138,7 +129,7 @@ TEST(RemoteConfigAsmFeaturesListener, std::string error_message = ""; std::string expected_error_message = "Invalid config contents"; remote_config::config non_base_64_content_config = - get_config(invalid_content, false); + mock::get_config("ASM_FEATURES", invalid_content); try { listener.on_update(non_base_64_content_config); @@ -160,7 +151,8 @@ TEST(RemoteConfigAsmFeaturesListener, auto remote_config_service = std::make_shared(); remote_config::asm_features_listener listener(remote_config_service); std::string invalid_content = "invalidJsonContent"; - remote_config::config config = get_config(invalid_content); + remote_config::config config = + mock::get_config("ASM_FEATURES", invalid_content); try { listener.on_update(config); @@ -181,7 +173,8 @@ TEST(RemoteConfigAsmFeaturesListener, ListenerThrowsAnErrorWhenAsmKeyMissing) "Invalid config json encoded contents: asm key missing or invalid"; auto remote_config_service = std::make_shared(); remote_config::asm_features_listener listener(remote_config_service); - remote_config::config asm_key_missing = get_config("{}"); + remote_config::config asm_key_missing = + mock::get_config("ASM_FEATURES", "{}"); try { listener.on_update(asm_key_missing); @@ -201,7 +194,8 @@ TEST(RemoteConfigAsmFeaturesListener, ListenerThrowsAnErrorWhenAsmIsNotValid) "Invalid config json encoded contents: asm key missing or invalid"; auto remote_config_service = std::make_shared(); remote_config::asm_features_listener listener(remote_config_service); - remote_config::config invalid_asm_key = get_config("{ \"asm\": 123}"); + remote_config::config invalid_asm_key = + mock::get_config("ASM_FEATURES", "{ \"asm\": 123}"); try { listener.on_update(invalid_asm_key); @@ -222,7 +216,8 @@ TEST( "Invalid config json encoded contents: enabled key missing"; auto remote_config_service = std::make_shared(); remote_config::asm_features_listener listener(remote_config_service); - remote_config::config enabled_key_missing = get_config("{ \"asm\": {}}"); + remote_config::config enabled_key_missing = + mock::get_config("ASM_FEATURES", "{ \"asm\": {}}"); try { listener.on_update(enabled_key_missing); @@ -244,7 +239,7 @@ TEST(RemoteConfigAsmFeaturesListener, auto remote_config_service = std::make_shared(); remote_config::asm_features_listener listener(remote_config_service); remote_config::config enabled_key_invalid = - get_config("{ \"asm\": { \"enabled\": 123}}"); + mock::get_config("ASM_FEATURES", "{ \"asm\": { \"enabled\": 123}}"); try { listener.on_update(enabled_key_invalid); diff --git a/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_aggregator_test.cpp b/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_aggregator_test.cpp index 987d0edfd0..43c0974819 100644 --- a/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_aggregator_test.cpp +++ b/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_aggregator_test.cpp @@ -9,12 +9,13 @@ #include "json_helper.hpp" #include "remote_config/exception.hpp" #include "remote_config/listeners/config_aggregators/asm_aggregator.hpp" +#include "remote_config/product.hpp" #include namespace dds::remote_config { namespace { -using mock::generate_config; +using mock::get_config; const std::string waf_rule = R"({"version":"2.1","rules":[{"id":"1","name":"rule1","tags":{"type":"flow1","category":"category1"},"conditions":[{"operator":"match_regex","parameters":{"inputs":[{"address":"arg1","key_path":[]}],"regex":".*"}}]}]})"; @@ -59,8 +60,8 @@ TEST(RemoteConfigAsmAggregator, EmptyConfigThrows) rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - EXPECT_THROW(aggregator.add(generate_config("ASM", {})), - remote_config::error_applying_config); + EXPECT_THROW(aggregator.add(get_config("ASM", {})), + std::runtime_error); // mmap failure aggregator.aggregate(doc); @@ -91,7 +92,7 @@ TEST(RemoteConfigAsmAggregator, IncorrectTypeThrows) rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - EXPECT_THROW(aggregator.add(generate_config("ASM", rule_override)), + EXPECT_THROW(aggregator.add(get_config("ASM", rule_override)), remote_config::error_applying_config); aggregator.aggregate(doc); @@ -121,7 +122,7 @@ TEST(RemoteConfigAsmAggregator, RulesOverrideEmpty) rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -150,7 +151,7 @@ TEST(RemoteConfigAsmAggregator, RulesOverrideSingleConfig) rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -193,10 +194,10 @@ TEST(RemoteConfigAsmAggregator, RulesOverrideMultipleConfigs) rapidjson::Document doc(rapidjson::kObjectType); remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", rule_override)); - aggregator.add(generate_config("ASM", rule_override)); - aggregator.add(generate_config("ASM", rule_override)); - aggregator.add(generate_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -241,15 +242,15 @@ TEST(RemoteConfigAsmAggregator, RulesOverrideIgnoreInvalidConfigs) rapidjson::Document doc(rapidjson::kObjectType); remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", rule_override)); - aggregator.add(generate_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); { const std::string invalid = R"({"rules_override": {"rules_target": [{"tags": {"confidence": "1"}}], "on_match": ["block"]}})"; - EXPECT_THROW(aggregator.add(generate_config("ASM", invalid)), + EXPECT_THROW(aggregator.add(get_config("ASM", invalid)), remote_config::error_applying_config); } - aggregator.add(generate_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -295,7 +296,7 @@ TEST(RemoteConfigAsmAggregator, RulesOverridesConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -335,9 +336,9 @@ TEST(RemoteConfigAsmAggregator, RulesOverridesConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", rule_override)); - aggregator.add(generate_config("ASM", rule_override)); - aggregator.add(generate_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); + aggregator.add(get_config("ASM", rule_override)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -384,7 +385,7 @@ TEST(RemoteConfigAsmAggregator, ActionsSingleConfig) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -413,9 +414,9 @@ TEST(RemoteConfigAsmAggregator, ActionsMultipleConfigs) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", action_definitions)); - aggregator.add(generate_config("ASM", action_definitions)); - aggregator.add(generate_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -444,15 +445,15 @@ TEST(RemoteConfigAsmAggregator, ActionsIgnoreInvalidConfigs) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); { const std::string invalid = R"({"actions": {"id": "redirect", "type": "redirect_request", "parameters": {"status_code": "303", "location": "localhost"}}})"; - EXPECT_THROW(aggregator.add(generate_config("ASM", invalid)), + EXPECT_THROW(aggregator.add(get_config("ASM", invalid)), remote_config::error_applying_config); } - aggregator.add(generate_config("ASM", action_definitions)); - aggregator.add(generate_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -482,9 +483,9 @@ TEST(RemoteConfigAsmAggregator, ActionsConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", action_definitions)); - aggregator.add(generate_config("ASM", action_definitions)); - aggregator.add(generate_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -507,7 +508,7 @@ TEST(RemoteConfigAsmAggregator, ActionsConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", action_definitions)); + aggregator.add(get_config("ASM", action_definitions)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -537,7 +538,7 @@ TEST(RemoteConfigAsmAggregator, ExclusionsSingleConfig) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -566,10 +567,10 @@ TEST(RemoteConfigAsmAggregator, ExclusionsMultipleConfigs) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -598,15 +599,15 @@ TEST(RemoteConfigAsmAggregator, ExclusionsIgnoreInvalidConfigs) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); { const std::string invalid = R"({"exclusions": {"id":1,"rules_target":[{"rule_id":1}]}})"; - EXPECT_THROW(aggregator.add(generate_config("ASM", invalid)), + EXPECT_THROW(aggregator.add(get_config("ASM", invalid)), remote_config::error_applying_config); } - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -636,10 +637,10 @@ TEST(RemoteConfigAsmAggregator, ExclusionsConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -662,7 +663,7 @@ TEST(RemoteConfigAsmAggregator, ExclusionsConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -692,7 +693,7 @@ TEST(RemoteConfigAsmAggregator, CustomRulesSingleConfig) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -721,15 +722,15 @@ TEST(RemoteConfigAsmAggregator, CustomRulesMultipleConfigs) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); { const std::string invalid = R"({"custom_rules": {"id":"1","name":"custom_rule1","tags":{"type":"custom","category":"custom"},"conditions":[{"operator":"match_regex","parameters":{"inputs":[{"address":"arg3","key_path":[]}],"regex":"^custom.*"}}],"on_match":["block"]}})"; - EXPECT_THROW(aggregator.add(generate_config("ASM", invalid)), + EXPECT_THROW(aggregator.add(get_config("ASM", invalid)), remote_config::error_applying_config); } - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -758,10 +759,10 @@ TEST(RemoteConfigAsmAggregator, CustomRulesIgnoreInvalidConfigs) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -791,10 +792,10 @@ TEST(RemoteConfigAsmAggregator, CustomRulesConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -817,7 +818,7 @@ TEST(RemoteConfigAsmAggregator, CustomRulesConfigCycling) { rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -847,7 +848,7 @@ TEST(RemoteConfigAsmAggregator, AllSingleConfigs) remote_config::asm_aggregator aggregator; aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); aggregator.aggregate(doc); const auto &overrides = doc["rules_override"]; @@ -876,23 +877,23 @@ TEST(RemoteConfigAsmAggregator, IgnoreInvalidConfigs) { const std::string update = R"({"rules_override": [{"rules_target": [{"tags": {"confidence": "1"}}], "on_match": ["block"]}]})"; - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); } { const std::string update = R"({"exclusions":[{"id":1,"rules_target":[{"rule_id":1}]}]})"; - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); } { const std::string update = R"({"actions": {"id": "redirect", "type": "redirect_request", "parameters": {"status_code": "303", "location": "localhost"}}})"; - EXPECT_THROW(aggregator.add(generate_config("ASM", update)), + EXPECT_THROW(aggregator.add(get_config("ASM", update)), remote_config::error_applying_config); } { const std::string update = R"({"custom_rules":[{"id":"1","name":"custom_rule1","tags":{"type":"custom","category":"custom"},"conditions":[{"operator":"match_regex","parameters":{"inputs":[{"address":"arg3","key_path":[]}],"regex":"^custom.*"}}],"on_match":["block"]}]})"; - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); } aggregator.aggregate(doc); @@ -922,23 +923,23 @@ TEST(RemoteConfigAsmAggregator, IgnoreInvalidOverlappingConfigs) { const std::string update = R"({"rules_override": [{"rules_target": [{"tags": {"confidence": "1"}}], "on_match": ["block"]}],"exclusions":[{"id":1,"rules_target":[{"rule_id":1}]}]})"; - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); } { const std::string update = R"({"exclusions":[{"id":1,"rules_target":[{"rule_id":1}]}],"custom_rules":[{"id":"1","name":"custom_rule1","tags":{"type":"custom","category":"custom"},"conditions":[{"operator":"match_regex","parameters":{"inputs":[{"address":"arg3","key_path":[]}],"regex":"^custom.*"}}],"on_match":["block"]}]})"; - aggregator.add(generate_config("ASM", update)); + aggregator.add(get_config("ASM", update)); } { const std::string update = R"({"rules_override": [{"rules_target": [{"tags": {"confidence": "1"}}], "on_match": ["block"]}],"actions": {"id": "redirect", "type": "redirect_request", "parameters": {"status_code": "303", "location": "localhost"}}})"; - EXPECT_THROW(aggregator.add(generate_config("ASM", update)), + EXPECT_THROW(aggregator.add(get_config("ASM", update)), remote_config::error_applying_config); } { const std::string update = R"({"custom_rules":{"id":"1","name":"custom_rule1","tags":{"type":"custom","category":"custom"},"conditions":[{"operator":"match_regex","parameters":{"inputs":[{"address":"arg3","key_path":[]}],"regex":"^custom.*"}}],"on_match":["block"]}})"; - EXPECT_THROW(aggregator.add(generate_config("ASM", update)), + EXPECT_THROW(aggregator.add(get_config("ASM", update)), remote_config::error_applying_config); } aggregator.aggregate(doc); diff --git a/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_data_aggregator_test.cpp b/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_data_aggregator_test.cpp index 30a17de218..776bc51efd 100644 --- a/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_data_aggregator_test.cpp +++ b/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_data_aggregator_test.cpp @@ -15,7 +15,7 @@ namespace dds::remote_config { -using mock::generate_config; +using mock::get_config; struct test_rule_data_data { std::optional expiration; @@ -62,7 +62,7 @@ remote_config::config get_rules_data(std::vector data) rapidjson::Writer writer(buffer); document.Accept(writer); - return generate_config("ASM_DATA", buffer.get_string_ref()); + return get_config("ASM_DATA", buffer.get_string_ref()); } TEST(RemoteConfigAsmDataAggregator, ParseRulesData) @@ -335,7 +335,7 @@ TEST(RemoteConfigAsmDataAggregator, IgnoreInvalidConfigs) { const std::string &invalid = R"({"rules_data": [{"id": "id01", "data": [{"expiration": 11, "value": "1.2.3.5"} ], "type": "ip_with_expiration"},{"data": [{"expiration": 11111, "value": "1.2.3.4"} ], "type": "ip_with_expiration"}]})"; - EXPECT_THROW(aggregator.add(generate_config("ASM_DATA", invalid)), + EXPECT_THROW(aggregator.add(get_config("ASM_DATA", invalid)), remote_config::error_applying_config); } aggregator.aggregate(doc); @@ -414,8 +414,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfContentNotInBase64) { std::string invalid_content = "&&&"; std::string expected_error_message = "Invalid config contents"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, false); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -438,8 +437,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfContentNotValidJsonContent) { std::string invalid_content = "InvalidJsonContent"; std::string expected_error_message = "Invalid config contents"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -464,8 +462,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfNoRulesDataKey) std::string expected_error_message = "Invalid config json contents: rules_data key missing or " "invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -490,8 +487,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfRulesDataNotArray) std::string expected_error_message = "Invalid config json contents: rules_data key missing or " "invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -515,8 +511,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfRulesDataEntryNotObject) std::string invalid_content = "{\"rules_data\": [\"invalid\"] }"; std::string expected_error_message = "Invalid config json contents: rules_data entry invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -543,8 +538,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfNoId) std::string expected_error_message = "Invalid config json contents: rules_data missing a field or " "field is invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -571,8 +565,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfIdNotString) std::string expected_error_message = "Invalid config json contents: rules_data missing a field or " "field is invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -599,8 +592,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfNoType) std::string expected_error_message = "Invalid config json contents: rules_data missing a field or " "field is invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -627,8 +619,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfTypeNotString) std::string expected_error_message = "Invalid config json contents: rules_data missing a field or " "field is invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -654,8 +645,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfNoData) std::string expected_error_message = "Invalid config json contents: rules_data missing a field or " "field is invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -682,8 +672,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfDataNotArray) std::string expected_error_message = "Invalid config json contents: rules_data missing a field or " "field is invalid"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -708,8 +697,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfDataEntryNotObject) R"({"rules_data": [{"data": [ "invalid" ], "id": "some_id", "type": "ip_with_expiration"} ] })"; std::string expected_error_message = "Invalid config json contents: Entry on data not a valid object"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -733,8 +721,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfDataExpirationHasInvalidType) std::string invalid_content = R"({"rules_data": [{"data": [{"expiration": "invalid", "value": "1.2.3.4"}], "id": "some_id", "type": "data_with_expiration"}]})"; std::string expected_error_message = "Invalid type for expiration entry"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -759,8 +746,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfDataValueMissing) "{\"rules_data\": [{\"data\": [{\"expiration\": 11} ], \"id\": " "\"some_id\", \"type\": \"data_with_expiration\"} ] }"; std::string expected_error_message = "Invalid value of data entry"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; @@ -786,8 +772,7 @@ TEST(RemoteConfigAsmDataAggregator, ThrowsAnErrorIfDataValueHasInvalidType) "\"value\": 1234} ], \"id\": \"some_id\", \"type\": " "\"ip_with_expiration\"} ] }"; std::string expected_error_message = "Invalid value of data entry"; - remote_config::config config = - generate_config("ASM_DATA", invalid_content, true); + remote_config::config config = get_config("ASM_DATA", invalid_content); remote_config::asm_data_aggregator aggregator; diff --git a/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator_test.cpp b/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator_test.cpp index 964ab22b3b..1c9dd18f23 100644 --- a/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator_test.cpp +++ b/appsec/tests/helper/remote_config/listeners/config_aggregators/asm_dd_aggregator_test.cpp @@ -16,7 +16,7 @@ const std::string waf_rule = namespace dds::remote_config { -using mock::generate_config; +using mock::get_config; TEST(RemoteConfigAsmDdAggregator, AddConfig) { @@ -24,7 +24,7 @@ TEST(RemoteConfigAsmDdAggregator, AddConfig) rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.add(generate_config("ASM_DD", waf_rule)); + aggregator.add(get_config("ASM_DD", waf_rule)); aggregator.aggregate(doc); const auto &rules = doc["rules"]; @@ -38,7 +38,7 @@ TEST(RemoteConfigAsmDdAggregator, RemoveConfig) rapidjson::Document doc(rapidjson::kObjectType); aggregator.init(&doc.GetAllocator()); - aggregator.remove(generate_config("ASM_DD", waf_rule)); + aggregator.remove(get_config("ASM_DD", waf_rule)); aggregator.aggregate(doc); const auto &rules = doc["rules"]; @@ -55,8 +55,7 @@ TEST(RemoteConfigAsmDdAggregator, AddConfigInvalidBase64Content) std::string invalid_content = "&&&"; std::string error_message = ""; std::string expected_error_message = "Invalid config contents"; - remote_config::config config = - generate_config("ASM_DD", invalid_content, false); + remote_config::config config = get_config("ASM_DD", invalid_content); remote_config::asm_dd_aggregator aggregator; rapidjson::Document doc(rapidjson::kObjectType); @@ -79,8 +78,7 @@ TEST(RemoteConfigAsmDdAggregator, AddConfigInvalidJsonContent) std::string invalid_content = "InvalidJsonContent"; std::string error_message = ""; std::string expected_error_message = "Invalid config contents"; - remote_config::config config = - generate_config("ASM_DD", invalid_content, true); + remote_config::config config = get_config("ASM_DD", invalid_content); remote_config::asm_dd_aggregator aggregator; rapidjson::Document doc(rapidjson::kObjectType); diff --git a/appsec/tests/helper/remote_config/listeners/engine_listener_test.cpp b/appsec/tests/helper/remote_config/listeners/engine_listener_test.cpp index 44905a1c37..1e3a3d4bb1 100644 --- a/appsec/tests/helper/remote_config/listeners/engine_listener_test.cpp +++ b/appsec/tests/helper/remote_config/listeners/engine_listener_test.cpp @@ -7,11 +7,13 @@ #include "../../common.hpp" #include "../mocks.hpp" #include "base64.h" +#include "engine.hpp" #include "json_helper.hpp" #include "remote_config/exception.hpp" #include "remote_config/listeners/engine_listener.hpp" #include "remote_config/product.hpp" #include "subscriber/waf.hpp" +#include #include const std::string waf_rule = @@ -19,7 +21,7 @@ const std::string waf_rule = namespace dds::remote_config { -using mock::generate_config; +using mock::get_config; namespace { @@ -55,7 +57,7 @@ TEST(RemoteConfigEngineListener, UnknownConfig) remote_config::engine_listener listener(engine); listener.init(); - EXPECT_THROW(listener.on_update(generate_config("UNKNOWN", waf_rule)), + EXPECT_THROW(listener.on_update(get_config("UNKNOWN", waf_rule)), error_applying_config); listener.commit(); } @@ -72,7 +74,7 @@ TEST(RemoteConfigEngineListener, RuleUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM_DD", waf_rule)); listener.commit(); { @@ -102,7 +104,7 @@ TEST(RemoteConfigEngineListener, RuleUpdateFallback) remote_config::engine_listener listener(engine, create_sample_rules_ok()); listener.init(); - listener.on_unapply(generate_config("ASM_DD", waf_rule)); + listener.on_unapply(get_config("ASM_DD", waf_rule)); listener.commit(); { @@ -135,7 +137,7 @@ TEST(RemoteConfigEngineListener, RulesOverrideUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -176,8 +178,8 @@ TEST(RemoteConfigEngineListener, RulesAndRulesOverrideUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -225,7 +227,7 @@ TEST(RemoteConfigEngineListener, ExclusionsUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -267,8 +269,8 @@ TEST(RemoteConfigEngineListener, RulesAndExclusionsUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -317,7 +319,7 @@ TEST(RemoteConfigEngineListener, ActionsUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -360,8 +362,8 @@ TEST(RemoteConfigEngineListener, RulesAndActionsUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -412,7 +414,7 @@ TEST(RemoteConfigEngineListener, CustomRulesUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -457,8 +459,8 @@ TEST(RemoteConfigEngineListener, RulesAndCustomRulesUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM", update)); listener.commit(); { @@ -506,7 +508,7 @@ TEST(RemoteConfigEngineListener, RulesDataUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DATA", update)); + listener.on_update(get_config("ASM_DATA", update)); listener.commit(); { @@ -540,8 +542,8 @@ TEST(RemoteConfigEngineListener, RulesAndRuleDataUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); - listener.on_update(generate_config("ASM_DATA", update)); + listener.on_update(get_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM_DATA", update)); listener.commit(); { @@ -578,11 +580,11 @@ TEST(RemoteConfigEngineListener, FullUpdate) remote_config::engine_listener listener(engine); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM_DD", waf_rule)); { const std::string update = R"({"rules_data":[{"id":"blocked_ips","type":"ip_with_expiration","data":[{"value":"1.2.3.4","expiration":0}]}]})"; - listener.on_update(generate_config("ASM_DATA", update)); + listener.on_update(get_config("ASM_DATA", update)); } { const std::string update = @@ -591,23 +593,23 @@ TEST(RemoteConfigEngineListener, FullUpdate) {"inputs":[{"address":"arg3","key_path":[]}],"regex":"^custom.*"}}], "on_match":["block"]}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } { const std::string update = R"({"exclusions":[{"id":1,"rules_target":[{"rule_id":1}]}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } { const std::string update = R"({"actions": [{"id": "redirect", "type": "redirect_request", "parameters": {"status_code": "303", "location": "localhost"}}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } { const std::string update = R"({"rules_override": [{"rules_target": [{"rule_id": "1"}], "enabled":"false"}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } listener.commit(); @@ -634,11 +636,11 @@ TEST(RemoteConfigEngineListener, MultipleInitCommitUpdates) remote_config::engine_listener listener(engine, create_sample_rules_ok()); listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM_DD", waf_rule)); { const std::string update = R"({"rules_data":[{"id":"blocked_ips","type":"ip_with_expiration","data":[{"value":"1.2.3.4","expiration":0}]}]})"; - listener.on_update(generate_config("ASM_DATA", update)); + listener.on_update(get_config("ASM_DATA", update)); } listener.commit(); @@ -673,12 +675,12 @@ TEST(RemoteConfigEngineListener, MultipleInitCommitUpdates) {"inputs":[{"address":"arg3","key_path":[]}],"regex":"^custom.*"}}], "on_match":["block"]}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } { const std::string update = R"({"exclusions":[{"id":1,"rules_target":[{"rule_id":1}]}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } listener.commit(); @@ -714,17 +716,17 @@ TEST(RemoteConfigEngineListener, MultipleInitCommitUpdates) } listener.init(); - listener.on_update(generate_config("ASM_DD", waf_rule)); + listener.on_update(get_config("ASM_DD", waf_rule)); { const std::string update = R"({"actions": [{"id": "redirect", "type": "redirect_request", "parameters": {"status_code": "303", "location": "localhost"}}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } { const std::string update = R"({"rules_override": [{"rules_target": [{"rule_id": "1"}], "enabled":"false"}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); } listener.commit(); @@ -776,7 +778,7 @@ TEST(RemoteConfigEngineListener, EngineRuleUpdate) std::map meta; std::map metrics; - auto e{engine::create()}; + std::shared_ptr e{engine::create()}; e->subscribe(waf::instance::from_string(rules, meta, metrics)); { @@ -797,7 +799,7 @@ TEST(RemoteConfigEngineListener, EngineRuleUpdate) remote_config::engine_listener listener(e); listener.init(); - listener.on_update(generate_config("ASM_DD", new_rules)); + listener.on_update(get_config("ASM_DD", new_rules)); listener.commit(); { @@ -823,7 +825,7 @@ TEST(RemoteConfigEngineListener, EngineRuleUpdateFallback) std::map meta; std::map metrics; - auto e{engine::create()}; + std::shared_ptr e{engine::create()}; e->subscribe(waf::instance::from_string(rules, meta, metrics)); { @@ -840,7 +842,7 @@ TEST(RemoteConfigEngineListener, EngineRuleUpdateFallback) remote_config::engine_listener listener(e, create_sample_rules_ok()); listener.init(); - listener.on_unapply(generate_config("ASM_DD", "")); + listener.on_unapply(get_config("ASM_DD", "")); listener.commit(); { @@ -859,7 +861,7 @@ TEST(RemoteConfigEngineListener, EngineRuleOverrideUpdateDisableRule) std::map meta; std::map metrics; - auto engine{dds::engine::create()}; + std::shared_ptr engine{dds::engine::create()}; engine->subscribe(waf::instance::from_string(waf_rule, meta, metrics)); remote_config::engine_listener listener(engine); @@ -877,7 +879,7 @@ TEST(RemoteConfigEngineListener, EngineRuleOverrideUpdateDisableRule) const std::string rule_override = R"({"rules_override": [{"rules_target": [{"rule_id": "1"}], "enabled":"false"}]})"; - listener.on_update(generate_config("ASM", rule_override)); + listener.on_update(get_config("ASM", rule_override)); { auto ctx = engine->get_context(); @@ -906,7 +908,7 @@ TEST(RemoteConfigEngineListener, RuleOverrideUpdateSetOnMatch) std::map meta; std::map metrics; - auto engine{dds::engine::create()}; + std::shared_ptr engine{dds::engine::create()}; engine->subscribe(waf::instance::from_string(waf_rule, meta, metrics)); remote_config::engine_listener listener(engine); @@ -926,7 +928,7 @@ TEST(RemoteConfigEngineListener, RuleOverrideUpdateSetOnMatch) const std::string rule_override = R"({"rules_override": [{"rules_target": [{"tags": {"type": "flow1"}}], "on_match": ["block"]}]})"; - listener.on_update(generate_config("ASM", rule_override)); + listener.on_update(get_config("ASM", rule_override)); { auto ctx = engine->get_context(); @@ -957,7 +959,7 @@ TEST(RemoteConfigEngineListener, EngineRuleOverrideAndActionsUpdate) std::map meta; std::map metrics; - auto engine{dds::engine::create()}; + std::shared_ptr engine{dds::engine::create()}; engine->subscribe(waf::instance::from_string(waf_rule, meta, metrics)); remote_config::engine_listener listener(engine); @@ -979,7 +981,7 @@ TEST(RemoteConfigEngineListener, EngineRuleOverrideAndActionsUpdate) {"status_code": "303", "location": "localhost"}}],"rules_override": [{"rules_target": [{"rule_id": "1"}], "on_match": ["redirect"]}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); { auto ctx = engine->get_context(); @@ -1010,7 +1012,7 @@ TEST(RemoteConfigEngineListener, EngineExclusionsUpdatePasslistRule) std::map meta; std::map metrics; - auto engine{dds::engine::create()}; + std::shared_ptr engine{dds::engine::create()}; engine->subscribe(waf::instance::from_string(waf_rule, meta, metrics)); remote_config::engine_listener listener(engine); @@ -1029,7 +1031,7 @@ TEST(RemoteConfigEngineListener, EngineExclusionsUpdatePasslistRule) const std::string update = R"({"exclusions":[{"id":1,"rules_target":[{"rule_id":1}]}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); { auto ctx = engine->get_context(); @@ -1058,7 +1060,7 @@ TEST(RemoteConfigEngineListener, EngineCustomRulesUpdate) std::map meta; std::map metrics; - auto engine{dds::engine::create()}; + std::shared_ptr engine{dds::engine::create()}; engine->subscribe(waf::instance::from_string(waf_rule, meta, metrics)); remote_config::engine_listener listener(engine); @@ -1090,7 +1092,7 @@ TEST(RemoteConfigEngineListener, EngineCustomRulesUpdate) "category":"custom"},"conditions":[{"operator":"match_regex","parameters": {"inputs":[{"address":"arg3","key_path":[]}],"regex":"^custom.*"}}], "on_match":["block"]}]})"; - listener.on_update(generate_config("ASM", update)); + listener.on_update(get_config("ASM", update)); { auto ctx = engine->get_context(); @@ -1134,7 +1136,7 @@ TEST(RemoteConfigEngineListener, EngineCustomRulesUpdate) } listener.init(); - listener.on_update(generate_config("ASM", R"({"custom_rules":[]})")); + listener.on_update(get_config("ASM", R"({"custom_rules":[]})")); listener.commit(); { @@ -1168,7 +1170,7 @@ TEST(RemoteConfigEngineListener, EngineRuleDataUpdate) std::map meta; std::map metrics; - auto e{engine::create()}; + std::shared_ptr e{engine::create()}; e->subscribe(waf::instance::from_string(waf_rule_with_data, meta, metrics)); remote_config::engine_listener listener(e); @@ -1186,7 +1188,7 @@ TEST(RemoteConfigEngineListener, EngineRuleDataUpdate) const std::string update = R"({"rules_data":[{"id":"blocked_ips","type":"ip_with_expiration","data":[{"value":"1.2.3.4","expiration":0}]}]})"; - listener.on_update(generate_config("ASM_DATA", update)); + listener.on_update(get_config("ASM_DATA", update)); { auto ctx = e->get_context(); diff --git a/appsec/tests/helper/remote_config/mocks.cpp b/appsec/tests/helper/remote_config/mocks.cpp new file mode 100644 index 0000000000..6aa0988781 --- /dev/null +++ b/appsec/tests/helper/remote_config/mocks.cpp @@ -0,0 +1,41 @@ +#include "mocks.hpp" +#include +#include +#include +#include + +namespace dds::remote_config::mock { +remote_config::config get_config( + std::string_view product_name, const std::string &content) +{ + static std::atomic id{0}; + + const int cur_id = id.fetch_add(1, std::memory_order_relaxed); + + std::string shm_path{"/test-rc-file-" + std::to_string(cur_id)}; + ::shm_unlink(shm_path.c_str()); + int shm_fd = ::shm_open(shm_path.c_str(), O_CREAT | O_RDWR, 0600); + if (shm_fd == -1) { + std::abort(); + } + ::ftruncate(shm_fd, content.size()); + + if (content.size() > 0) { + void *mem = + ::mmap(nullptr, content.size(), PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (mem == MAP_FAILED) { + std::abort(); + } + std::copy_n(content.data(), content.size(), static_cast(mem)); + + if (::munmap(mem, content.size()) != 0) { + std::abort(); + } + } + + ::close(shm_fd); + + return {shm_path, std::string{"datadog/2/"} + std::string{product_name} + + "/foobar_" + std::to_string(cur_id) + "/config"}; +} +} // namespace dds::remote_config::mock diff --git a/appsec/tests/helper/remote_config/mocks.hpp b/appsec/tests/helper/remote_config/mocks.hpp index c142c89bf1..f34f1d7701 100644 --- a/appsec/tests/helper/remote_config/mocks.hpp +++ b/appsec/tests/helper/remote_config/mocks.hpp @@ -11,7 +11,6 @@ #include "engine.hpp" #include "remote_config/client.hpp" #include "remote_config/config.hpp" -#include "service_identifier.hpp" namespace dds::remote_config::mock { @@ -30,30 +29,7 @@ class engine : public dds::engine { static auto create() { return std::shared_ptr(new engine()); } }; -class client : public remote_config::client { -public: - client(service_identifier sid) - : remote_config::client(nullptr, std::move(sid), {}) - {} - ~client() override = default; - MOCK_METHOD0(poll, bool()); - MOCK_METHOD0(is_remote_config_available, bool()); - MOCK_METHOD(void, register_runtime_id, (const std::string &id), (override)); - MOCK_METHOD( - void, unregister_runtime_id, (const std::string &id), (override)); -}; - -inline remote_config::config generate_config( - const std::string &product, const std::string &content, bool encode = true) -{ - std::string encoded_content = content; - if (encode) { - encoded_content = base64_encode(content); - } - - return {product, "id", encoded_content, "path", {}, 123, 321, - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; -} +remote_config::config get_config( + std::string_view product_name, const std::string &content); } // namespace dds::remote_config::mock diff --git a/appsec/tests/helper/remote_config/parser_test.cpp b/appsec/tests/helper/remote_config/parser_test.cpp deleted file mode 100644 index 64765834e4..0000000000 --- a/appsec/tests/helper/remote_config/parser_test.cpp +++ /dev/null @@ -1,1227 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -#include -#include -#include - -#include "../common.hpp" -#include "remote_config/protocol/tuf/parser.hpp" - -namespace dds { - -std::string get_example_response() -{ - std::string response( - "{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAia2V5aWQi" - "OiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZGY3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0Mz" - "EyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdj" - "Y2JkODBkOWM4NDU4ZDdkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZj" - "c2MWI4N2I2YzBlYjliMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5" - "MjU4MDkwMGYiCiAgICAgICAgfQogICAgXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgIl" - "90eXBlIjogInRhcmdldHMiLAogICAgICAgICJjdXN0b20iOiB7CiAgICAgICAgICAgICJv" - "cGFxdWVfYmFja2VuZF9zdGF0ZSI6ICJleUoyWlhKemFXOXVJam94TENKemRHRjBaU0k2ZX" - "lKbWFXeGxYMmhoYzJobGN5STZXeUpTS3pKRFZtdGxkRVJ6WVc1cFdrZEphMFphWkZKTlQy" - "RllhM1Z6TURGMWVsUTFNM3BuZW1sU1RHRTBQU0lzSWtJd1dtTTNUMUlyVWxWTGNuZE9iMF" - "ZFV2pZM1VYVjVXRWxyYTJjeGIyTkhWV1IzZWtac1MwZERaRlU5SWl3aWVIRnFUbFV4VFV4" - "WFUzQlJiRFpOYWt4UFUyTnZTVUoyYjNsU2VsWnJkelp6TkdFcmRYVndPV2d3UVQwaVhYMT" - "kiCiAgICAgICAgfSwKICAgICAgICAiZXhwaXJlcyI6ICIyMDIyLTExLTA0VDEzOjMxOjU5" - "WiIsCiAgICAgICAgInNwZWNfdmVyc2lvbiI6ICIxLjAuMCIsCiAgICAgICAgInRhcmdldH" - "MiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFtaWNfcmF0" - "ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgICAgICAgIC" - "AgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAg" - "ICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogIjA3NDY1Y2" - "VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM5NzIyNDkyMGQ2ODcwNjUxZGMzMzE2NTI4" - "NjA5ZDUiLAogICAgICAgICAgICAgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaGFzaGhlcm" - "UwMSIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogNjYz" - "OTkKICAgICAgICAgICAgfSwKICAgICAgICAgICAgImRhdGFkb2cvMi9ERUJVRy9sdWtlLn" - "N0ZWVuc2VuL2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICJjdXN0b20iOiB7CiAgICAg" - "ICAgICAgICAgICAgICAgInYiOiAzCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgIC" - "AgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogImM2YThj" - "ZDUzNTMwYjU5MmE1MDk3YTMyMzJjZTQ5Y2EwODA2ZmEzMjQ3MzU2NGMzYWIzODZiZWJhZW" - "E3ZDg3NDAiLAogICAgICAgICAgICAgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaGFzaGhl" - "cmUwMiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogMT" - "MKICAgICAgICAgICAgfSwKICAgICAgICAgICAgImVtcGxveWVlL0RFQlVHX0RELzIudGVz" - "dDEuY29uZmlnL2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICJjdXN0b20iOiB7CiAgIC" - "AgICAgICAgICAgICAgICAgInYiOiAxCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAg" - "ICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogIjQ3ZW" - "Q4MjU2NDdhZDBlYzZhNzg5OTE4ODkwNTY1ZDQ0YzM5YTVlNGJhY2QzNWJiMzRmOWRmMzgz" - "Mzg5MTJkYWUiLAogICAgICAgICAgICAgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaGFzaG" - "hlcmUwMyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjog" - "NDEKICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgInZlcnNpb24iOiAyNzQ4Nz" - "E1NgogICAgfQp9\", \"target_files\": [{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - - return response; -} - -void assert_parser_error(std::function parser, - std::string payload, - remote_config::protocol::remote_config_parser_result result) -{ - remote_config::protocol::remote_config_parser_result error; - try { - parser(payload); - } catch (remote_config::protocol::parser_exception &e) { - error = e.get_error(); - } - - EXPECT_EQ(result, error); -} - -TEST(RemoteConfigParser, ItReturnsErrorWhenInvalidBodyIsGiven) -{ - assert_parser_error(remote_config::protocol::parse, "invalid_json", - remote_config::protocol::remote_config_parser_result::invalid_json); -} - -TEST(RemoteConfigParser, ParsingNonObjectPayloadThrowsException) -{ - assert_parser_error(remote_config::protocol::parse, "[]", - remote_config::protocol::remote_config_parser_result::invalid_response); -} - -TEST(RemoteConfigParser, TargetsFieldMustBeString) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"targets\": [], \"target_files\": [], \"client_configs\": [] }", - remote_config::protocol::remote_config_parser_result:: - targets_field_invalid_type); -} - -TEST(RemoteConfigParser, targetFilesFieldCanBeMissed) -{ - auto response = remote_config::protocol::parse( - "{\"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAia2V5" - "aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZGY3NGFiN2Q1MTgzZGZmZmJkNz" - "FiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAgICJzaWciOiAiNDliOTBm" - "NWY0YmZjMjdjY2JkODBkOWM4NDU4ZDdkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOG" - "IzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjliMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMy" - "NWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAgICAgICAgfQogICAgXSwKICAgICJzaW" - "duZWQiOiB7CiAgICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICJjdXN0" - "b20iOiB7CiAgICAgICAgICAgICJvcGFxdWVfYmFja2VuZF9zdGF0ZSI6ICJleUoyWl" - "hKemFXOXVJam94TENKemRHRjBaU0k2ZXlKbWFXeGxYMmhoYzJobGN5STZXeUpTS3pK" - "RFZtdGxkRVJ6WVc1cFdrZEphMFphWkZKTlQyRllhM1Z6TURGMWVsUTFNM3BuZW1sU1" - "RHRTBQU0lzSWtJd1dtTTNUMUlyVWxWTGNuZE9iMFZFV2pZM1VYVjVXRWxyYTJjeGIy" - "TkhWV1IzZWtac1MwZERaRlU5SWl3aWVIRnFUbFV4VFV4WFUzQlJiRFpOYWt4UFUyTn" - "ZTVUoyYjNsU2VsWnJkelp6TkdFcmRYVndPV2d3UVQwaVhYMTkiCiAgICAgICAgfSwK" - "ICAgICAgICAiZXhwaXJlcyI6ICIyMDIyLTExLTA0VDEzOjMxOjU5WiIsCiAgICAgIC" - "AgInNwZWNfdmVyc2lvbiI6ICIxLjAuMCIsCiAgICAgICAgInRhcmdldHMiOiB7CiAg" - "ICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFtaWNfcmF0ZXMvY2" - "9uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgICAgICAgICAg" - "ICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgIC" - "AgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogIjA3" - "NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM5NzIyNDkyMGQ2ODcwNjUxZG" - "MzMzE2NTI4NjA5ZDUiLAogICAgICAgICAgICAgICAgICAgICJzaGE1MTIiOiAic2hh" - "NTEyaGFzaGhlcmUwMSIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgIC" - "AibGVuZ3RoIjogNjYzOTkKICAgICAgICAgICAgfSwKICAgICAgICAgICAgImRhdGFk" - "b2cvMi9ERUJVRy9sdWtlLnN0ZWVuc2VuL2NvbmZpZyI6IHsKICAgICAgICAgICAgIC" - "AgICJjdXN0b20iOiB7CiAgICAgICAgICAgICAgICAgICAgInYiOiAzCiAgICAgICAg" - "ICAgICAgICB9LAogICAgICAgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgIC" - "AgICAgICAgICAic2hhMjU2IjogImM2YThjZDUzNTMwYjU5MmE1MDk3YTMyMzJjZTQ5" - "Y2EwODA2ZmEzMjQ3MzU2NGMzYWIzODZiZWJhZWE3ZDg3NDAiLAogICAgICAgICAgIC" - "AgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaGFzaGhlcmUwMiIKICAgICAgICAgICAg" - "ICAgIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogMTMKICAgICAgICAgICAgfS" - "wKICAgICAgICAgICAgImVtcGxveWVlL0RFQlVHX0RELzIudGVzdDEuY29uZmlnL2Nv" - "bmZpZyI6IHsKICAgICAgICAgICAgICAgICJjdXN0b20iOiB7CiAgICAgICAgICAgIC" - "AgICAgICAgInYiOiAxCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAg" - "Imhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogIjQ3ZWQ4Mj" - "U2NDdhZDBlYzZhNzg5OTE4ODkwNTY1ZDQ0YzM5YTVlNGJhY2QzNWJiMzRmOWRmMzgz" - "Mzg5MTJkYWUiLAogICAgICAgICAgICAgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaG" - "FzaGhlcmUwMyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibGVu" - "Z3RoIjogNDEKICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgInZlcnNpb2" - "4iOiAyNzQ4NzE1NgogICAgfQp9\", \"client_configs\": [] }"); - ASSERT_TRUE(response.target_files.empty()); -} - -TEST(RemoteConfigParser, targetFilesFieldMustBeArray) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"targets\": \"\", \"target_files\": \"\", \"client_configs\": [] }", - remote_config::protocol::remote_config_parser_result:: - target_files_field_invalid_type); -} - -TEST(RemoteConfigParser, clientConfigsFieldCanBeMissed) -{ - auto response = remote_config::protocol::parse( - "{\"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAia2V5" - "aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZGY3NGFiN2Q1MTgzZGZmZmJkNz" - "FiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAgICJzaWciOiAiNDliOTBm" - "NWY0YmZjMjdjY2JkODBkOWM4NDU4ZDdkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOG" - "IzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjliMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMy" - "NWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAgICAgICAgfQogICAgXSwKICAgICJzaW" - "duZWQiOiB7CiAgICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICJjdXN0" - "b20iOiB7CiAgICAgICAgICAgICJvcGFxdWVfYmFja2VuZF9zdGF0ZSI6ICJleUoyWl" - "hKemFXOXVJam94TENKemRHRjBaU0k2ZXlKbWFXeGxYMmhoYzJobGN5STZXeUpTS3pK" - "RFZtdGxkRVJ6WVc1cFdrZEphMFphWkZKTlQyRllhM1Z6TURGMWVsUTFNM3BuZW1sU1" - "RHRTBQU0lzSWtJd1dtTTNUMUlyVWxWTGNuZE9iMFZFV2pZM1VYVjVXRWxyYTJjeGIy" - "TkhWV1IzZWtac1MwZERaRlU5SWl3aWVIRnFUbFV4VFV4WFUzQlJiRFpOYWt4UFUyTn" - "ZTVUoyYjNsU2VsWnJkelp6TkdFcmRYVndPV2d3UVQwaVhYMTkiCiAgICAgICAgfSwK" - "ICAgICAgICAiZXhwaXJlcyI6ICIyMDIyLTExLTA0VDEzOjMxOjU5WiIsCiAgICAgIC" - "AgInNwZWNfdmVyc2lvbiI6ICIxLjAuMCIsCiAgICAgICAgInRhcmdldHMiOiB7CiAg" - "ICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFtaWNfcmF0ZXMvY2" - "9uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgICAgICAgICAg" - "ICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgIC" - "AgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogIjA3" - "NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM5NzIyNDkyMGQ2ODcwNjUxZG" - "MzMzE2NTI4NjA5ZDUiLAogICAgICAgICAgICAgICAgICAgICJzaGE1MTIiOiAic2hh" - "NTEyaGFzaGhlcmUwMSIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgIC" - "AibGVuZ3RoIjogNjYzOTkKICAgICAgICAgICAgfSwKICAgICAgICAgICAgImRhdGFk" - "b2cvMi9ERUJVRy9sdWtlLnN0ZWVuc2VuL2NvbmZpZyI6IHsKICAgICAgICAgICAgIC" - "AgICJjdXN0b20iOiB7CiAgICAgICAgICAgICAgICAgICAgInYiOiAzCiAgICAgICAg" - "ICAgICAgICB9LAogICAgICAgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgIC" - "AgICAgICAgICAic2hhMjU2IjogImM2YThjZDUzNTMwYjU5MmE1MDk3YTMyMzJjZTQ5" - "Y2EwODA2ZmEzMjQ3MzU2NGMzYWIzODZiZWJhZWE3ZDg3NDAiLAogICAgICAgICAgIC" - "AgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaGFzaGhlcmUwMiIKICAgICAgICAgICAg" - "ICAgIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogMTMKICAgICAgICAgICAgfS" - "wKICAgICAgICAgICAgImVtcGxveWVlL0RFQlVHX0RELzIudGVzdDEuY29uZmlnL2Nv" - "bmZpZyI6IHsKICAgICAgICAgICAgICAgICJjdXN0b20iOiB7CiAgICAgICAgICAgIC" - "AgICAgICAgInYiOiAxCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAg" - "Imhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogIjQ3ZWQ4Mj" - "U2NDdhZDBlYzZhNzg5OTE4ODkwNTY1ZDQ0YzM5YTVlNGJhY2QzNWJiMzRmOWRmMzgz" - "Mzg5MTJkYWUiLAogICAgICAgICAgICAgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaG" - "FzaGhlcmUwMyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibGVu" - "Z3RoIjogNDEKICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgInZlcnNpb2" - "4iOiAyNzQ4NzE1NgogICAgfQp9\", \"target_files\": [] }"); - - ASSERT_TRUE(response.client_configs.empty()); -} - -TEST(RemoteConfigParser, clientConfigsFieldMustBeArray) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"targets\": \"\", \"target_files\": [], \"client_configs\": \"\" }", - remote_config::protocol::remote_config_parser_result:: - client_config_field_invalid_type); -} - -TEST(RemoteConfigParser, TargetFilesAreParsed) -{ - std::string response = get_example_response(); - - auto gcr = remote_config::protocol::parse(response); - - EXPECT_EQ(2, gcr.target_files.size()); - - auto target_files = gcr.target_files; - - EXPECT_EQ("employee/DEBUG_DD/2.test1.config/config", - target_files.find("employee/DEBUG_DD/2.test1.config/config") - ->second.path); - EXPECT_EQ("UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=", - target_files.find("employee/DEBUG_DD/2.test1.config/config") - ->second.raw); - - EXPECT_EQ("datadog/2/DEBUG/luke.steensen/config", - target_files.find("datadog/2/DEBUG/luke.steensen/config")->second.path); - EXPECT_EQ("aGVsbG8gdmVjdG9yIQ==", - target_files.find("datadog/2/DEBUG/luke.steensen/config")->second.raw); -} - -TEST(RemoteConfigParser, TargetFilesWithoutPathAreInvalid) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"roots\": [], \"targets\": \"b2s=\", \"target_files\": [{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{ \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }", - remote_config::protocol::remote_config_parser_result:: - target_files_path_field_missing); -} - -TEST(RemoteConfigParser, TargetFilesWithNonStringPathAreInvalid) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"roots\": [], \"targets\": \"b2s=\", \"target_files\": [{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": [], \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }", - remote_config::protocol::remote_config_parser_result:: - target_files_path_field_invalid_type); -} - -TEST(RemoteConfigParser, TargetFilesWithoutRawAreInvalid) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"roots\": [], \"targets\": \"b2s=\", \"target_files\": [{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\"} ], " - "\"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }", - remote_config::protocol::remote_config_parser_result:: - target_files_raw_field_missing); -} - -TEST(RemoteConfigParser, TargetFilesWithNonNonStringRawAreInvalid) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"roots\": [], \"targets\": \"b2s=\", \"target_files\": [{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": []} ], " - "\"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }", - remote_config::protocol::remote_config_parser_result:: - target_files_raw_field_invalid_type); -} - -TEST(RemoteConfigParser, TargetFilesMustBeObjects) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"roots\": [], \"targets\": \"b2s=\", \"target_files\": [ " - "\"invalid\", " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }", - remote_config::protocol::remote_config_parser_result:: - target_files_object_invalid); -} - -TEST(RemoteConfigParser, ClientConfigsAreParsed) -{ - std::string response = get_example_response(); - - auto gcr = remote_config::protocol::parse(response); - - EXPECT_EQ(2, gcr.client_configs.size()); - - auto client_configs = gcr.client_configs; - - EXPECT_EQ("datadog/2/DEBUG/luke.steensen/config", client_configs[0]); - EXPECT_EQ("employee/DEBUG_DD/2.test1.config/config", client_configs[1]); -} - -TEST(RemoteConfigParser, ClientConfigsMustBeStrings) -{ - assert_parser_error(remote_config::protocol::parse, - "{\"roots\": [], \"targets\": \"b2s=\", " - "\"target_files\": [], \"client_configs\": " - "[[\"invalid\"], " - "\"employee/DEBUG_DD/2.test1.config/config\"] }", - remote_config::protocol::remote_config_parser_result:: - client_config_field_invalid_entry); -} - -TEST(RemoteConfigParser, TargetsMustBeNotEmpty) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": \"\", " - "\"target_files\": [{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - targets_field_empty); -} - -TEST(RemoteConfigParser, TargetsnMustBeValidBase64Encoded) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": \"nonValid%Base64Here\", " - "\"target_files\": [{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - targets_field_invalid_base64); -} - -TEST(RemoteConfigParser, TargetsDecodedMustBeValidJson) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": \"nonJsonHere\", \"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - targets_field_invalid_json); -} - -TEST(RemoteConfigParser, SignedFieldOnTargetsMustBeObject) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAia2V5aWQ" - "iOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1ZGY3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0" - "MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjM" - "jdjY2JkODBkOWM4NDU4ZDdkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1Yj" - "FkZjc2MWI4N2I2YzBlYjliMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZ" - "kYTg5MjU4MDkwMGYiCiAgICAgICAgfQogICAgXSwKICAgICJzaWduZWQiOiAiaW52YWxp" - "ZCIKfQ==\", \"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - signed_targets_field_invalid); -} - -TEST(RemoteConfigParser, SignedFieldOnTargetsMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbICAgICAgICAKICAgIF0KfQ==\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - signed_targets_field_missing); -} - -TEST(RemoteConfigParser, _TypeFieldOnSignedTargetsMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgImN" - "1c3RvbSI6IHsKICAgICAgICAgICAgIm9wYXF1ZV9iYWNrZW5kX3N0YXRlIjogInNvbWV0" - "aGluZyIKICAgICAgICB9LAogICAgICAgICJleHBpcmVzIjogIjIwMjItMTEtMDRUMTM6M" - "zE6NTlaIiwKICAgICAgICAic3BlY192ZXJzaW9uIjogIjEuMC4wIiwKICAgICAgICAidG" - "FyZ2V0cyI6IHsKICAgICAgICAgICAgImRhdGFkb2cvMi9BUE1fU0FNUExJTkcvZHluYW1" - "pY19yYXRlcy9jb25maWciOiB7CiAgICAgICAgICAgICAgICAiY3VzdG9tIjogewogICAg" - "ICAgICAgICAgICAgICAgICJ2IjogMQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgI" - "CAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICAgICAgInNoYTI1NiI6ICJibG" - "FoIiwKICAgICAgICAgICAgICAgICAgICAic2hhNTEyIjogInNoYTUxMmhhc2hoZXJlMDE" - "iCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgImxlbmd0aCI6IDIKICAg" - "ICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgInZlcnNpb24iOiAyNzQ4NzE1NgogI" - "CAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}], " - "\"client_configs\": " - "[ \"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - type_signed_targets_field_missing); -} - -TEST(RemoteConfigParser, _TypeFieldOnSignedTargetsMustBeString) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgIl9" - "0eXBlIjoge30sCiAgICAgICAgImN1c3RvbSI6IHsKICAgICAgICAgICAgIm9wYXF1ZV9i" - "YWNrZW5kX3N0YXRlIjogInNvbWV0aGluZyIKICAgICAgICB9LAogICAgICAgICJleHBpc" - "mVzIjogIjIwMjItMTEtMDRUMTM6MzE6NTlaIiwKICAgICAgICAic3BlY192ZXJzaW9uIj" - "ogIjEuMC4wIiwKICAgICAgICAidGFyZ2V0cyI6IHsKICAgICAgICAgICAgImRhdGFkb2c" - "vMi9BUE1fU0FNUExJTkcvZHluYW1pY19yYXRlcy9jb25maWciOiB7CiAgICAgICAgICAg" - "ICAgICAiY3VzdG9tIjogewogICAgICAgICAgICAgICAgICAgICJ2IjogMQogICAgICAgI" - "CAgICAgICAgfSwKICAgICAgICAgICAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgIC" - "AgICAgICAgInNoYTI1NiI6ICJibGFoIiwKICAgICAgICAgICAgICAgICAgICAic2hhNTE" - "yIjogInNoYTUxMmhhc2hoZXJlMDEiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAg" - "ICAgICAgImxlbmd0aCI6IDIKICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgI" - "nZlcnNpb24iOiAyNzQ4NzE1NgogICAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}], " - "\"client_configs\": " - "[ \"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - type_signed_targets_field_invalid); -} - -TEST(RemoteConfigParser, _TypeFieldOnSignedTargetsMustBeEqualToTargets) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgIl9" - "0eXBlIjogIm5vbl9hY2NlcHRlZF90eXBlIiwKICAgICAgICAiY3VzdG9tIjogewogICAg" - "ICAgICAgICAib3BhcXVlX2JhY2tlbmRfc3RhdGUiOiAic29tZXRoaW5nIgogICAgICAgI" - "H0sCiAgICAgICAgImV4cGlyZXMiOiAiMjAyMi0xMS0wNFQxMzozMTo1OVoiLAogICAgIC" - "AgICJzcGVjX3ZlcnNpb24iOiAiMS4wLjAiLAogICAgICAgICJ0YXJnZXRzIjogewogICA" - "gICAgICAgICAiZGF0YWRvZy8yL0FQTV9TQU1QTElORy9keW5hbWljX3JhdGVzL2NvbmZp" - "ZyI6IHsKICAgICAgICAgICAgICAgICJjdXN0b20iOiB7CiAgICAgICAgICAgICAgICAgI" - "CAgInYiOiAxCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgImhhc2hlcy" - "I6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogImJsYWgiLAogICAgICAgICA" - "gICAgICAgICAgICJzaGE1MTIiOiAic2hhNTEyaGFzaGhlcmUwMSIKICAgICAgICAgICAg" - "ICAgIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogMgogICAgICAgICAgICB9CiAgI" - "CAgICAgfSwKICAgICAgICAidmVyc2lvbiI6IDI3NDg3MTU2CiAgICB9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}], " - "\"client_configs\": " - "[ \"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - type_signed_targets_field_invalid_type); -} - -TEST(RemoteConfigParser, VersionFieldOnSignedTargetsMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICBdLAogICAgInNpZ25lZCI6IHsKICAgICA" - "gICAiY3VzdG9tIjogewogICAgICAgICAgICAib3BhcXVlX2JhY2tlbmRfc3RhdGUiOiAi" - "ZXlKMlpYSnphVzl1SWpveExDSnpkR0YwWlNJNmV5Sm1hV3hsWDJoaGMyaGxjeUk2V3lKU" - "0t6SkRWbXRsZEVSellXNXBXa2RKYTBaYVpGSk5UMkZZYTNWek1ERjFlbFExTTNwbmVtbF" - "NUR0UwUFNJc0lrSXdXbU0zVDFJclVsVkxjbmRPYjBWRVdqWTNVWFY1V0VscmEyY3hiMk5" - "IVldSM2VrWnNTMGREWkZVOUlpd2llSEZxVGxVeFRVeFhVM0JSYkRaTmFreFBVMk52U1VK" - "MmIzbFNlbFpyZHpaek5HRXJkWFZ3T1dnd1FUMGlYWDE5IgogICAgICAgIH0sCiAgICAgI" - "CAgInRhcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2" - "R5bmFtaWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHs" - "KICAgICAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAog" - "ICAgICAgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhM" - "jU2IjogIjA3NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM5NzIyNDkyMGQ2OD" - "cwNjUxZGMzMzE2NTI4NjA5ZDUiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICA" - "gICAgImxlbmd0aCI6IDY2Mzk5CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJkYXRh" - "ZG9nLzIvREVCVUcvbHVrZS5zdGVlbnNlbi9jb25maWciOiB7CiAgICAgICAgICAgICAgI" - "CAiY3VzdG9tIjogewogICAgICAgICAgICAgICAgICAgICJ2IjogMwogICAgICAgICAgIC" - "AgICAgfSwKICAgICAgICAgICAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICA" - "gICAgInNoYTI1NiI6ICJjNmE4Y2Q1MzUzMGI1OTJhNTA5N2EzMjMyY2U0OWNhMDgwNmZh" - "MzI0NzM1NjRjM2FiMzg2YmViYWVhN2Q4NzQwIgogICAgICAgICAgICAgICAgfSwKICAgI" - "CAgICAgICAgICAgICJsZW5ndGgiOiAxMwogICAgICAgICAgICB9LAogICAgICAgICAgIC" - "AiZW1wbG95ZWUvREVCVUdfREQvMi50ZXN0MS5jb25maWcvY29uZmlnIjogewogICAgICA" - "gICAgICAgICAgImN1c3RvbSI6IHsKICAgICAgICAgICAgICAgICAgICAidiI6IDEKICAg" - "ICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgI" - "CAgICAgICAgICAgICJzaGEyNTYiOiAiNDdlZDgyNTY0N2FkMGVjNmE3ODk5MTg4OTA1Nj" - "VkNDRjMzlhNWU0YmFjZDM1YmIzNGY5ZGYzODMzODkxMmRhZSIKICAgICAgICAgICAgICA" - "gIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogNDEKICAgICAgICAgICAgfQogICAg" - "ICAgIH0KICAgIH0KfQ==\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - version_signed_targets_field_missing); -} - -TEST(RemoteConfigParser, VersionFieldOnSignedTargetsMustBeNumber) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICBdLAogICAgInNpZ25lZCI6IHsKICAgICA" - "gICAiY3VzdG9tIjogewogICAgICAgICAgICAib3BhcXVlX2JhY2tlbmRfc3RhdGUiOiAi" - "ZXlKMlpYSnphVzl1SWpveExDSnpkR0YwWlNJNmV5Sm1hV3hsWDJoaGMyaGxjeUk2V3lKU" - "0t6SkRWbXRsZEVSellXNXBXa2RKYTBaYVpGSk5UMkZZYTNWek1ERjFlbFExTTNwbmVtbF" - "NUR0UwUFNJc0lrSXdXbU0zVDFJclVsVkxjbmRPYjBWRVdqWTNVWFY1V0VscmEyY3hiMk5" - "IVldSM2VrWnNTMGREWkZVOUlpd2llSEZxVGxVeFRVeFhVM0JSYkRaTmFreFBVMk52U1VK" - "MmIzbFNlbFpyZHpaek5HRXJkWFZ3T1dnd1FUMGlYWDE5IgogICAgICAgIH0sCiAgICAgI" - "CAgInRhcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2" - "R5bmFtaWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHs" - "KICAgICAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAog" - "ICAgICAgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhM" - "jU2IjogIjA3NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM5NzIyNDkyMGQ2OD" - "cwNjUxZGMzMzE2NTI4NjA5ZDUiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICA" - "gICAgImxlbmd0aCI6IDY2Mzk5CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJkYXRh" - "ZG9nLzIvREVCVUcvbHVrZS5zdGVlbnNlbi9jb25maWciOiB7CiAgICAgICAgICAgICAgI" - "CAiY3VzdG9tIjogewogICAgICAgICAgICAgICAgICAgICJ2IjogMwogICAgICAgICAgIC" - "AgICAgfSwKICAgICAgICAgICAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICA" - "gICAgInNoYTI1NiI6ICJjNmE4Y2Q1MzUzMGI1OTJhNTA5N2EzMjMyY2U0OWNhMDgwNmZh" - "MzI0NzM1NjRjM2FiMzg2YmViYWVhN2Q4NzQwIgogICAgICAgICAgICAgICAgfSwKICAgI" - "CAgICAgICAgICAgICJsZW5ndGgiOiAxMwogICAgICAgICAgICB9LAogICAgICAgICAgIC" - "AiZW1wbG95ZWUvREVCVUdfREQvMi50ZXN0MS5jb25maWcvY29uZmlnIjogewogICAgICA" - "gICAgICAgICAgImN1c3RvbSI6IHsKICAgICAgICAgICAgICAgICAgICAidiI6IDEKICAg" - "ICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgI" - "CAgICAgICAgICAgICJzaGEyNTYiOiAiNDdlZDgyNTY0N2FkMGVjNmE3ODk5MTg4OTA1Nj" - "VkNDRjMzlhNWU0YmFjZDM1YmIzNGY5ZGYzODMzODkxMmRhZSIKICAgICAgICAgICAgICA" - "gIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogNDEKICAgICAgICAgICAgfQogICAg" - "ICAgIH0sCiAgICAgICAgInZlcnNpb24iOiB7fQogICAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - version_signed_targets_field_invalid); -} - -TEST(RemoteConfigParser, CustomFieldOnSignedTargetsMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICA" - "gICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1" - "ZGY3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgI" - "CAgICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZD" - "dkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjl" - "iMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAg" - "ICAgICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgI" - "CAgICAgICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImV4cG" - "lyZXMiOiAiMjAyMi0xMS0wNFQxMzozMTo1OVoiLAogICAgICAgICAgICAgICAgInNwZWN" - "fdmVyc2lvbiI6ICIxLjAuMCIsCiAgICAgICAgICAgICAgICAidGFyZ2V0cyI6IHsKICAg" - "ICAgICAgICAgICAgICAgICAgICAgImRhdGFkb2cvMi9GRUFUVVJFUy9keW5hbWljX3Jhd" - "GVzL2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG" - "9tIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiA" - "zNjc0MAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAg" - "ICAgICAgICAgICAgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICJzaGEyNTYiOiAiMDc0NjVjZWNlNDdlNDU0MmFiYz" - "BkYTA0MGQ5ZWJiNDJlYzk3MjI0OTIwZDY4NzA2NTFkYzMzMTY1Mjg2MDlkNSIKICAgICA" - "gICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICJsZW5ndGgiOiA2NjM5OQogICAgICAgICAgICAgICAgICAgICAgICB9L" - "AogICAgICAgICAgICAgICAgICAgICAgICAiZGF0YWRvZy8yL0ZFQVRVUkVTL2x1a2Uuc3" - "RlZW5zZW4vY29uZmlnIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ" - "jdXN0b20iOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAi" - "diI6IDMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgICAgICAgICAic2hhMjU2IjogImM2YThjZDUzNTMwYjU5MmE1MDk" - "3YTMyMzJjZTQ5Y2EwODA2ZmEzMjQ3MzU2NGMzYWIzODZiZWJhZWE3ZDg3NDAiCiAgICAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAibGVuZ3RoIjogMTMKICAgICAgICAgICAgICAgICAgICAgICAgfSwKIC" - "AgICAgICAgICAgICAgICAgICAgICAgImVtcGxveWVlL0ZFQVRVUkVTLzIudGVzdDEuY29" - "uZmlnL2NvbmZpZyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3Vz" - "dG9tIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiO" - "iAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICAgICAgICA" - "gICAgICAgICAgICAgICAgICAgInNoYTI1NiI6ICI0N2VkODI1NjQ3YWQwZWM2YTc4OTkx" - "ODg5MDU2NWQ0NGMzOWE1ZTRiYWNkMzViYjM0ZjlkZjM4MzM4OTEyZGFlIgogICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgImxlbmd0aCI6IDQxCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICA" - "gICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAidmVyc2lvbiI6IDI3NDg3MTU2CiAg" - "ICAgICAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - custom_signed_targets_field_missing); -} - -TEST(RemoteConfigParser, CustomFieldOnSignedTargetsMustBeObject) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICA" - "gICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1" - "ZGY3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgI" - "CAgICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZD" - "dkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjl" - "iMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAg" - "ICAgICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgI" - "CAgICAgICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImN1c3" - "RvbSI6ICJpbnZhbGlkIiwKICAgICAgICAgICAgICAgICJleHBpcmVzIjogIjIwMjItMTE" - "tMDRUMTM6MzE6NTlaIiwKICAgICAgICAgICAgICAgICJzcGVjX3ZlcnNpb24iOiAiMS4w" - "LjAiLAogICAgICAgICAgICAgICAgInRhcmdldHMiOiB7CiAgICAgICAgICAgICAgICAgI" - "CAgICAgICJkYXRhZG9nLzIvRkVBVFVSRVMvZHluYW1pY19yYXRlcy9jb25maWciOiB7Ci" - "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgICAgICA" - "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2IjogMzY3NDAKICAgICAgICAg" - "ICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAic2hhMjU2IjogIjA3NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM" - "5NzIyNDkyMGQ2ODcwNjUxZGMzMzE2NTI4NjA5ZDUiCiAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGVuZ" - "3RoIjogNjYzOTkKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgIC" - "AgICAgICAgICAgImRhdGFkb2cvMi9GRUFUVVJFUy9sdWtlLnN0ZWVuc2VuL2NvbmZpZyI" - "6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIjogewogICAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiAzCiAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - "gICAgICAgInNoYTI1NiI6ICJjNmE4Y2Q1MzUzMGI1OTJhNTA5N2EzMjMyY2U0OWNhMDgw" - "NmZhMzI0NzM1NjRjM2FiMzg2YmViYWVhN2Q4NzQwIgogICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxlbm" - "d0aCI6IDEzCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICA" - "gICAgICAgICJlbXBsb3llZS9GRUFUVVJFUy8yLnRlc3QxLmNvbmZpZy9jb25maWciOiB7" - "CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2IjogMQogICAgICAgICAgIC" - "AgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - "gICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICJzaGEyNTYiOiAiNDdlZDgyNTY0N2FkMGVjNmE3ODk5MTg4OTA1NjVkNDRjMzlhN" - "WU0YmFjZDM1YmIzNGY5ZGYzODMzODkxMmRhZSIKICAgICAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsZW5ndGg" - "iOiA0MQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9LAog" - "ICAgICAgICAgICAgICAgInZlcnNpb24iOiAyNzQ4NzE1NgogICAgICAgIH0KfQ==\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - custom_signed_targets_field_invalid); -} - -TEST(RemoteConfigParser, - OpaqueBackendStateCustomFieldOnSignedTargetsMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICA" - "gICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1" - "ZGY3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgI" - "CAgICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZD" - "dkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjl" - "iMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAg" - "ICAgICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgI" - "CAgICAgICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImN1c3" - "RvbSI6IHt9LAogICAgICAgICAgICAgICAgImV4cGlyZXMiOiAiMjAyMi0xMS0wNFQxMzo" - "zMTo1OVoiLAogICAgICAgICAgICAgICAgInNwZWNfdmVyc2lvbiI6ICIxLjAuMCIsCiAg" - "ICAgICAgICAgICAgICAidGFyZ2V0cyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgI" - "mRhdGFkb2cvMi9GRUFUVVJFUy9keW5hbWljX3JhdGVzL2NvbmZpZyI6IHsKICAgICAgIC" - "AgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIjogewogICAgICAgICAgICAgICA" - "gICAgICAgICAgICAgICAgICAgICAgICAgInYiOiAzNjc0MAogICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "mhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - "JzaGEyNTYiOiAiMDc0NjVjZWNlNDdlNDU0MmFiYzBkYTA0MGQ5ZWJiNDJlYzk3MjI0OTI" - "wZDY4NzA2NTFkYzMzMTY1Mjg2MDlkNSIKICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsZW5ndGgiOiA2N" - "jM5OQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgIC" - "AgICAiZGF0YWRvZy8yL0ZFQVRVUkVTL2x1a2Uuc3RlZW5zZW4vY29uZmlnIjogewogICA" - "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdXN0b20iOiB7CiAgICAgICAgICAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidiI6IDMKICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - "JoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - "ic2hhMjU2IjogImM2YThjZDUzNTMwYjU5MmE1MDk3YTMyMzJjZTQ5Y2EwODA2ZmEzMjQ3" - "MzU2NGMzYWIzODZiZWJhZWE3ZDg3NDAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGVuZ3RoIjogMT" - "MKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICA" - "gImVtcGxveWVlL0ZFQVRVUkVTLzIudGVzdDEuY29uZmlnL2NvbmZpZyI6IHsKICAgICAg" - "ICAgICAgICAgICAgICAgICAgICAgICAgICAiY3VzdG9tIjogewogICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgInYiOiAxCiAgICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGF" - "zaGVzIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNo" - "YTI1NiI6ICI0N2VkODI1NjQ3YWQwZWM2YTc4OTkxODg5MDU2NWQ0NGMzOWE1ZTRiYWNkM" - "zViYjM0ZjlkZjM4MzM4OTEyZGFlIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - "AgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxlbmd0aCI6IDQxCiA" - "gICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0sCiAgICAgICAg" - "ICAgICAgICAidmVyc2lvbiI6IDI3NDg3MTU2CiAgICAgICAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - obs_custom_signed_targets_field_missing); -} - -TEST(RemoteConfigParser, - OpaqueBackendStateCustomFieldOnSignedTargetsMustBeString) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgICAgICJzaWduYXR1cmVzIjogWwogICAgICAgICAgICAgICAgewogICAgICA" - "gICAgICAgICAgICAgICAgICAia2V5aWQiOiAiNWM0ZWNlNDEyNDFhMWJiNTEzZjZlM2U1" - "ZGY3NGFiN2Q1MTgzZGZmZmJkNzFiZmQ0MzEyNzkyMGQ4ODA1NjlmZCIsCiAgICAgICAgI" - "CAgICAgICAgICAgICAgICJzaWciOiAiNDliOTBmNWY0YmZjMjdjY2JkODBkOWM4NDU4ZD" - "dkMjJiYTlmYTA4OTBmZDc3NWRkMTE2YzUyOGIzNmRkNjA1YjFkZjc2MWI4N2I2YzBlYjl" - "iMDI2NDA1YTEzZWZlZjQ4Mjc5MzRkNmMyNWE3ZDZiODkyNWZkYTg5MjU4MDkwMGYiCiAg" - "ICAgICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAic2lnbmVkIjogewogICAgI" - "CAgICAgICAgICAgIl90eXBlIjogInRhcmdldHMiLAogICAgICAgICAgICAgICAgImN1c3" - "RvbSI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm9wYXF1ZV9iYWNrZW5kX3N0YXR" - "lIjoge30KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZXhwaXJlcyI6" - "ICIyMDIyLTExLTA0VDEzOjMxOjU5WiIsCiAgICAgICAgICAgICAgICAic3BlY192ZXJza" - "W9uIjogIjEuMC4wIiwKICAgICAgICAgICAgICAgICJ0YXJnZXRzIjogewogICAgICAgIC" - "AgICAgICAgICAgICAgICAiZGF0YWRvZy8yL0ZFQVRVUkVTL2R5bmFtaWNfcmF0ZXMvY29" - "uZmlnIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdXN0b20iOiB7" - "CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidiI6IDM2NzQwC" - "iAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIC" - "AgICAgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICAgICAgICAgICA" - "gICAgICAgICAgICAgICAgInNoYTI1NiI6ICIwNzQ2NWNlY2U0N2U0NTQyYWJjMGRhMDQw" - "ZDllYmI0MmVjOTcyMjQ5MjBkNjg3MDY1MWRjMzMxNjUyODYwOWQ1IgogICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - "AgICAgImxlbmd0aCI6IDY2Mzk5CiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICA" - "gICAgICAgICAgICAgICAgICAgICJkYXRhZG9nLzIvRkVBVFVSRVMvbHVrZS5zdGVlbnNl" - "bi9jb25maWciOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN1c3Rvb" - "SI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2IjogMw" - "ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICA" - "gICAgICAgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgICAgICJzaGEyNTYiOiAiYzZhOGNkNTM1MzBiNTkyYTUwOTdhMzIzM" - "mNlNDljYTA4MDZmYTMyNDczNTY0YzNhYjM4NmJlYmFlYTdkODc0MCIKICAgICAgICAgIC" - "AgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICA" - "gICAgICJsZW5ndGgiOiAxMwogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAg" - "ICAgICAgICAgICAgICAgICAiZW1wbG95ZWUvRkVBVFVSRVMvMi50ZXN0MS5jb25maWcvY" - "29uZmlnIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdXN0b20iOi" - "B7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidiI6IDEKICA" - "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAg" - "ICAgICAgICAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAic2hhMjU2IjogIjQ3ZWQ4MjU2NDdhZDBlYzZhNzg5OTE4ODkwNT" - "Y1ZDQ0YzM5YTVlNGJhY2QzNWJiMzRmOWRmMzgzMzg5MTJkYWUiCiAgICAgICAgICAgICA" - "gICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - "ICAibGVuZ3RoIjogNDEKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgI" - "CAgICAgfSwKICAgICAgICAgICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgICAgIC" - "B9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - obs_custom_signed_targets_field_invalid); -} - -TEST(RemoteConfigParser, TargetsFieldOnSignedTargetsMustBeObject) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICBdLAogICAgInNpZ25lZCI6IHsKICAgICA" - "gICAiY3VzdG9tIjogewogICAgICAgICAgICAib3BhcXVlX2JhY2tlbmRfc3RhdGUiOiBb" - "XQogICAgICAgIH0sCiAgICAgICAgInRhcmdldHMiOiBbXSwKICAgICAgICAidmVyc2lvb" - "iI6IDI3NDg3MTU2CiAgICB9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - targets_signed_targets_field_invalid); -} - -TEST(RemoteConfigParser, TargetsFieldOnSignedTargetsMustExists) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICBdLAogICAgInNpZ25lZCI6IHsKICAgICA" - "gICAiY3VzdG9tIjogewogICAgICAgICAgICAib3BhcXVlX2JhY2tlbmRfc3RhdGUiOiBb" - "XQogICAgICAgIH0sICAgICAgICAKICAgICAgICAidmVyc2lvbiI6IDI3NDg3MTU2CiAgI" - "CB9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - targets_signed_targets_field_missing); -} - -TEST(RemoteConfigParser, CustomOnPathMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImhhc2hlcyI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAic2hhMjU2IjogIjA3NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwND" - "BkOWViYjQyZWM5NzIyNDkyMGQ2ODcwNjUxZGMzMzE2NTI4NjA5ZDUiCiAgICAgICAgICA" - "gICAgICB9LAogICAgICAgICAgICAgICAgImxlbmd0aCI6IDY2Mzk5CiAgICAgICAgICAg" - "IH0KICAgICAgICB9LAogICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgIH0KfQ==" - "\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - custom_path_targets_field_missing); -} - -TEST(RemoteConfigParser, CustomOnPathMustBeObject) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbCiAgICBdLAogICAgInNpZ25lZCI6IHsKICAgICA" - "gICAiY3VzdG9tIjogewogICAgICAgICAgICAib3BhcXVlX2JhY2tlbmRfc3RhdGUiOiBb" - "XQogICAgICAgIH0sCiAgICAgICAgInRhcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZ" - "G9nLzIvQVBNX1NBTVBMSU5HL2R5bmFtaWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgIC" - "AgICAgICAgImN1c3RvbSI6ICJpbnZhbGlkIiwKICAgICAgICAgICAgICAgICJoYXNoZXM" - "iOiB7CiAgICAgICAgICAgICAgICAgICAgInNoYTI1NiI6ICIwNzQ2NWNlY2U0N2U0NTQy" - "YWJjMGRhMDQwZDllYmI0MmVjOTcyMjQ5MjBkNjg3MDY1MWRjMzMxNjUyODYwOWQ1IgogI" - "CAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJsZW5ndGgiOiA2NjM5OQogIC" - "AgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICAidmVyc2lvbiI6IDI3NDg3MTU2CiA" - "gICB9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - custom_path_targets_field_invalid); -} - -TEST(RemoteConfigParser, VCustomOnPathMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgIC" - "AgICAgICAgICAgICJzaGEyNTYiOiAiMDc0NjVjZWNlNDdlNDU0MmFiYzBkYTA0MGQ5ZWJ" - "iNDJlYzk3MjI0OTIwZDY4NzA2NTFkYzMzMTY1Mjg2MDlkNSIKICAgICAgICAgICAgICAg" - "IH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogNjYzOTkKICAgICAgICAgICAgfQogI" - "CAgICAgIH0sCiAgICAgICAgInZlcnNpb24iOiAyNzQ4NzE1NgogICAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - v_path_targets_field_missing); -} - -TEST(RemoteConfigParser, VCustomOnPathMustBeNumber) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAidiI6ICJpbnZhbGlkIgogICAgICAgICAgICAgICAgfSwKIC" - "AgICAgICAgICAgICAgICJoYXNoZXMiOiB7CiAgICAgICAgICAgICAgICAgICAgInNoYTI" - "1NiI6ICIwNzQ2NWNlY2U0N2U0NTQyYWJjMGRhMDQwZDllYmI0MmVjOTcyMjQ5MjBkNjg3" - "MDY1MWRjMzMxNjUyODYwOWQ1IgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgI" - "CAgICJsZW5ndGgiOiA2NjM5OQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgIC" - "AidmVyc2lvbiI6IDI3NDg3MTU2CiAgICB9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - v_path_targets_field_invalid); -} - -TEST(RemoteConfigParser, HashesOnPathMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgIC" - "AgICAgICAgICAgImxlbmd0aCI6IDY2Mzk5CiAgICAgICAgICAgIH0KICAgICAgICB9LAo" - "gICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgIH0KfQ==\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - hashes_path_targets_field_missing); -} - -TEST(RemoteConfigParser, HashesOnPathMustBeObject) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgIC" - "AgICAgICAgICAgImhhc2hlcyI6ICJpbnZhbGlkIiwKICAgICAgICAgICAgICAgICJsZW5" - "ndGgiOiA2NjM5OQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICAidmVyc2lv" - "biI6IDI3NDg3MTU2CiAgICB9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - hashes_path_targets_field_invalid); -} - -TEST(RemoteConfigParser, AtLeastOneHashMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgIC" - "AgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICA" - "gICAgICAibGVuZ3RoIjogNjYzOTkKICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAg" - "ICAgInZlcnNpb24iOiAyNzQ4NzE1NgogICAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - hashes_path_targets_field_empty); -} - -TEST(RemoteConfigParser, HashesOnPathMustBeString) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgIC" - "AgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2Ijo" - "ge30KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibGVuZ3RoIjogNjYz" - "OTkKICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgInZlcnNpb24iOiAyNzQ4N" - "zE1NgogICAgfQp9\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - hash_hashes_path_targets_field_invalid); -} - -TEST(RemoteConfigParser, LengthOnPathMustBePresent) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgIC" - "AgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2Ijo" - "gIjA3NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM5NzIyNDkyMGQ2ODcwNjUx" - "ZGMzMzE2NTI4NjA5ZDUiCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgI" - "CAgICB9LAogICAgICAgICJ2ZXJzaW9uIjogMjc0ODcxNTYKICAgIH0KfQ==\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - length_path_targets_field_missing); -} - -TEST(RemoteConfigParser, LengthOnPathMustBeString) -{ - std::string invalid_response = - ("{\"roots\": [], \"targets\": " - "\"ewogICAgInNpZ25hdHVyZXMiOiBbXSwKICAgICJzaWduZWQiOiB7CiAgICAgICAgInR" - "hcmdldHMiOiB7CiAgICAgICAgICAgICJkYXRhZG9nLzIvQVBNX1NBTVBMSU5HL2R5bmFt" - "aWNfcmF0ZXMvY29uZmlnIjogewogICAgICAgICAgICAgICAgImN1c3RvbSI6IHsKICAgI" - "CAgICAgICAgICAgICAgICAidiI6IDM2NzQwCiAgICAgICAgICAgICAgICB9LAogICAgIC" - "AgICAgICAgICAgImhhc2hlcyI6IHsKICAgICAgICAgICAgICAgICAgICAic2hhMjU2Ijo" - "gIjA3NDY1Y2VjZTQ3ZTQ1NDJhYmMwZGEwNDBkOWViYjQyZWM5NzIyNDkyMGQ2ODcwNjUx" - "ZGMzMzE2NTI4NjA5ZDUiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgI" - "mxlbmd0aCI6ICJpbnZhbGlkIgogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgIC" - "AidmVyc2lvbiI6IDI3NDg3MTU2CiAgICB9Cn0=\", " - "\"target_files\": " - "[{\"path\": " - "\"employee/DEBUG_DD/2.test1.config/config\", \"raw\": " - "\"UmVtb3RlIGNvbmZpZ3VyYXRpb24gaXMgc3VwZXIgc3VwZXIgY29vbAo=\"}, " - "{\"path\": \"datadog/2/DEBUG/luke.steensen/config\", \"raw\": " - "\"aGVsbG8gdmVjdG9yIQ==\"} ], \"client_configs\": " - "[\"datadog/2/DEBUG/luke.steensen/config\", " - "\"employee/DEBUG_DD/2.test1.config/config\"] }"); - assert_parser_error(remote_config::protocol::parse, invalid_response, - remote_config::protocol::remote_config_parser_result:: - length_path_targets_field_invalid); -} - -TEST(RemoteConfigParser, TargetsAreParsed) -{ - std::string response = get_example_response(); - - auto gcr = remote_config::protocol::parse(response); - - std::optional _targets = gcr.targets; - - EXPECT_EQ(27487156, _targets->version); - - std::unordered_map paths = - _targets->paths; - - EXPECT_EQ(3, paths.size()); - - auto path_itr = paths.find("datadog/2/APM_SAMPLING/dynamic_rates/config"); - auto temp_path = path_itr->second; - EXPECT_EQ(36740, temp_path.custom_v); - EXPECT_EQ(2, temp_path.hashes.size()); - EXPECT_EQ( - "07465cece47e4542abc0da040d9ebb42ec97224920d6870651dc3316528609d5", - temp_path.hashes["sha256"]); - EXPECT_EQ("sha512hashhere01", temp_path.hashes["sha512"]); - EXPECT_EQ(66399, temp_path.length); - - path_itr = paths.find("datadog/2/DEBUG/luke.steensen/config"); - temp_path = path_itr->second; - EXPECT_EQ(3, temp_path.custom_v); - EXPECT_EQ(2, temp_path.hashes.size()); - EXPECT_EQ( - "c6a8cd53530b592a5097a3232ce49ca0806fa32473564c3ab386bebaea7d8740", - temp_path.hashes["sha256"]); - EXPECT_EQ("sha512hashhere02", temp_path.hashes["sha512"]); - EXPECT_EQ(13, temp_path.length); - - path_itr = paths.find("employee/DEBUG_DD/2.test1.config/config"); - temp_path = path_itr->second; - EXPECT_EQ(1, temp_path.custom_v); - EXPECT_EQ(2, temp_path.hashes.size()); - EXPECT_EQ( - "47ed825647ad0ec6a789918890565d44c39a5e4bacd35bb34f9df38338912dae", - temp_path.hashes["sha256"]); - EXPECT_EQ("sha512hashhere03", temp_path.hashes["sha512"]); - EXPECT_EQ(41, temp_path.length); -} - -TEST(RemoteConfigParser, RemoteConfigParserResultCanBeCastToString) -{ - EXPECT_EQ("success", - remote_config::protocol::remote_config_parser_result_to_str( - remote_config::protocol::remote_config_parser_result::success)); - EXPECT_EQ("target_files_path_field_invalid_type", - remote_config::protocol::remote_config_parser_result_to_str( - remote_config::protocol::remote_config_parser_result:: - target_files_path_field_invalid_type)); - EXPECT_EQ("length_path_targets_field_missing", - remote_config::protocol::remote_config_parser_result_to_str( - remote_config::protocol::remote_config_parser_result:: - length_path_targets_field_missing)); - EXPECT_EQ("", remote_config::protocol::remote_config_parser_result_to_str( - remote_config::protocol::remote_config_parser_result:: - num_of_values)); -} - -TEST(RemoteConfigParser, ParseEmptyResponses) -{ - auto gcr = remote_config::protocol::parse("{}"); - - EXPECT_FALSE(gcr.targets.has_value()); -} - -TEST(RemoteConfigParser, ParseInfoResponse) -{ - const std::string response = - R"({"endpoints": ["/some/endpoint", "/another/endpoint"] })"; - auto info_response = remote_config::protocol::parse_info(response); - - EXPECT_EQ(2, info_response.endpoints.size()); - EXPECT_TRUE(std::find(info_response.endpoints.begin(), - info_response.endpoints.end(), - "/some/endpoint") != info_response.endpoints.end()); - EXPECT_TRUE(std::find(info_response.endpoints.begin(), - info_response.endpoints.end(), - "/another/endpoint") != info_response.endpoints.end()); -} - -TEST(RemoteConfigParser, ParseInfoInvalidJsonThrowsException) -{ - assert_parser_error(remote_config::protocol::parse_info, "invalid_json", - remote_config::protocol::remote_config_parser_result::invalid_json); -} - -TEST(RemoteConfigParser, ParseInfoNonObjectPayloadThrowsException) -{ - assert_parser_error(remote_config::protocol::parse_info, "[]", - remote_config::protocol::remote_config_parser_result::invalid_response); -} - -TEST(RemoteConfigParser, ParseInfoWithoutEndpointsThrowsException) -{ - assert_parser_error(remote_config::protocol::parse_info, - R"({"some":"value"})", - remote_config::protocol::remote_config_parser_result:: - endpoints_field_missing); -} - -TEST(RemoteConfigParser, ParseInfoWithEndpointsNotArrayThrowsException) -{ - assert_parser_error(remote_config::protocol::parse_info, - R"({"endpoints":"invalid_type"})", - remote_config::protocol::remote_config_parser_result:: - endpoints_field_invalid); -} - -TEST(RemoteConfigParser, ParseInfoWithNonStringEndpointThrowsException) -{ - assert_parser_error(remote_config::protocol::parse_info, - R"({"endpoints": [ 1234 ]})", - remote_config::protocol::remote_config_parser_result::invalid_endpoint); -} - -} // namespace dds diff --git a/appsec/tests/helper/remote_config/product_test.cpp b/appsec/tests/helper/remote_config/product_test.cpp deleted file mode 100644 index 997cc403cc..0000000000 --- a/appsec/tests/helper/remote_config/product_test.cpp +++ /dev/null @@ -1,262 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -#include "../common.hpp" -#include "remote_config/config.hpp" -#include "remote_config/exception.hpp" -#include "remote_config/listeners/listener.hpp" -#include "remote_config/product.hpp" - -using capabilities_e = dds::remote_config::protocol::capabilities_e; - -namespace dds { - -namespace mock { - -ACTION(ThrowErrorApplyingConfig) -{ - throw remote_config::error_applying_config("some error"); -}; - -class listener_mock : public remote_config::listener_base { -public: - listener_mock(std::string name = "MOCK_PRODUCT", - remote_config::protocol::capabilities_e capability = - remote_config::protocol::capabilities_e::ASM_DD_RULES) - : name_(name), capability_(capability){}; - ~listener_mock() override = default; - - MOCK_METHOD( - void, on_update, ((const remote_config::config &config)), (override)); - MOCK_METHOD( - void, on_unapply, ((const remote_config::config &config)), (override)); - MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(void, commit, (), (override)); - - [[nodiscard]] std::unordered_map - get_supported_products() override - { - return {{name_, capability_}}; - } - -protected: - std::string name_; - remote_config::protocol::capabilities_e capability_; -}; -} // namespace mock - -remote_config::config get_config(std::string id) -{ - return {"some product", id, "some contents", "some path", {}, 123, 321, - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; -} - -remote_config::config get_config() { return get_config("some id"); } - -remote_config::config unacknowledged(remote_config::config c) -{ - c.apply_state = - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED; - return c; -} - -remote_config::config acknowledged(remote_config::config c) -{ - c.apply_state = - remote_config::protocol::config_state::applied_state::ACKNOWLEDGED; - return c; -} - -TEST(RemoteConfigProduct, InvalidListener) -{ - EXPECT_THROW(remote_config::product("", nullptr), std::runtime_error); -} - -TEST(RemoteConfigProduct, NameFromListenerIsSaved) -{ - auto listener = std::make_shared(); - remote_config::product product("MOCK_PRODUCT", listener); - - EXPECT_EQ("MOCK_PRODUCT", product.get_name()); -} - -TEST(RemoteConfigProduct, ConfigsAreEmptyByDefault) -{ - auto listener = std::make_shared(); - remote_config::product product("MOCK_PRODUCT", listener); - - EXPECT_EQ(0, product.get_configs().size()); -} - -TEST(RemoteConfigProduct, ConfigsAreSaved) -{ - auto listener = std::make_shared(); - remote_config::product product("MOCK_PRODUCT", listener); - - remote_config::config config = get_config(); - - EXPECT_CALL(*listener, on_update(config)).Times(1); - - product.assign_configs({{"config name", config}}); - - auto configs_on_product = product.get_configs(); - auto config_saved = configs_on_product.find("config name"); - - EXPECT_EQ(1, configs_on_product.size()); - EXPECT_EQ("config name", config_saved->first); - EXPECT_EQ(acknowledged(config), config_saved->second); -} - -TEST( - RemoteConfigProduct, WhenAConfigIsSavedTheProductListenerIsCalledToOnUpdate) -{ - auto listener = std::make_shared(); - remote_config::config config = get_config(); - EXPECT_CALL(*listener, on_update(config)).Times(1); - EXPECT_CALL(*listener, on_unapply(_)).Times(0); - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name", config}}); - - EXPECT_EQ( - remote_config::protocol::config_state::applied_state::ACKNOWLEDGED, - product.get_configs().find("config name")->second.apply_state); -} - -TEST(RemoteConfigProduct, - WhenAConfigIsRemovedTheProductListenerIsCalledToUnApply) -{ - auto listener = std::make_shared(); - remote_config::config config = get_config(); - - EXPECT_CALL(*listener, on_update(unacknowledged(config))).Times(1); - EXPECT_CALL(*listener, on_unapply(acknowledged(config))).Times(1); - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name", unacknowledged(config)}}); - product.assign_configs({}); - - EXPECT_EQ(0, product.get_configs().size()); -} - -TEST(RemoteConfigProduct, WhenConfigDoesNotChangeItsListenersShouldNotBeCalled) -{ - auto listener = std::make_shared(); - remote_config::config config01 = get_config("id 01"); - remote_config::config config02 = get_config("id 02"); - - EXPECT_CALL(*listener, on_update(unacknowledged(config01))).Times(1); - EXPECT_CALL(*listener, on_update(unacknowledged(config02))).Times(1); - - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name 01", unacknowledged(config01)}, - {"config name 02", unacknowledged(config02)}}); - product.assign_configs({{"config name 01", unacknowledged(config01)}, - {"config name 02", unacknowledged(config02)}}); - - EXPECT_EQ(2, product.get_configs().size()); -} - -TEST(RemoteConfigProduct, EvenIfJustOneKeyConfigIsDiferentItCallsToAllListeners) -{ - auto listener = std::make_shared(); - remote_config::config config01 = get_config("id 01"); - remote_config::config config02 = get_config("id 02"); - remote_config::config config03 = get_config("id 03"); - - EXPECT_CALL(*listener, on_update(unacknowledged(config01))).Times(1); - EXPECT_CALL(*listener, on_update(acknowledged(config01))).Times(1); - EXPECT_CALL(*listener, on_update(unacknowledged(config02))).Times(1); - EXPECT_CALL(*listener, on_update(acknowledged(config02))).Times(1); - EXPECT_CALL(*listener, on_update(unacknowledged(config03))).Times(1); - - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name 01", unacknowledged(config01)}, - {"config name 02", unacknowledged(config02)}}); - product.assign_configs({{"config name 01", unacknowledged(config01)}, - {"config name 02", unacknowledged(config02)}, - {"config name 03", unacknowledged(config03)}}); - - EXPECT_EQ(3, product.get_configs().size()); -} - -TEST(RemoteConfigProduct, WhenAConfigGetsDeletedItAlsoUpdateWaf) -{ - auto listener = std::make_shared(); - remote_config::config config01 = get_config("id 01"); - remote_config::config config02 = get_config("id 02"); - - EXPECT_CALL(*listener, on_update(unacknowledged(config01))).Times(1); - EXPECT_CALL(*listener, on_update(acknowledged(config01))).Times(1); - EXPECT_CALL(*listener, on_update(unacknowledged(config02))).Times(1); - EXPECT_CALL(*listener, on_unapply(acknowledged(config02))).Times(1); - - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name 01", unacknowledged(config01)}, - {"config name 02", unacknowledged(config02)}}); - product.assign_configs({{"config name 01", unacknowledged(config01)}}); - - EXPECT_EQ(1, product.get_configs().size()); -} - -TEST(RemoteConfigProduct, WhenAConfigChangeItsHashItsListenerUpdateIsCalled) -{ - auto listener = std::make_shared(); - remote_config::config config = get_config(); - remote_config::config same_config_different_hash = get_config(); - same_config_different_hash.hashes.emplace("hash key", "hash value"); - - EXPECT_CALL(*listener, on_update(unacknowledged(config))).Times(1); - EXPECT_CALL( - *listener, on_update(unacknowledged(same_config_different_hash))) - .Times(1); - EXPECT_CALL(*listener, on_unapply(_)).Times(0); - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name", config}}); - product.assign_configs({{"config name", same_config_different_hash}}); - - EXPECT_EQ(1, product.get_configs().size()); -} - -TEST(RemoteConfigProduct, SameConfigWithDifferentNameItsTreatedAsNewConfig) -{ - auto listener = std::make_shared(); - remote_config::config config = get_config(); - - EXPECT_CALL(*listener, on_update(unacknowledged(config))).Times(2); - EXPECT_CALL(*listener, on_unapply(acknowledged(config))).Times(1); - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name 01", config}}); - product.assign_configs({{"config name 02", config}}); - - EXPECT_EQ(1, product.get_configs().size()); -} - -TEST(RemoteConfigProduct, WhenAListenerFailsUpdatingAConfigItsStateGetsError) -{ - auto listener = std::make_shared(); - remote_config::config config = get_config(); - - EXPECT_CALL(*listener, on_update(_)) - .WillRepeatedly(mock::ThrowErrorApplyingConfig()); - remote_config::product product("MOCK_PRODUCT", listener); - - product.assign_configs({{"config name", config}}); - - EXPECT_EQ(1, product.get_configs().size()); - EXPECT_EQ(remote_config::protocol::config_state::applied_state::ERROR, - product.get_configs().find("config name")->second.apply_state); - EXPECT_EQ("some error", - product.get_configs().find("config name")->second.apply_error); -} - -} // namespace dds diff --git a/appsec/tests/helper/remote_config/serializer_test.cpp b/appsec/tests/helper/remote_config/serializer_test.cpp deleted file mode 100644 index 7ed8d0f3a5..0000000000 --- a/appsec/tests/helper/remote_config/serializer_test.cpp +++ /dev/null @@ -1,351 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are -// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. -// -// This product includes software developed at Datadog -// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -#include -#include - -#include "../common.hpp" -#include "base64.h" -#include "remote_config/protocol/client.hpp" -#include "remote_config/protocol/client_state.hpp" -#include "remote_config/protocol/client_tracer.hpp" -#include "remote_config/protocol/config_state.hpp" -#include "remote_config/protocol/tuf/get_configs_request.hpp" -#include "remote_config/protocol/tuf/serializer.hpp" - -namespace dds { - -bool array_contains_string(const rapidjson::Value &array, const char *searched) -{ - if (!array.IsArray()) { - return false; - } - - bool result = false; - for (rapidjson::Value::ConstValueIterator itr = array.Begin(); - itr != array.End(); ++itr) { - if (itr->IsString() && strcmp(searched, itr->GetString()) == 0) { - result = true; - } - } - - return result; -} - -rapidjson::Value::ConstMemberIterator assert_it_contains_string( - const rapidjson::Value &parent_field, const char *key, const char *value) -{ - rapidjson::Value::ConstMemberIterator tmp_itr = - parent_field.FindMember(key); - bool found = false; - if (tmp_itr != parent_field.MemberEnd()) { - found = true; - } - EXPECT_TRUE(found) << "Key " << key << " not found"; - EXPECT_EQ(rapidjson::kStringType, tmp_itr->value.GetType()); - EXPECT_EQ(value, tmp_itr->value); - - return tmp_itr; -} - -rapidjson::Value::ConstMemberIterator assert_it_contains_int( - const rapidjson::Value &parent_field, const char *key, int value) -{ - rapidjson::Value::ConstMemberIterator tmp_itr = - parent_field.FindMember(key); - bool found = false; - if (tmp_itr != parent_field.MemberEnd()) { - found = true; - } - EXPECT_TRUE(found) << "Key " << key << " not found"; - EXPECT_EQ(rapidjson::kNumberType, tmp_itr->value.GetType()); - EXPECT_EQ(value, tmp_itr->value); - - return tmp_itr; -} - -rapidjson::Value::ConstMemberIterator assert_it_contains_bool( - const rapidjson::Value &parent_field, const char *key, bool value) -{ - rapidjson::Value::ConstMemberIterator tmp_itr = - parent_field.FindMember(key); - bool found = false; - if (tmp_itr != parent_field.MemberEnd()) { - found = true; - } - EXPECT_TRUE(found) << "Key " << key << " not found"; - rapidjson::Type type = rapidjson::kTrueType; - if (!value) { - type = rapidjson::kFalseType; - } - EXPECT_EQ(type, tmp_itr->value.GetType()); - EXPECT_EQ(value, tmp_itr->value); - - return tmp_itr; -} - -rapidjson::Value::ConstMemberIterator find_and_assert_type( - const rapidjson::Value &parent_field, const char *key, rapidjson::Type type) -{ - rapidjson::Value::ConstMemberIterator tmp_itr = - parent_field.FindMember(key); - bool found = false; - if (tmp_itr != parent_field.MemberEnd()) { - found = true; - } - EXPECT_TRUE(found) << "Key " << key << " not found"; - EXPECT_EQ(type, tmp_itr->value.GetType()) - << "Key " << key << " not matching expected type"; - - return tmp_itr; -} - -int config_state_version = 456; -int targets_version = 123; - -remote_config::protocol::client get_client() -{ - remote_config::protocol::client_tracer client_tracer = {"some runtime id", - "some tracer version", "some service", {"extra01", "extra02"}, - "some env", "some app version"}; - - std::vector config_states; - - remote_config::protocol::config_state cs_unknown = { - "unknown config_state id", 11, "unknown config_state product", - remote_config::protocol::config_state::applied_state::UNKNOWN, ""}; - remote_config::protocol::config_state cs_unacknowledged = { - "unacknowledged config_state id", 22, - "unacknowledged config_state product", - remote_config::protocol::config_state::applied_state::UNACKNOWLEDGED, - ""}; - remote_config::protocol::config_state cs_acknowledged = { - "acknowledged config_state id", 33, "acknowledged config_state product", - remote_config::protocol::config_state::applied_state::ACKNOWLEDGED, ""}; - remote_config::protocol::config_state cs_error = {"error config_state id", - 44, "error config_state product", - remote_config::protocol::config_state::applied_state::ERROR, - "error description"}; - config_states.push_back(cs_unknown); - config_states.push_back(cs_unacknowledged); - config_states.push_back(cs_acknowledged); - config_states.push_back(cs_error); - - remote_config::protocol::client_state client_s = { - targets_version, config_states, false, "", "some backend client state"}; - - return {"some_id", {"ASM_DD"}, client_tracer, client_s}; -} - -std::vector -get_cached_target_files() -{ - std::vector - cached_target_files; - - std::vector first_hashes; - remote_config::protocol::cached_target_files_hash first_hash{ - "first hash algorithm", "first hash hash"}; - first_hashes.push_back(first_hash); - remote_config::protocol::cached_target_files first{ - "first some path", 1, std::move(first_hashes)}; - cached_target_files.push_back(first); - - std::vector - second_hashes; - remote_config::protocol::cached_target_files_hash second_hash{ - "second hash algorithm", "second hash hash"}; - second_hashes.push_back(second_hash); - remote_config::protocol::cached_target_files second{ - "second some path", 1, std::move(second_hashes)}; - cached_target_files.push_back(second); - - return cached_target_files; -} - -TEST(RemoteConfigSerializer, RequestCanBeSerializedWithClientField) -{ - remote_config::protocol::get_configs_request request = { - get_client(), get_cached_target_files()}; - - std::optional serialised_string; - serialised_string = remote_config::protocol::serialize(std::move(request)); - - EXPECT_TRUE(serialised_string); - - // Lets transform the resulting string back to json so we can assert more - // easily - rapidjson::Document serialized_doc; - serialized_doc.Parse(serialised_string.value()); - - // Client fields - rapidjson::Value::ConstMemberIterator client_itr = - find_and_assert_type(serialized_doc, "client", rapidjson::kObjectType); - - assert_it_contains_string(client_itr->value, "id", "some_id"); - assert_it_contains_bool(client_itr->value, "is_tracer", true); - - // Client products fields - rapidjson::Value::ConstMemberIterator products_itr = find_and_assert_type( - client_itr->value, "products", rapidjson::kArrayType); - array_contains_string(products_itr->value, "ASM_DD"); - - // Client tracer fields - rapidjson::Value::ConstMemberIterator client_tracer_itr = - find_and_assert_type( - client_itr->value, "client_tracer", rapidjson::kObjectType); - assert_it_contains_string(client_tracer_itr->value, "language", "php"); - assert_it_contains_string( - client_tracer_itr->value, "runtime_id", "some runtime id"); - assert_it_contains_string( - client_tracer_itr->value, "tracer_version", "some tracer version"); - assert_it_contains_string( - client_tracer_itr->value, "service", "some service"); - assert_it_contains_string(client_tracer_itr->value, "env", "some env"); - assert_it_contains_string( - client_tracer_itr->value, "app_version", "some app version"); - - rapidjson::Value::ConstMemberIterator extra_services_itr = - find_and_assert_type( - client_tracer_itr->value, "extra_services", rapidjson::kArrayType); - EXPECT_EQ("extra01", extra_services_itr->value[0]); - EXPECT_EQ("extra02", extra_services_itr->value[1]); - - // Client state fields - rapidjson::Value::ConstMemberIterator client_state_itr = - find_and_assert_type( - client_itr->value, "state", rapidjson::kObjectType); - assert_it_contains_int( - client_state_itr->value, "targets_version", targets_version); - assert_it_contains_int(client_state_itr->value, "root_version", 1); - assert_it_contains_bool(client_state_itr->value, "has_error", false); - assert_it_contains_string(client_state_itr->value, "error", ""); - assert_it_contains_string(client_state_itr->value, "backend_client_state", - "some backend client state"); - - // Config state fields - rapidjson::Value::ConstMemberIterator config_states_itr = - find_and_assert_type( - client_state_itr->value, "config_states", rapidjson::kArrayType); - ; - - // UNKNOWN - rapidjson::Value::ConstValueIterator itr = config_states_itr->value.Begin(); - assert_it_contains_string(*itr, "id", "unknown config_state id"); - assert_it_contains_int(*itr, "version", 11); - assert_it_contains_string(*itr, "product", "unknown config_state product"); - assert_it_contains_int(*itr, "apply_state", 0); - assert_it_contains_string(*itr, "apply_error", ""); - // UNACKNOWLEDGED - itr++; - assert_it_contains_string(*itr, "id", "unacknowledged config_state id"); - assert_it_contains_int(*itr, "version", 22); - assert_it_contains_string( - *itr, "product", "unacknowledged config_state product"); - assert_it_contains_int(*itr, "apply_state", 1); - assert_it_contains_string(*itr, "apply_error", ""); - // ACKNOWLEDGED - itr++; - assert_it_contains_string(*itr, "id", "acknowledged config_state id"); - assert_it_contains_int(*itr, "version", 33); - assert_it_contains_string( - *itr, "product", "acknowledged config_state product"); - assert_it_contains_int(*itr, "apply_state", 2); - assert_it_contains_string(*itr, "apply_error", ""); - // ERROR - itr++; - assert_it_contains_string(*itr, "id", "error config_state id"); - assert_it_contains_int(*itr, "version", 44); - assert_it_contains_string(*itr, "product", "error config_state product"); - assert_it_contains_int(*itr, "apply_state", 3); - assert_it_contains_string(*itr, "apply_error", "error description"); -} - -TEST(RemoteConfigSerializer, RequestCanBeSerializedWithCachedTargetFields) -{ - remote_config::protocol::get_configs_request request = { - get_client(), get_cached_target_files()}; - - std::optional serialised_string; - serialised_string = remote_config::protocol::serialize(std::move(request)); - - EXPECT_TRUE(serialised_string); - - // Lets transform the resulting string back to json so we can assert more - // easily - rapidjson::Document serialized_doc; - serialized_doc.Parse(serialised_string.value()); - - // cached_target_files fields - rapidjson::Value::ConstMemberIterator cached_target_files_itr = - find_and_assert_type( - serialized_doc, "cached_target_files", rapidjson::kArrayType); - - EXPECT_EQ(2, cached_target_files_itr->value.Size()); - - rapidjson::Value::ConstValueIterator first = - cached_target_files_itr->value.Begin(); - assert_it_contains_string(*first, "path", "first some path"); - assert_it_contains_int(*first, "length", 1); - - // Cached target file hash of first - rapidjson::Value::ConstMemberIterator first_cached_target_files_hash = - find_and_assert_type(*first, "hashes", rapidjson::kArrayType); - EXPECT_EQ(1, first_cached_target_files_hash->value.Size()); - assert_it_contains_string(*first_cached_target_files_hash->value.Begin(), - "algorithm", "first hash algorithm"); - assert_it_contains_string(*first_cached_target_files_hash->value.Begin(), - "hash", "first hash hash"); - - rapidjson::Value::ConstValueIterator second = - std::next(cached_target_files_itr->value.Begin()); - assert_it_contains_string(*second, "path", "second some path"); - assert_it_contains_int(*second, "length", 1); - - // Cached target file hash of second - rapidjson::Value::ConstMemberIterator second_cached_target_files_hash = - find_and_assert_type(*second, "hashes", rapidjson::kArrayType); - EXPECT_EQ(1, second_cached_target_files_hash->value.Size()); - assert_it_contains_string(*second_cached_target_files_hash->value.Begin(), - "algorithm", "second hash algorithm"); - assert_it_contains_string(*second_cached_target_files_hash->value.Begin(), - "hash", "second hash hash"); -} - -TEST(RemoteConfigSerializer, CapabilitiesCanBeSet) -{ - auto client = get_client(); - client.capabilities = - remote_config::protocol::capabilities_e::RESERVED | - remote_config::protocol::capabilities_e::ASM_ACTIVATION | - remote_config::protocol::capabilities_e::ASM_IP_BLOCKING | - remote_config::protocol::capabilities_e::ASM_DD_RULES; - - remote_config::protocol::get_configs_request request = { - client, get_cached_target_files()}; - - std::optional serialised_string; - serialised_string = remote_config::protocol::serialize(std::move(request)); - - EXPECT_TRUE(serialised_string); - - // Lets transform the resulting string back to json so we can assert more - // easily - rapidjson::Document serialized_doc; - serialized_doc.Parse(serialised_string.value()); - - // Client fields - rapidjson::Value::ConstMemberIterator client_itr = - find_and_assert_type(serialized_doc, "client", rapidjson::kObjectType); - - rapidjson::Value::ConstMemberIterator capabilities_itr = - find_and_assert_type( - client_itr->value, "capabilities", rapidjson::kStringType); - - EXPECT_STREQ("AA8=", capabilities_itr->value.GetString()); -} - -} // namespace dds diff --git a/appsec/tests/helper/service_manager_test.cpp b/appsec/tests/helper/service_manager_test.cpp index 9fe5a9273e..bd1995dc85 100644 --- a/appsec/tests/helper/service_manager_test.cpp +++ b/appsec/tests/helper/service_manager_test.cpp @@ -33,21 +33,17 @@ TEST(ServiceManagerTest, LoadRulesOK) dds::engine_settings engine_settings; engine_settings.rules_file = fn; engine_settings.waf_timeout_us = 42; - auto service = manager.create_service({"service", {}, "env", "", "", ""}, - engine_settings, {}, meta, metrics, {}); + auto service = + manager.create_service(engine_settings, {}, meta, metrics, {}); + auto *service_rp = service.get(); EXPECT_EQ(manager.get_cache().size(), 1); EXPECT_EQ(metrics[tag::event_rules_loaded], 4); // loading again should take from the cache - auto service2 = manager.create_service({"service", {}, "env", "", "", ""}, - engine_settings, {}, meta, metrics, {}); - EXPECT_EQ(manager.get_cache().size(), 1); - - // Even with different extra services, it should get the same - auto service3 = manager.create_service( - {"service", {"some", "services"}, "env", "", "", ""}, engine_settings, - {}, meta, metrics, {}); + auto service2 = + manager.create_service(engine_settings, {}, meta, metrics, {}); EXPECT_EQ(manager.get_cache().size(), 1); + EXPECT_EQ(service, service2); // destroying the services should expire the cache ptr auto cache_it = manager.get_cache().begin(); @@ -58,20 +54,19 @@ TEST(ServiceManagerTest, LoadRulesOK) service.reset(); ASSERT_FALSE(weak_ptr.expired()); service2.reset(); - ASSERT_FALSE(weak_ptr.expired()); - service3.reset(); // the last one should be kept by the manager ASSERT_FALSE(weak_ptr.expired()); // loading another service should cleanup the cache - auto service4 = manager.create_service( - {"service2", {}, "env"}, engine_settings, {}, meta, metrics, {}); + auto service3 = + manager.create_service(engine_settings, {true}, meta, metrics, {}); + ASSERT_NE(service3.get(), service_rp); ASSERT_TRUE(weak_ptr.expired()); EXPECT_EQ(manager.get_cache().size(), 1); // another service identifier should result in another service - auto service5 = manager.create_service({"service", {}, "env", "", "", ""}, - engine_settings, {}, meta, metrics, {}); + auto service4 = + manager.create_service(engine_settings, {}, meta, metrics, {}); EXPECT_EQ(manager.get_cache().size(), 2); } @@ -86,8 +81,7 @@ TEST(ServiceManagerTest, LoadRulesFileNotFound) dds::engine_settings engine_settings; engine_settings.rules_file = "/file/that/does/not/exist"; engine_settings.waf_timeout_us = 42; - manager.create_service( - {"s", {}, "e"}, engine_settings, {}, meta, metrics, {}); + manager.create_service(engine_settings, {}, meta, metrics, {}); }, std::runtime_error); } @@ -103,8 +97,7 @@ TEST(ServiceManagerTest, BadRulesFile) dds::engine_settings engine_settings; engine_settings.rules_file = "/dev/null"; engine_settings.waf_timeout_us = 42; - manager.create_service( - {"s", {}, "e"}, engine_settings, {}, meta, metrics, {}); + manager.create_service(engine_settings, {}, meta, metrics, {}); }, dds::parsing_error); } @@ -119,12 +112,10 @@ TEST(ServiceManagerTest, UniqueServices) dds::engine_settings engine_settings; engine_settings.rules_file = fn; - auto service1 = manager.create_service( - {"service", {}, "env", "1.0", "2.0", "runtime ID 0"}, engine_settings, - {}, meta, metrics, {}); - auto service2 = manager.create_service( - {"service", {}, "env", "1.1", "3.0", "runtime ID 1"}, engine_settings, - {}, meta, metrics, {}); + auto service1 = + manager.create_service(engine_settings, {}, meta, metrics, {}); + auto service2 = + manager.create_service(engine_settings, {}, meta, metrics, {}); EXPECT_EQ(service1.get(), service2.get()); } diff --git a/appsec/tests/helper/service_test.cpp b/appsec/tests/helper/service_test.cpp index 9fd6bfc340..815fa0d03a 100644 --- a/appsec/tests/helper/service_test.cpp +++ b/appsec/tests/helper/service_test.cpp @@ -5,26 +5,62 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #include "common.hpp" #include "remote_config/mocks.hpp" +#include "remote_config/settings.hpp" #include #include #include +extern "C" { +struct ddog_CharSlice { + const char *ptr; + uintptr_t len; +}; +struct ddog_RemoteConfigReader { + std::string shm_path; + struct ddog_CharSlice next_line; +}; +__attribute__((visibility("default"))) ddog_RemoteConfigReader * +ddog_remote_config_reader_for_path(const char *path) +{ + return new ddog_RemoteConfigReader{path}; +} +__attribute__((visibility("default"))) bool ddog_remote_config_read( + ddog_RemoteConfigReader *reader, ddog_CharSlice *data) +{ + if (reader->next_line.len == 0) { + return false; + } + data->ptr = reader->next_line.ptr; + data->len = reader->next_line.len; + reader->next_line.len = 0; + return true; +} +__attribute__((visibility("default"))) void ddog_remote_config_reader_drop( + struct ddog_RemoteConfigReader *reader) +{ + delete reader; +} + +__attribute__((constructor)) void resolve_symbols() +{ + dds::remote_config::resolve_symbols(); +} +} + namespace dds { TEST(ServiceTest, NullEngine) { - service_identifier sid{"service", {"extra01", "extra02"}, "env", - "tracer_version", "app_version", "runtime_id"}; - std::shared_ptr engine; - auto client = std::make_unique(sid); - EXPECT_CALL(*client, poll).Times(0); + std::shared_ptr engine{}; + remote_config::settings rc_settings{true, ""}; + auto client = remote_config::client::from_settings(rc_settings, {}); auto service_config = std::make_shared(); - auto client_handler = std::make_shared( - std::move(client), service_config, 1s); + auto client_handler = std::make_unique( + std::move(client), service_config); EXPECT_THROW( - auto s = service(engine, service_config, std::move(client_handler)), + auto s = service(engine, service_config, std::move(client_handler), ""), std::runtime_error); } @@ -35,7 +71,7 @@ TEST(ServiceTest, NullServiceHandler) // A null service handler doesn't make a difference as remote config is // optional - service svc{engine, service_config, nullptr}; + service svc{engine, service_config, nullptr, ""}; EXPECT_EQ(engine.get(), svc.get_engine().get()); } @@ -43,9 +79,7 @@ TEST(ServiceTest, ServicePickSchemaExtractionSamples) { std::shared_ptr engine{engine::create()}; - service_identifier sid{"service", {"extra01", "extra02"}, "env", - "tracer_version", "app_version", "runtime_id"}; - auto client = std::make_unique(sid); + auto client = remote_config::client::from_settings({true}, {}); auto service_config = std::make_shared(); engine_settings engine_settings = {}; engine_settings.rules_file = create_sample_rules_ok(); @@ -54,8 +88,8 @@ TEST(ServiceTest, ServicePickSchemaExtractionSamples) { // Constructor. It picks based on rate double all_requests_are_picked = 1.0; - auto s = service( - engine, service_config, nullptr, {true, all_requests_are_picked}); + auto s = service(engine, service_config, nullptr, "", + {true, all_requests_are_picked}); EXPECT_TRUE(s.get_schema_sampler()->picked()); } @@ -63,7 +97,7 @@ TEST(ServiceTest, ServicePickSchemaExtractionSamples) { // Constructor. It does not pick based on rate double no_request_is_picked = 0.0; auto s = service( - engine, service_config, nullptr, {true, no_request_is_picked}); + engine, service_config, nullptr, "", {true, no_request_is_picked}); EXPECT_FALSE(s.get_schema_sampler()->picked()); } @@ -71,7 +105,7 @@ TEST(ServiceTest, ServicePickSchemaExtractionSamples) { // Constructor. It does not pick if disabled double all_requests_are_picked = 1.0; bool schema_extraction_disabled = false; - auto s = service(engine, service_config, nullptr, + auto s = service(engine, service_config, nullptr, "", {schema_extraction_disabled, all_requests_are_picked}); EXPECT_FALSE(s.get_schema_sampler()->picked()); @@ -80,8 +114,8 @@ TEST(ServiceTest, ServicePickSchemaExtractionSamples) { // Static constructor. It picks based on rate engine_settings.schema_extraction.enabled = true; engine_settings.schema_extraction.sample_rate = 1.0; - auto service = service::from_settings( - service_identifier(sid), engine_settings, {}, meta, metrics, false); + auto service = + service::from_settings(engine_settings, {}, meta, metrics, false); EXPECT_TRUE(service->get_schema_sampler()->picked()); } @@ -89,8 +123,8 @@ TEST(ServiceTest, ServicePickSchemaExtractionSamples) { // Static constructor. It does not pick based on rate engine_settings.schema_extraction.enabled = true; engine_settings.schema_extraction.sample_rate = 0.0; - auto service = service::from_settings( - service_identifier(sid), engine_settings, {}, meta, metrics, false); + auto service = + service::from_settings(engine_settings, {}, meta, metrics, false); EXPECT_FALSE(service->get_schema_sampler()->picked()); } @@ -98,8 +132,8 @@ TEST(ServiceTest, ServicePickSchemaExtractionSamples) { // Static constructor. It does not pick if disabled engine_settings.schema_extraction.enabled = false; engine_settings.schema_extraction.sample_rate = 1.0; - auto service = service::from_settings( - service_identifier(sid), engine_settings, {}, meta, metrics, false); + auto service = + service::from_settings(engine_settings, {}, meta, metrics, false); EXPECT_FALSE(service->get_schema_sampler()->picked()); } diff --git a/appsec/tests/helper/waf_test.cpp b/appsec/tests/helper/waf_test.cpp index 576d70de3c..0c3d533ce5 100644 --- a/appsec/tests/helper/waf_test.cpp +++ b/appsec/tests/helper/waf_test.cpp @@ -44,7 +44,7 @@ TEST(WafTest, InitWithInvalidRules) std::map meta; std::map metrics; - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_settings(cs, ruleset, meta, metrics)}; EXPECT_EQ(meta.size(), 2); @@ -70,7 +70,8 @@ TEST(WafTest, RunWithInvalidParam) std::map meta; std::map metrics; - subscriber::ptr wi{waf::instance::from_string(waf_rule, meta, metrics)}; + std::shared_ptr wi{ + waf::instance::from_string(waf_rule, meta, metrics)}; auto ctx = wi->get_listener(); parameter_view pv; dds::event e; @@ -82,7 +83,8 @@ TEST(WafTest, RunWithTimeout) std::map meta; std::map metrics; - subscriber::ptr wi(waf::instance::from_string(waf_rule, meta, metrics, 0)); + std::shared_ptr wi( + waf::instance::from_string(waf_rule, meta, metrics, 0)); auto ctx = wi->get_listener(); auto p = parameter::map(); @@ -99,7 +101,8 @@ TEST(WafTest, ValidRunGood) std::map meta; std::map metrics; - subscriber::ptr wi{waf::instance::from_string(waf_rule, meta, metrics)}; + std::shared_ptr wi{ + waf::instance::from_string(waf_rule, meta, metrics)}; auto ctx = wi->get_listener(); auto p = parameter::map(); @@ -119,7 +122,8 @@ TEST(WafTest, ValidRunMonitor) std::map meta; std::map metrics; - subscriber::ptr wi{waf::instance::from_string(waf_rule, meta, metrics)}; + std::shared_ptr wi{ + waf::instance::from_string(waf_rule, meta, metrics)}; auto ctx = wi->get_listener(); auto p = parameter::map(); @@ -148,8 +152,9 @@ TEST(WafTest, ValidRunMonitorObfuscated) std::map meta; std::map metrics; - subscriber::ptr wi{waf::instance::from_string(waf_rule, meta, metrics, - waf::instance::default_waf_timeout_us, "password"sv, "string 3"sv)}; + std::shared_ptr wi{ + waf::instance::from_string(waf_rule, meta, metrics, + waf::instance::default_waf_timeout_us, "password"sv, "string 3"sv)}; auto ctx = wi->get_listener(); auto p = parameter::map(), sub_p = parameter::map(); @@ -189,7 +194,7 @@ TEST(WafTest, ValidRunMonitorObfuscatedFromSettings) cs.obfuscator_key_regex = "password"; auto ruleset = engine_ruleset::from_path(cs.rules_file); - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_settings(cs, ruleset, meta, metrics)}; auto ctx = wi->get_listener(); @@ -223,7 +228,7 @@ TEST(WafTest, UpdateRuleData) std::map meta; std::map metrics; - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_string(waf_rule_with_data, meta, metrics)}; ASSERT_TRUE(wi); @@ -282,7 +287,7 @@ TEST(WafTest, UpdateInvalid) std::map meta; std::map metrics; - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_string(waf_rule_with_data, meta, metrics)}; ASSERT_TRUE(wi); @@ -307,7 +312,8 @@ TEST(WafTest, SchemasAreAdded) std::map meta; std::map metrics; - subscriber::ptr wi{waf::instance::from_string(waf_rule, meta, metrics)}; + std::shared_ptr wi{ + waf::instance::from_string(waf_rule, meta, metrics)}; auto ctx = wi->get_listener(); auto p = parameter::map(), sub_p = parameter::map(); @@ -343,7 +349,7 @@ TEST(WafTest, ActionsAreSentAndParsed) std::string rules_with_actions = R"({"version":"2.1","rules":[{"id":"blk-001-001","name":"BlockIPAddresses","tags":{"type":"block_ip","category":"security_response"},"conditions":[{"parameters":{"inputs":[{"address":"http.client_ip"}],"data":"blocked_ips"},"operator":"ip_match"}],"transformers":[],"on_match":["custom"]}],"actions":[{"id":"custom","type":"block_request","parameters":{"status_code":123,"grpc_status_code":321,"type":"json","custom_param":"foo"}}],"rules_data":[{"id":"blocked_ips","type":"data_with_expiration","data":[{"value":"192.168.1.1","expiration":"9999999999"}]}]})"; - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_string(rules_with_actions, meta, metrics)}; ASSERT_TRUE(wi); @@ -382,7 +388,7 @@ TEST(WafTest, ActionsAreSentAndParsed) std::string rules_with_actions = R"({"version":"2.1","rules":[{"id":"blk-001-001","name":"BlockIPAddresses","tags":{"type":"block_ip","category":"security_response"},"conditions":[{"parameters":{"inputs":[{"address":"http.client_ip"}],"data":"blocked_ips"},"operator":"ip_match"}],"transformers":[],"on_match":["custom"]}],"actions":[{"id":"custom","type":"block_request","parameters":{}}],"rules_data":[{"id":"blocked_ips","type":"data_with_expiration","data":[{"value":"192.168.1.1","expiration":"9999999999"}]}]})"; - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_string(rules_with_actions, meta, metrics)}; ASSERT_TRUE(wi); @@ -421,7 +427,7 @@ TEST(WafTest, ActionsAreSentAndParsed) std::string rules_with_actions = R"({"version":"2.1","rules":[{"id":"blk-001-001","name":"BlockIPAddresses","tags":{"type":"block_ip","category":"security_response"},"conditions":[{"parameters":{"inputs":[{"address":"http.client_ip"}],"data":"blocked_ips"},"operator":"ip_match"}],"transformers":[],"on_match":["custom"]}],"actions":[{"id":"custom","type":"custom_type","parameters":{"some":"parameter"}}],"rules_data":[{"id":"blocked_ips","type":"data_with_expiration","data":[{"value":"192.168.1.1","expiration":"9999999999"}]}]})"; - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_string(rules_with_actions, meta, metrics)}; ASSERT_TRUE(wi); @@ -455,7 +461,7 @@ TEST(WafTest, ActionsAreSentAndParsed) std::string rules_with_actions = R"({"version":"2.1","rules":[{"id":"blk-001-001","name":"BlockIPAddresses","tags":{"type":"block_ip","category":"security_response"},"conditions":[{"parameters":{"inputs":[{"address":"http.client_ip"}],"data":"blocked_ips"},"operator":"ip_match"}],"transformers":[],"on_match":["block"]}], "rules_data":[{"id":"blocked_ips","type":"data_with_expiration","data":[{"value":"192.168.1.1","expiration":"9999999999"}]}]})"; - subscriber::ptr wi{ + std::shared_ptr wi{ waf::instance::from_string(rules_with_actions, meta, metrics)}; ASSERT_TRUE(wi); diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index 4103c09868..2402f703be 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -286,6 +286,7 @@ def buildTracerTask = { String version, String variant -> '../../../ext', '../../../zend_abstract_interface', '../../../libdatadog', + '../../../ddtrace.sym', ], ], outputs: [ diff --git a/appsec/tests/integration/src/docker/php/Dockerfile-php-deps b/appsec/tests/integration/src/docker/php/Dockerfile-php-deps index 82bbd360e7..dcbfec8ddd 100644 --- a/appsec/tests/integration/src/docker/php/Dockerfile-php-deps +++ b/appsec/tests/integration/src/docker/php/Dockerfile-php-deps @@ -17,9 +17,10 @@ RUN apt-get update && apt-get install -y \ python3-dev \ vim \ && rm -rf /var/lib/apt/lists/* + ADD build_dev_php.sh /build/php/ +RUN USER=root /build/php/build_dev_php.sh deps -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.73.0 -y +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.81.0 -y ENV PATH="/root/.cargo/bin:${PATH}" -RUN USER=root /build/php/build_dev_php.sh deps diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy index 625e58c467..605f8450f5 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/AppSecContainer.groovy @@ -1,6 +1,9 @@ package com.datadog.appsec.php.docker import com.datadog.appsec.php.mock_agent.MockDatadogAgent +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigRequest +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigResponse +import com.datadog.appsec.php.mock_agent.rem_cfg.Target import com.datadog.appsec.php.model.Span import com.datadog.appsec.php.model.Trace import com.github.dockerjava.api.command.CreateContainerCmd @@ -73,6 +76,7 @@ class AppSecContainer> extends GenericContain withEnv 'DD_TRACE_LOG_LEVEL', 'info,startup=off' withEnv 'DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS', '0' withEnv 'DD_TRACE_AGENT_FLUSH_INTERVAL', '0' + withEnv 'DD_TRACE_SIDECAR_TRACE_SENDER', '0' withEnv 'DD_TRACE_DEBUG', '1' withEnv 'DD_AUTOLOAD_NO_COMPILE', 'true' // must be exactly 'true' withEnv 'DD_TRACE_GIT_METADATA_ENABLED', '0' @@ -117,6 +121,14 @@ class AppSecContainer> extends GenericContain mockDatadogAgent.drainTelemetry(timeoutInMs) } + void setNextRCResponse(Target target, RemoteConfigResponse nextResponse) { + mockDatadogAgent.setNextRCResponse(target, nextResponse) + } + + RemoteConfigRequest waitForRCVersion(Target target, long version, long timeoutInMs) { + mockDatadogAgent.waitForRCVersion(target, version, timeoutInMs) + } + void close() { copyLogs() super.close() diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy index af6b52c189..e4bca9a83a 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/ConfigV07Handler.groovy @@ -2,6 +2,7 @@ package com.datadog.appsec.php.mock_agent import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigResponse import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigRequest +import com.datadog.appsec.php.mock_agent.rem_cfg.Target import com.google.common.collect.Lists import groovy.transform.CompileStatic import groovy.util.logging.Slf4j @@ -13,29 +14,56 @@ import org.jetbrains.annotations.NotNull @CompileStatic @Singleton class ConfigV07Handler implements Handler { - RemoteConfigResponse nextResponse - final List capturedRequests = [] + private final Map nextResponse = [:] + final Map capturedRequests = [:] @Override void handle(@NotNull Context context) throws Exception { RemoteConfigRequest request = context.bodyStreamAsClass(RemoteConfigRequest) log.debug("Received request with version ${request.client.clientState.targetsVersion}: {}", request.toString()) + Target target = request.extractTarget() + RemoteConfigResponse resp + synchronized (this) { + resp = nextResponse[target] + nextResponse[target] = (RemoteConfigResponse) null + } synchronized (capturedRequests) { - capturedRequests.add(request) + capturedRequests[target] = request capturedRequests.notify() } - if (nextResponse != null) { - context.json(nextResponse) + if (resp != null) { + log.info("Sending RC response for {}, req targets version={}, resp targets version={}", + target, request.client.clientState.targetsVersion, resp.targetsSigned.version) + context.json(resp) } else { context.json([:]) } } - void setNextResponse(RemoteConfigResponse nextResponse) { - this.nextResponse = nextResponse + void setNextResponse(Target target, RemoteConfigResponse nextResponse) { + synchronized (this) { + this.nextResponse[target] = nextResponse + } + } + + RemoteConfigRequest waitForVersion(Target target, long version, long timeoutInMs) { + log.debug("waitForVersion start") + long deadline = System.currentTimeMillis() + timeoutInMs + synchronized (capturedRequests) { + while (System.currentTimeMillis() < deadline) { + RemoteConfigRequest request = capturedRequests[target] + if (request != null && request.client.clientState.targetsVersion == version) { + return request + } + capturedRequests.wait(deadline - System.currentTimeMillis()) + } + } + log.debug("waitForVersion timeout") + throw new AssertionError("No request with version $version gotten within " + + "$timeoutInMs ms for $target" as Object) } - List drain(long timeoutInMs) { + List> drain(long timeoutInMs) { synchronized (capturedRequests) { if (capturedRequests.isEmpty()) { capturedRequests.wait(timeoutInMs) diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy index 3895aaf2d5..1451e41588 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/MockDatadogAgent.groovy @@ -1,6 +1,8 @@ package com.datadog.appsec.php.mock_agent - +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigRequest +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigResponse +import com.datadog.appsec.php.mock_agent.rem_cfg.Target import com.datadog.appsec.php.model.Trace import groovy.transform.CompileStatic import groovy.util.logging.Slf4j @@ -69,4 +71,12 @@ class MockDatadogAgent implements Startable { List drainTelemetry(int timeoutInMs) { TelemetryHandler.instance.drain(timeoutInMs) } + + void setNextRCResponse(Target target, RemoteConfigResponse nextResponse) { + ConfigV07Handler.instance.setNextResponse(target, nextResponse) + } + + RemoteConfigRequest waitForRCVersion(Target target, long version, long timeoutInMs) { + ConfigV07Handler.instance.waitForVersion(target, version, timeoutInMs) + } } diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TracesV04Handler.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TracesV04Handler.groovy index 72026e3614..b6882c8dc3 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TracesV04Handler.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/TracesV04Handler.groovy @@ -101,7 +101,9 @@ class TracesV04Handler implements Handler { List traces = [] MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(is) while (unpacker.hasNext()) { - traces << MsgpackHelper.unpackSingle(unpacker) + def trace = MsgpackHelper.unpackSingle(unpacker) + log.debug('Read submitted trace {}', trace) + traces << trace } traces.first() as List ?: [] diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Capability.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Capability.groovy new file mode 100644 index 0000000000..c52e9c01fa --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Capability.groovy @@ -0,0 +1,51 @@ +package com.datadog.appsec.php.mock_agent.rem_cfg + +enum Capability { + ASM_ACTIVATION(1), + ASM_IP_BLOCKING(2), + ASM_DD_RULES(3), + ASM_EXCLUSIONS(4), + ASM_REQUEST_BLOCKING(5), + ASM_RESPONSE_BLOCKING(6), + ASM_USER_BLOCKING(7), + ASM_CUSTOM_RULES(8), + ASM_CUSTOM_BLOCKING_RESPONSE(9), + ASM_TRUSTED_IPS(10), + ASM_API_SECURITY_SAMPLE_RATE(11), + APM_TRACING_SAMPLE_RATE(12), + APM_TRACING_LOGS_INJECTION(13), + APM_TRACING_HTTP_HEADER_TAGS(14), + APM_TRACING_CUSTOM_TAGS(15), + ASM_PROCESSOR_OVERRIDES(16), + ASM_CUSTOM_DATA_SCANNERS(17), + ASM_EXCLUSION_DATA(18), + APM_TRACING_ENABLED(19), + APM_TRACING_DATA_STREAMS_ENABLED(20), + ASM_RASP_SQLI(21), + ASM_RASP_LFI(22), + ASM_RASP_SSRF(23), + ASM_RASP_SHI(24), + ASM_RASP_XXE(25), + ASM_RASP_RCE(26), + ASM_RASP_NOSQLI(27), + ASM_RASP_XSS(28), + APM_TRACING_SAMPLE_RULES(29), + CSM_ACTIVATION(30) + + final int ordinal + + Capability(int ordinal) { + this.ordinal = ordinal + } + + static EnumSet forByteArray(byte[] arr) { + def capabilities = EnumSet.noneOf(Capability) + def bi = new BigInteger(arr) + for (Capability c: values()) { + if (bi.testBit(c.ordinal)) { + capabilities << c + } + } + capabilities + } +} diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java index f2db7ed5c1..2481f2517a 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigRequest.java @@ -55,6 +55,13 @@ public ClientInfo getClient() { return this.client; } + public Target extractTarget() { + return Target.create( + client.tracerInfo.serviceName, + client.tracerInfo.serviceEnv, + client.tracerInfo.serviceVersion); + } + /** Stores client information for Remote Configuration */ public static class ClientInfo { @JsonProperty("state") @@ -75,8 +82,17 @@ public static class ClientInfo { @JsonProperty("is_agent") public Boolean isAgent = null; // MUST NOT be set; + @JsonProperty("last_seen") + public long lastSeen; + public byte[] capabilities; + @JsonProperty("is_updater") + public Boolean isUpdater = null; // MUST NOT be set; + + @JsonProperty("client_updater") + public Map clientUpdater; + public ClientInfo() {} public ClientInfo( ClientState clientState, @@ -118,13 +134,13 @@ public static class ClientState { public String error; @JsonProperty("backend_client_state") - public String backendClientState; + public byte[] backendClientState; public void setState( long targetsVersion, List configStates, String error, - String backendClientState) { + byte[] backendClientState) { this.targetsVersion = targetsVersion; this.configStates = configStates; this.error = error; @@ -136,8 +152,8 @@ public static class ConfigState { public static final int APPLY_STATE_ACKNOWLEDGED = 2; public static final int APPLY_STATE_ERROR = 3; - private String id; - private long version; + public String id; + public long version; public String product; @JsonProperty("apply_state") @@ -281,9 +297,9 @@ public boolean hashesMatch(Map hashesMap) { return true; } - public class TargetFileHash { - String algorithm; - String hash; + static public class TargetFileHash { + public String algorithm; + public String hash; TargetFileHash() {} TargetFileHash(String algorithm, String hash) { diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java index 0c77f63dc8..4ce3f5e6b2 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/RemoteConfigResponse.java @@ -1,12 +1,17 @@ package com.datadog.appsec.php.mock_agent.rem_cfg; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.io.IOException; +import java.io.StringWriter; import java.lang.reflect.UndeclaredThrowableException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -23,7 +28,8 @@ public class RemoteConfigResponse { public List clientConfigs; @JsonDeserialize(using = TargetsDeserializer.class) - private Targets targets; + @JsonSerialize(using = TargetsSerializer.class) + public Targets targets; @JsonProperty("target_files") public List targetFiles; @@ -103,7 +109,7 @@ public byte[] getFileContents(String configKey) { throw new MissingContentException("No content for " + configKey); } - private static BigInteger sha256(byte[] bytes) { + public static BigInteger sha256(byte[] bytes) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(bytes); @@ -136,6 +142,8 @@ public static class TargetsSigned { public String type; public TargetsCustom custom; + + @JsonSerialize(using=InstantSerializer.class) public Instant expires; @JsonProperty("spec_version") @@ -145,6 +153,9 @@ public static class TargetsSigned { public Map targets; public static class TargetsCustom { + @JsonProperty("agent_request_interval") + public long agentRequestInterval; + @JsonProperty("opaque_backend_state") public String opaqueBackendState; } @@ -183,4 +194,27 @@ public Targets deserialize(JsonParser jsonParser, DeserializationContext deseria return (Targets) defaultDeserializer.deserialize(defParser, deserializationContext); } } + + public static class TargetsSerializer extends JsonSerializer { + @Override + public void serialize(Targets value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + JsonSerializer defaultSerializer = serializers.findValueSerializer(Targets.class); + + StringWriter sw = new StringWriter(); + JsonGenerator defGen = gen.getCodec().getFactory().createGenerator(sw); + defaultSerializer.serialize(value, defGen, serializers); + defGen.flush(); + + byte[] base64 = Base64.getEncoder().encode(sw.toString().getBytes(StandardCharsets.UTF_8)); + + gen.writeString(new String(base64, StandardCharsets.ISO_8859_1)); + } + } + + public static class InstantSerializer extends JsonSerializer { + @Override + public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(value.toString()); + } + } } diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Target.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Target.groovy new file mode 100644 index 0000000000..92ecb4ad94 --- /dev/null +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/mock_agent/rem_cfg/Target.groovy @@ -0,0 +1,14 @@ +package com.datadog.appsec.php.mock_agent.rem_cfg + +import groovy.transform.Immutable + +@Immutable +class Target { + String service + String env + String appVersion + + static Target create(String service, String env, String appVersion) { + new Target(service, env, appVersion) + } +} diff --git a/appsec/tests/integration/src/test/bin/enable_extensions.sh b/appsec/tests/integration/src/test/bin/enable_extensions.sh index 7363a31907..3d91dadc5f 100755 --- a/appsec/tests/integration/src/test/bin/enable_extensions.sh +++ b/appsec/tests/integration/src/test/bin/enable_extensions.sh @@ -24,6 +24,7 @@ if [[ -f /appsec/ddappsec.so && -d /project ]]; then echo datadog.appsec.helper_path=/appsec/libddappsec-helper.so echo datadog.appsec.helper_log_file=/tmp/logs/helper.log echo datadog.appsec.helper_log_level=info +# echo datadog.appsec.helper_log_level=debug echo datadog.appsec.rules=/etc/recommended.json echo datadog.appsec.log_file=/tmp/logs/appsec.log echo datadog.appsec.log_level=debug diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy index 2f0a815046..84ca54e07a 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy @@ -392,7 +392,7 @@ trait CommonTests { void 'module does not have STATIC_TLS flag'() { Container.ExecResult res = container.execInContainer( 'bash', '-c', - '''! { readelf -d "$(php -r 'echo ini_get("extension_dir");')"/ddappsec.so | grep STATIC_TLS; }''') + '''! { readelf -d "$(DD_TRACE_CLI_ENABLED=0 php -r 'echo ini_get("extension_dir");')"/ddappsec.so | grep STATIC_TLS; }''') if (res.exitCode != 0) { throw new AssertionError("Module has STATIC_TLS flag: $res.stdout") } diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/NginxFpmTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/NginxFpmTests.groovy index f679392c45..25ec33cdd4 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/NginxFpmTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/NginxFpmTests.groovy @@ -12,9 +12,7 @@ import org.testcontainers.junit.jupiter.Testcontainers import java.net.http.HttpResponse import static com.datadog.appsec.php.integration.TestParams.getPhpVersion -import static com.datadog.appsec.php.integration.TestParams.getTracerVersion import static com.datadog.appsec.php.integration.TestParams.getVariant -import static org.testcontainers.containers.Container.ExecResult @Testcontainers @Slf4j diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RemoteConfigTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RemoteConfigTests.groovy new file mode 100644 index 0000000000..b06027dca1 --- /dev/null +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RemoteConfigTests.groovy @@ -0,0 +1,404 @@ +package com.datadog.appsec.php.integration + +import com.datadog.appsec.php.docker.AppSecContainer +import com.datadog.appsec.php.docker.FailOnUnmatchedTraces +import com.datadog.appsec.php.mock_agent.rem_cfg.Capability +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigRequest +import com.datadog.appsec.php.mock_agent.rem_cfg.RemoteConfigResponse +import com.datadog.appsec.php.mock_agent.rem_cfg.Target +import groovy.json.JsonOutput +import groovy.util.logging.Slf4j +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.DisabledIf +import org.testcontainers.junit.jupiter.Container +import org.testcontainers.junit.jupiter.Testcontainers + +import java.net.http.HttpRequest +import java.net.http.HttpResponse +import java.nio.charset.StandardCharsets +import java.time.Instant + +import static com.datadog.appsec.php.integration.TestParams.getPhpVersion +import static com.datadog.appsec.php.integration.TestParams.getVariant +import static java.net.http.HttpResponse.BodyHandlers.ofString +import static org.junit.jupiter.api.Assumptions.assumeTrue +import static org.testcontainers.containers.Container.ExecResult + +@Testcontainers +@Slf4j +@DisabledIf('isDisabled') +class RemoteConfigTests { + static boolean disabled = variant.contains('zts') || phpVersion != '8.3' + + private static final Target INITIAL_TARGET = new Target('some-name', 'none', '') + + @Container + @FailOnUnmatchedTraces + public static final AppSecContainer CONTAINER = + new AppSecContainer( + workVolume: this.name, + baseTag: 'nginx-fpm-php', + phpVersion: phpVersion, + phpVariant: variant, + www: 'base', + ) + + @BeforeAll + static void beforeAll() { + ExecResult res = CONTAINER.execInContainer( + 'bash', '-c', + '''sed -e '/appsec.enabled/d' -e '/appsec.rules=/d' /etc/php/php.ini > /etc/php/php-rc.ini; + kill -9 `pgrep php-fpm`; + export _DD_DEBUG_SIDECAR_RC_POLL_INTERVAL_MILLIS=1000 DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS=1 DD_ENV=; + php-fpm -y /etc/php-fpm.conf -c /etc/php/php-rc.ini''') + assert res.exitCode == 0 + } + + @Test + void 'test remote activation and capabilities'() { + def doReq = { int expectedStatus -> + HttpRequest req = CONTAINER.buildReq('/hello.php') + .GET() + .header('User-agent', 'dd-test-scanner-log-block') + .build() + CONTAINER.traceFromRequest(req, ofString()) { HttpResponse resp -> + assert resp.statusCode() == expectedStatus + } + } + + doReq.call(200) + + RemoteConfigRequest rcr = applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [ + asm: [enabled: true] + ] + ]) + + def capSet = Capability.forByteArray(rcr.client.capabilities) + + [ + Capability.ASM_ACTIVATION, + Capability.ASM_IP_BLOCKING, + Capability.ASM_DD_RULES, + Capability.ASM_EXCLUSIONS, + Capability.ASM_REQUEST_BLOCKING, + Capability.ASM_RESPONSE_BLOCKING, + Capability.ASM_USER_BLOCKING, + Capability.ASM_CUSTOM_RULES, + Capability.ASM_CUSTOM_BLOCKING_RESPONSE, + Capability.ASM_TRUSTED_IPS, + ].each { assert it in capSet } + + doReq.call(403) + + dropRemoteConfig(INITIAL_TARGET) + + doReq.call(200) + } + + @Test + void 'test asm_data'() { + def doReq = { String ip, int expectedStatus -> + HttpRequest req = CONTAINER.buildReq('/hello.php') + .GET() + .header('X-Real-Ip', ip) + .build() + CONTAINER.traceFromRequest(req, ofString()) { HttpResponse resp -> + assert resp.statusCode() == expectedStatus + } + } + + doReq.call('1.2.3.4', 200) + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: true]], + 'datadog/2/ASM_DATA/mydata/config': [ + rules_data: [ + [ + id: 'blocked_ips', + type: 'ip_with_expiration', + data: [ + [ + expiration: 0, + value: '1.2.3.0/24' + ] + ] + ] + ] + + ] + ]) + + doReq.call('1.2.3.4', 403) + doReq.call('1.2.4.1', 200) + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: true]]]) + + doReq.call('1.2.3.4', 200) + + dropRemoteConfig(INITIAL_TARGET) + } + + @Test + void 'test asm_dd'() { + def doReq = { int expectedStatus -> + HttpRequest req = CONTAINER.buildReq('/hello.php?a=matched+value').GET().build() + CONTAINER.traceFromRequest(req, ofString()) { HttpResponse resp -> + assert resp.statusCode() == expectedStatus + } + } + + doReq.call(200) + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: true]], + 'employee/ASM_DD/full_cfg/config': + [ + version: '2.1', + rules: [[ + id: 'partial_match_values', + name: 'Partially match values', + tags: [ + type: 'security_scanner', + category: 'attack_attempt' + ], + conditions: [[ + parameters: [ + inputs: [[ address: 'server.request.query' ]], + regex: '.*matched.+value.*' + ], + operator: 'match_regex' + ]], + transformers: ['values_only'], + on_match: ['block'] + ]] + ] + ]) + + doReq.call(403) + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: true]]]) + + doReq.call(200) + + dropRemoteConfig(INITIAL_TARGET) + } + + @Test + void 'test asm'() { + def doReq = { String path, int expectedStatus, Map headers = [:] -> + def br = CONTAINER.buildReq(path).GET() + headers.each { k, v -> br.header(k, v) } + HttpRequest req = br.build() + CONTAINER.traceFromRequest(req, ofString()) { HttpResponse resp -> + assert resp.statusCode() == expectedStatus + } + } + + doReq.call('/hello.php', 200) + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: true]], + 'datadog/2/ASM/custom_user_cfg_2/config': [ + actions: [[ + id: 'block_custom', + type: 'block_request', + parameters: [ + status_code: '408' + ] + ]] + ], + 'datadog/2/ASM/custom_user_cfg_1/config': [ + custom_rules: [[ + id: 'partial_match_values', + name: 'Partially match values', + tags: [ + type: 'security_scanner', + category: 'attack_attempt' + ], + conditions: [[ + parameters: [ + inputs: [[ + address: 'server.request.query' + ]], + regex: '.*matched.+value.*' + ], + operator: 'match_regex' + ]], + transformers: ['values_only'], + on_match: ['block_custom'] + + ]], + exclusions: [[ + id: 'excl1', + rules_target: [[ + rule_id: 'ua0-600-56x' + ]], + conditions: [[ + operator: 'match_regex', + parameters: [ + inputs: [[ + address: 'server.request.query' + ]], + regex: 'excluded' + ] + ]] + ]], + rules_override: [[ + rules_target: [[ + rule_id: 'ua0-600-56x' + ]], + on_match: ['block_custom2'], + enabled: true + ]], + actions: [ + [ + id: 'block_custom2', + type: 'block_request', + parameters: [ + status_code: '410' + ] + ] + ] + ] + ]) + + // matches user rule 'partial_match_values' + doReq.call('/hello.php?a=matched+value1', 408) + + // matches exclusion rule 'excl1' + doReq.call('/hello.php?b=excluded', 200, ['User-agent': 'dd-test-scanner-log-block']) + + // action is overridden in rules_override to block_custom2 (code: 410) + doReq.call('/hello.php', 410, ['User-agent': 'dd-test-scanner-log-block']) + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': null /* keep */, + 'datadog/2/ASM/custom_user_cfg_1/config': null /* keep */, + 'datadog/2/ASM/custom_user_cfg_2/config': [ + actions: [[ + id: 'block_custom', + type: 'block_request', + parameters: [ + status_code: '409' + ] + ]] + ], + ]) + + // status code changed to 409 + doReq.call('/hello.php?a=matched+value1', 409) + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: true]]]) + + doReq.call('/hello.php?a=matched+value1', 200) + doReq.call('/hello.php?b=excluded', 403, ['User-agent': 'dd-test-scanner-log-block']) + doReq.call('/hello.php', 403, ['User-agent': 'dd-test-scanner-log-block']) + + dropRemoteConfig(INITIAL_TARGET) + } + + @Test + void 'test env change'() { + Target newTarget = new Target('some-name', 'another-env', '') + + def doReq = { Integer expectedStatus, String path, Map headers = [:] -> + def br = CONTAINER.buildReq(path).GET() + headers.each { k, v -> br.header(k, v) } + HttpRequest req = br.build() + def gottenStatus = null + CONTAINER.traceFromRequest(req, ofString()) { HttpResponse resp -> + gottenStatus = resp.statusCode() + if (expectedStatus) { + assert gottenStatus == expectedStatus + } + } + gottenStatus + } + + doReq.call(200, '/hello.php') + + applyRemoteConfig(INITIAL_TARGET, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: false]]]) + + doReq.call(200, '/hello.php', ['User-agent': 'dd-test-scanner-log-block']) + + // changes env at the end of the request. The new rem cfg path is not transmitted + // to helper because appsec transmit rc path on req init + doReq.call(200, '/change_env.php?env=another-env') + + // new rem cfg path is transmitted to the helper on config_sync + doReq.call(200, '/change_env.php?env=another-env') + + applyRemoteConfig(newTarget, [ + 'datadog/2/ASM_FEATURES/asm_features_activation/config': [asm: [enabled: true]]]) + + def status = doReq.call(null, '/hello.php', ['User-agent': 'dd-test-scanner-log-block']) + if (status == 200) { + assumeTrue(false, "Test fails because env of rc client is reset on ddtrace_sidecar_rinit(), " + + "which runs before appsec rinit") + } + assert status == 403 + + dropRemoteConfig(INITIAL_TARGET) + dropRemoteConfig(newTarget) + } + + private RemoteConfigRequest applyRemoteConfig(Target target, Map files) { + Map encodedFiles = files + .findAll { it.value != null } + .collectEntries { + [ + it.key, + JsonOutput.toJson(it.value).getBytes(StandardCharsets.UTF_8) + ] + } + long newVersion = Instant.now().epochSecond + def rcr = new RemoteConfigResponse() + rcr.clientConfigs = files.keySet() as List + rcr.targetFiles = encodedFiles.collect { + new RemoteConfigResponse.TargetFile( + path: it.key, + raw: new String( + Base64.encoder.encode(it.value), + StandardCharsets.ISO_8859_1) + ) + } + rcr.targets = new RemoteConfigResponse.Targets( + signatures: [], + targetsSigned: new RemoteConfigResponse.Targets.TargetsSigned( + type: 'root', + custom: new RemoteConfigResponse.Targets.TargetsSigned.TargetsCustom( + opaqueBackendState: 'ABCDEF' + ), + specVersion:'1.0.0', + expires: Instant.parse('2030-01-01T00:00:00Z'), + version: newVersion, + targets: encodedFiles.collectEntries { + [ + it.key, + new RemoteConfigResponse.Targets.ConfigTarget( + hashes: [sha256: RemoteConfigResponse.sha256(it.value).toString(16).padLeft(64, '0')], + length: it.value.size(), + custom: new RemoteConfigResponse.Targets.ConfigTarget.ConfigTargetCustom( + version: newVersion + ) + ) + ] + } + ), + ) + + CONTAINER.setNextRCResponse(target, rcr) + CONTAINER.waitForRCVersion(target, newVersion, 5_000) + } + + RemoteConfigRequest dropRemoteConfig(Target target) { + applyRemoteConfig(target, [:]) + } + +} diff --git a/appsec/tests/integration/src/test/www/base/public/change_env.php b/appsec/tests/integration/src/test/www/base/public/change_env.php new file mode 100644 index 0000000000..129a83b2fd --- /dev/null +++ b/appsec/tests/integration/src/test/www/base/public/change_env.php @@ -0,0 +1,7 @@ +env = $_GET['env']; + +var_dump($root_span); diff --git a/appsec/tests/integration/src/test/www/laravel8x/initialize.sh b/appsec/tests/integration/src/test/www/laravel8x/initialize.sh index 778669352a..327e27be49 100755 --- a/appsec/tests/integration/src/test/www/laravel8x/initialize.sh +++ b/appsec/tests/integration/src/test/www/laravel8x/initialize.sh @@ -2,6 +2,8 @@ cd /var/www +export DD_TRACE_CLI_ENABLED=false + composer install --no-dev chown -R www-data.www-data vendor diff --git a/appsec/tests/integration/src/test/www/symfony62/initialize.sh b/appsec/tests/integration/src/test/www/symfony62/initialize.sh index 59254e3bc2..6e66797719 100755 --- a/appsec/tests/integration/src/test/www/symfony62/initialize.sh +++ b/appsec/tests/integration/src/test/www/symfony62/initialize.sh @@ -2,6 +2,8 @@ cd /var/www +export DD_TRACE_CLI_ENABLED=false + composer install php bin/console doctrine:database:drop --force php bin/console doctrine:database:create diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index 776c13459f..8bef6a8387 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -165,10 +165,12 @@ void ddog_reset_logger(void); uint32_t ddog_get_logs_count(ddog_CharSlice level); -void ddog_init_remote_config(bool live_debugging_enabled); +void ddog_init_remote_config(bool live_debugging_enabled, bool appsec_features, bool appsec_config); struct ddog_RemoteConfigState *ddog_init_remote_config_state(const struct ddog_Endpoint *endpoint); +const char *ddog_remote_config_get_path(const struct ddog_RemoteConfigState *remote_config); + void ddog_process_remote_configs(struct ddog_RemoteConfigState *remote_config); bool ddog_type_can_be_instrumented(const struct ddog_RemoteConfigState *remote_config, diff --git a/components-rs/remote_config.rs b/components-rs/remote_config.rs index 6bddeb4681..7952c37353 100644 --- a/components-rs/remote_config.rs +++ b/components-rs/remote_config.rs @@ -1,31 +1,39 @@ -use std::borrow::Cow; +use crate::sidecar::MaybeShmLimiter; +use datadog_dynamic_configuration::{data::TracingSamplingRuleProvenance, Configs}; +use datadog_live_debugger::debugger_defs::{DebuggerData, DebuggerPayload}; use datadog_live_debugger::{FilterList, LiveDebuggingData, ServiceConfiguration}; -use datadog_live_debugger_ffi::data::{Probe}; +use datadog_live_debugger_ffi::data::Probe; use datadog_live_debugger_ffi::evaluator::{ddog_register_expr_evaluator, Evaluator}; -use datadog_dynamic_configuration::{Configs, data::TracingSamplingRuleProvenance}; -use datadog_remote_config::{RemoteConfigCapabilities, RemoteConfigData, RemoteConfigProduct, Target}; +use datadog_live_debugger_ffi::send_data::{ + ddog_debugger_diagnostics_create_unboxed, ddog_snapshot_redacted_type, +}; use datadog_remote_config::fetch::ConfigInvariants; +use datadog_remote_config::{ + RemoteConfigCapabilities, RemoteConfigData, RemoteConfigProduct, Target, +}; +use datadog_sidecar::service::blocking::SidecarTransport; +use datadog_sidecar::service::{InstanceId, QueueId}; use datadog_sidecar::shm_remote_config::{RemoteConfigManager, RemoteConfigUpdate}; +use datadog_sidecar_ffi::ddog_sidecar_send_debugger_data; use ddcommon::Endpoint; use ddcommon_ffi::slice::AsBytes; use ddcommon_ffi::{CharSlice, MaybeError}; use itertools::Itertools; -use std::collections::{HashMap, HashSet}; +use regex_automata::dfa::regex::Regex; +use serde::Serialize; +use std::borrow::Cow; use std::collections::hash_map::Entry; +use std::collections::{HashMap, HashSet}; use std::ffi::c_char; use std::mem; use std::sync::Arc; -use serde::Serialize; -use crate::sidecar::MaybeShmLimiter; -use regex_automata::dfa::regex::Regex; use tracing::debug; -use datadog_live_debugger::debugger_defs::{DebuggerData, DebuggerPayload}; -use datadog_live_debugger_ffi::send_data::{ddog_debugger_diagnostics_create_unboxed, ddog_snapshot_redacted_type}; -use datadog_sidecar::service::blocking::SidecarTransport; -use datadog_sidecar::service::{InstanceId, QueueId}; -use datadog_sidecar_ffi::ddog_sidecar_send_debugger_data; -type DynamicConfigUpdate = for <'a> extern "C" fn(config: CharSlice, value: CharSlice, return_old: bool) -> *mut Vec; +type DynamicConfigUpdate = for<'a> extern "C" fn( + config: CharSlice, + value: CharSlice, + return_old: bool, +) -> *mut Vec; static mut LIVE_DEBUGGER_CALLBACKS: Option = None; static mut DYNAMIC_CONFIG_UPDATE: Option = None; @@ -36,7 +44,8 @@ pub static mut DDTRACE_REMOTE_CONFIG_PRODUCTS: VecRemoteConfigProduct = ddcommon type VecRemoteConfigCapabilities = ddcommon_ffi::Vec; #[no_mangle] -pub static mut DDTRACE_REMOTE_CONFIG_CAPABILITIES: VecRemoteConfigCapabilities = ddcommon_ffi::Vec::new(); +pub static mut DDTRACE_REMOTE_CONFIG_CAPABILITIES: VecRemoteConfigCapabilities = + ddcommon_ffi::Vec::new(); #[derive(Default)] struct DynamicConfig { @@ -74,8 +83,11 @@ pub struct LiveDebuggerState { } #[no_mangle] -pub unsafe extern "C" fn ddog_init_remote_config(live_debugging_enabled: bool) { - +pub unsafe extern "C" fn ddog_init_remote_config( + live_debugging_enabled: bool, + appsec_features: bool, + appsec_config: bool, +) { DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::ApmTracing); DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingCustomTags); DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::ApmTracingEnabled); @@ -87,6 +99,30 @@ pub unsafe extern "C" fn ddog_init_remote_config(live_debugging_enabled: bool) { if live_debugging_enabled { DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::LiveDebugger) } + + if appsec_features { + DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::AsmFeatures); + DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(RemoteConfigCapabilities::AsmActivation); + } + + if appsec_config { + DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::AsmData); + DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::AsmDD); + DDTRACE_REMOTE_CONFIG_PRODUCTS.push(RemoteConfigProduct::Asm); + [ + RemoteConfigCapabilities::AsmIpBlocking, + RemoteConfigCapabilities::AsmDdRules, + RemoteConfigCapabilities::AsmExclusions, + RemoteConfigCapabilities::AsmRequestBlocking, + RemoteConfigCapabilities::AsmResponseBlocking, + RemoteConfigCapabilities::AsmUserBlocking, + RemoteConfigCapabilities::AsmCustomRules, + RemoteConfigCapabilities::AsmCustomBlockingResponse, + RemoteConfigCapabilities::AsmTrustedIps, + ] + .iter() + .for_each(|c| DDTRACE_REMOTE_CONFIG_CAPABILITIES.push(*c)); + } } // Per-thread state @@ -122,31 +158,40 @@ struct SampleRule<'a> { fn map_config(config: &Configs) -> (&'static str, String) { match config { - Configs::TracingHeaderTags(tags) => { - ("datadog.trace.header_tags", tags.iter().map(|(k, _)| k).join(",")) - } - Configs::TracingSampleRate(rate) => { - ("datadog.trace.sample_rate", rate.to_string()) - } - Configs::LogInjectionEnabled(enabled) => { - ("datadog.logs_injection", (if *enabled { "1" } else { "0" }).to_string()) - } - Configs::TracingTags(tags) => { - ("datadog.tags", tags.join(",")) - } - Configs::TracingEnabled(enabled) => { - ("datadog.trace.enabled", (if *enabled { "1" } else { "0" }).to_string()) - } + Configs::TracingHeaderTags(tags) => ( + "datadog.trace.header_tags", + tags.iter().map(|(k, _)| k).join(","), + ), + Configs::TracingSampleRate(rate) => ("datadog.trace.sample_rate", rate.to_string()), + Configs::LogInjectionEnabled(enabled) => ( + "datadog.logs_injection", + (if *enabled { "1" } else { "0" }).to_string(), + ), + Configs::TracingTags(tags) => ("datadog.tags", tags.join(",")), + Configs::TracingEnabled(enabled) => ( + "datadog.trace.enabled", + (if *enabled { "1" } else { "0" }).to_string(), + ), Configs::TracingSamplingRules(rules) => { - let map: Vec<_> = rules.iter().map(|r| SampleRule { - name: r.name.as_deref(), - service: r.service.as_str(), - resource: r.resource.as_str(), - tags: r.tags.iter().map(|t| (t.key.as_str(), t.value_glob.as_str())).collect(), - provenance: r.provenance, - sample_rate: r.sample_rate, - }).collect(); - ("datadog.trace.sampling_rules", serde_json::to_string(&map).unwrap()) + let map: Vec<_> = rules + .iter() + .map(|r| SampleRule { + name: r.name.as_deref(), + service: r.service.as_str(), + resource: r.resource.as_str(), + tags: r + .tags + .iter() + .map(|t| (t.key.as_str(), t.value_glob.as_str())) + .collect(), + provenance: r.provenance, + sample_rate: r.sample_rate, + }) + .collect(); + ( + "datadog.trace.sampling_rules", + serde_json::to_string(&map).unwrap(), + ) } } } @@ -159,12 +204,17 @@ fn remove_old_configs(remote_config: &mut RemoteConfigState) { remote_config.dynamic_config.active_config_path = None; } -fn insert_new_configs(old_config_values: &mut HashMap>, old_configs: &mut Vec, new_configs: Vec) { +fn insert_new_configs( + old_config_values: &mut HashMap>, + old_configs: &mut Vec, + new_configs: Vec, +) { let mut found_configs = HashSet::new(); for config in new_configs.iter() { let (name, val) = map_config(config); let is_update = old_config_values.contains_key(name); - let original = unsafe { DYNAMIC_CONFIG_UPDATE }.unwrap()(name.into(), val.as_str().into(), !is_update); + let original = + unsafe { DYNAMIC_CONFIG_UPDATE }.unwrap()(name.into(), val.as_str().into(), !is_update); if !original.is_null() { old_config_values.insert(name.into(), *unsafe { Box::from_raw(original) }); } @@ -181,12 +231,25 @@ fn insert_new_configs(old_config_values: &mut HashMap>, old_ *old_configs = new_configs; } +#[no_mangle] +pub extern "C" fn ddog_remote_config_get_path(remote_config: &RemoteConfigState) -> *const c_char { + remote_config + .manager + .active_reader + .as_ref() + .map(|r| r.get_path().as_ptr()) + .unwrap_or(std::ptr::null()) +} + #[no_mangle] pub extern "C" fn ddog_process_remote_configs(remote_config: &mut RemoteConfigState) { loop { match remote_config.manager.fetch_update() { RemoteConfigUpdate::None => break, - RemoteConfigUpdate::Add { value, limiter_index } => match value.data { + RemoteConfigUpdate::Add { + value, + limiter_index, + } => match value.data { RemoteConfigData::LiveDebugger(debugger) => { let val = Box::new((debugger, MaybeShmLimiter::open(limiter_index))); let rc_ref = unsafe { mem::transmute(remote_config as *mut _) }; // sigh, borrow checker @@ -196,39 +259,46 @@ pub extern "C" fn ddog_process_remote_configs(remote_config: &mut RemoteConfigSt e.insert(val); e.into_mut() } - Entry::Vacant(e) => { - e.insert(val) - } + Entry::Vacant(e) => e.insert(val), }; apply_config(rc_ref, debugger, limiter); - }, + } RemoteConfigData::DynamicConfig(config_data) => { let configs: Vec = config_data.lib_config.into(); if !configs.is_empty() { - insert_new_configs(&mut remote_config.dynamic_config.old_config_values, &mut remote_config.dynamic_config.configs, configs); + insert_new_configs( + &mut remote_config.dynamic_config.old_config_values, + &mut remote_config.dynamic_config.configs, + configs, + ); remote_config.dynamic_config.active_config_path = Some(value.config_id); } - }, - RemoteConfigData::Ignored(_) => {} + } + RemoteConfigData::Ignored(_) => (), }, RemoteConfigUpdate::Remove(path) => match path.product { RemoteConfigProduct::LiveDebugger => { - if let Some(boxed) = remote_config.live_debugger.active.remove(&path.config_id) { + if let Some(boxed) = remote_config.live_debugger.active.remove(&path.config_id) + { remove_config(remote_config, &boxed.0); } - }, + } RemoteConfigProduct::ApmTracing => { if Some(path.config_id) == remote_config.dynamic_config.active_config_path { remove_old_configs(remote_config); } - }, - _ => {}, + } + _ => (), }, } } } -fn apply_config(remote_config: &mut RemoteConfigState, debugger: &LiveDebuggingData, limiter: &MaybeShmLimiter) { +fn apply_config( + remote_config: &mut RemoteConfigState, + debugger: &LiveDebuggingData, + limiter: &MaybeShmLimiter, +) { if let Some(callbacks) = unsafe { &LIVE_DEBUGGER_CALLBACKS } { match debugger { LiveDebuggingData::Probe(probe) => { @@ -240,7 +310,7 @@ fn apply_config(remote_config: &mut RemoteConfigState, debugger: &LiveDebuggingD .spans_map .insert(probe.id.clone(), hook_id); } - }, + } LiveDebuggingData::ServiceConfiguration(config) => { debug!("Applying live debugger service config {config:?}"); fn build_regex(list: &FilterList) -> Option { @@ -280,7 +350,7 @@ fn remove_config(remote_config: &mut RemoteConfigState, debugger: &LiveDebugging debug!("Removing live debugger probe {}", probe.id); (callbacks.remove_probe)(id); } - }, + } LiveDebuggingData::ServiceConfiguration(ServiceConfiguration { id, .. }) => { // There can only be one active service configuration, but I don't want to rely on the order of adding and removing service configurations if id == &remote_config.live_debugger.config_id { @@ -294,7 +364,10 @@ fn remove_config(remote_config: &mut RemoteConfigState, debugger: &LiveDebugging } #[no_mangle] -pub extern "C" fn ddog_type_can_be_instrumented(remote_config: &RemoteConfigState, typename: CharSlice) -> bool { +pub extern "C" fn ddog_type_can_be_instrumented( + remote_config: &RemoteConfigState, + typename: CharSlice, +) -> bool { if ddog_snapshot_redacted_type(typename) { return false; } @@ -316,7 +389,11 @@ pub extern "C" fn ddog_type_can_be_instrumented(remote_config: &RemoteConfigStat #[no_mangle] pub extern "C" fn ddog_global_log_probe_limiter_inc(remote_config: &RemoteConfigState) -> bool { - if let Some(boxed) = remote_config.live_debugger.active.get(&remote_config.live_debugger.config_id) { + if let Some(boxed) = remote_config + .live_debugger + .active + .get(&remote_config.live_debugger.config_id) + { if let (LiveDebuggingData::ServiceConfiguration(config), limiter) = &**boxed { limiter.inc(config.sampling_snapshots_per_second) } else { @@ -333,7 +410,12 @@ pub unsafe extern "C" fn ddog_CharSlice_to_owned(str: CharSlice) -> *mut Vec bool { +pub extern "C" fn ddog_remote_configs_service_env_change( + remote_config: &mut RemoteConfigState, + service: CharSlice, + env: CharSlice, + version: CharSlice, +) -> bool { let new_target = Target { service: service.to_utf8_lossy().to_string(), env: env.to_utf8_lossy().to_string(), @@ -348,13 +430,21 @@ pub extern "C" fn ddog_remote_configs_service_env_change(remote_config: &mut Rem remote_config.manager.track_target(&Arc::new(new_target)); ddog_process_remote_configs(remote_config); - + true } #[no_mangle] -pub unsafe extern "C" fn ddog_remote_config_alter_dynamic_config(remote_config: &mut RemoteConfigState, config: CharSlice, new_value: CharSlice) -> bool { - if let Some(entry) = remote_config.dynamic_config.old_config_values.get_mut(config.try_to_utf8().unwrap()) { +pub unsafe extern "C" fn ddog_remote_config_alter_dynamic_config( + remote_config: &mut RemoteConfigState, + config: CharSlice, + new_value: CharSlice, +) -> bool { + if let Some(entry) = remote_config + .dynamic_config + .old_config_values + .get_mut(config.try_to_utf8().unwrap()) + { *entry = new_value.as_slice().into(); return false; } @@ -362,7 +452,10 @@ pub unsafe extern "C" fn ddog_remote_config_alter_dynamic_config(remote_config: } #[no_mangle] -pub unsafe extern "C" fn ddog_setup_remote_config(update_config: DynamicConfigUpdate, setup: &LiveDebuggerSetup) { +pub unsafe extern "C" fn ddog_setup_remote_config( + update_config: DynamicConfigUpdate, + setup: &LiveDebuggerSetup, +) { ddog_register_expr_evaluator(setup.evaluator); DYNAMIC_CONFIG_UPDATE = Some(update_config); LIVE_DEBUGGER_CALLBACKS = Some(setup.callbacks.clone()); @@ -377,7 +470,10 @@ pub extern "C" fn ddog_rinit_remote_config(remote_config: &mut RemoteConfigState pub extern "C" fn ddog_rshutdown_remote_config(remote_config: &mut RemoteConfigState) { remote_config.live_debugger.spans_map.clear(); remote_config.dynamic_config.old_config_values.clear(); - remote_config.manager.unload_configs(&[RemoteConfigProduct::ApmTracing, RemoteConfigProduct::LiveDebugger]); + remote_config.manager.unload_configs(&[ + RemoteConfigProduct::ApmTracing, + RemoteConfigProduct::LiveDebugger, + ]); } #[no_mangle] @@ -386,21 +482,51 @@ pub extern "C" fn ddog_shutdown_remote_config(_: Box) {} #[no_mangle] pub extern "C" fn ddog_log_debugger_data(payloads: &Vec) { if !payloads.is_empty() { - debug!("Submitting debugger data: {}", serde_json::to_string(payloads).unwrap()); + debug!( + "Submitting debugger data: {}", + serde_json::to_string(payloads).unwrap() + ); } } #[no_mangle] pub extern "C" fn ddog_log_debugger_datum(payload: &DebuggerPayload) { - debug!("Submitting debugger data: {}", serde_json::to_string(payload).unwrap()); + debug!( + "Submitting debugger data: {}", + serde_json::to_string(payload).unwrap() + ); } #[no_mangle] -pub unsafe extern "C" fn ddog_send_debugger_diagnostics<'a>(remote_config_state: &RemoteConfigState, transport: &mut Box, instance_id: &InstanceId, queue_id: QueueId, probe: &'a Probe, timestamp: u64) -> MaybeError { - let service = Cow::Borrowed(remote_config_state.manager.get_target().map_or("", |t| t.service.as_str())); - let mut payload = ddog_debugger_diagnostics_create_unboxed(probe, service, Cow::Borrowed(&instance_id.runtime_id), timestamp); - let DebuggerData::Diagnostics(ref mut diagnostics) = payload.debugger else { unreachable!(); }; - diagnostics.parent_id = Some(Cow::Borrowed(remote_config_state.manager.current_runtime_id.as_str())); - debug!("Submitting debugger diagnostics data: {:?}", serde_json::to_string(&payload).unwrap()); +pub unsafe extern "C" fn ddog_send_debugger_diagnostics<'a>( + remote_config_state: &RemoteConfigState, + transport: &mut Box, + instance_id: &InstanceId, + queue_id: QueueId, + probe: &'a Probe, + timestamp: u64, +) -> MaybeError { + let service = Cow::Borrowed( + remote_config_state + .manager + .get_target() + .map_or("", |t| t.service.as_str()), + ); + let mut payload = ddog_debugger_diagnostics_create_unboxed( + probe, + service, + Cow::Borrowed(&instance_id.runtime_id), + timestamp, + ); + let DebuggerData::Diagnostics(ref mut diagnostics) = payload.debugger else { + unreachable!(); + }; + diagnostics.parent_id = Some(Cow::Borrowed( + remote_config_state.manager.current_runtime_id.as_str(), + )); + debug!( + "Submitting debugger diagnostics data: {:?}", + serde_json::to_string(&payload).unwrap() + ); ddog_sidecar_send_debugger_data(transport, instance_id, queue_id, vec![payload]) } diff --git a/ddtrace.sym b/ddtrace.sym index c3e558bafd..d20803d5d3 100644 --- a/ddtrace.sym +++ b/ddtrace.sym @@ -6,5 +6,12 @@ ddtrace_runtime_id ddtrace_user_req_add_listeners ddtrace_ip_extraction_find ddtrace_set_all_thread_vm_interrupt +ddtrace_remote_config_get_path +ddog_remote_config_reader_for_path +ddog_remote_config_read +ddog_remote_config_reader_drop get_module ddog_daemon_entry_point +ddog_set_rc_notify_fn +ddog_remote_config_path +ddog_remote_config_path_free diff --git a/dockerfiles/verify_packages/verify.sh b/dockerfiles/verify_packages/verify.sh index 480391119f..1e4195a0a8 100755 --- a/dockerfiles/verify_packages/verify.sh +++ b/dockerfiles/verify_packages/verify.sh @@ -2,6 +2,8 @@ set -e +export DD_REMOTE_CONFIG_ENABLED=false + # Installing generic dependencies. OS_ID='centos'|'debian'|'alpine' OS_ID=$(. /etc/os-release; echo $ID) sh $(pwd)/dockerfiles/verify_packages/${OS_ID}/install.sh diff --git a/ext/ddtrace.c b/ext/ddtrace.c index 4e8fcb3c35..9578861dbb 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -395,6 +395,8 @@ bool ddtrace_alter_dd_version(zval *old_value, zval *new_value, zend_string *new return dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_version), old_value, new_value, new_str); } +static zend_module_entry *dd_appsec_module() { return zend_hash_str_find_ptr(&module_registry, "ddappsec", sizeof("ddappsec") - 1); } + static void dd_activate_once(void) { ddtrace_config_first_rinit(); ddtrace_generate_runtime_id(); @@ -416,8 +418,8 @@ static void dd_activate_once(void) { bgs_service = ddtrace_default_service_name(); } } - zend_module_entry *appsec_module = zend_hash_str_find_ptr(&module_registry, "ddappsec", sizeof("ddappsec") - 1); - if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED() || get_global_DD_TRACE_SIDECAR_TRACE_SENDER() || appsec_module) + if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED() || get_global_DD_TRACE_SIDECAR_TRACE_SENDER() || + (dd_appsec_module() != NULL && !get_global_DD_APPSEC_TESTING())) #endif { bool request_startup = PG(during_request_startup); diff --git a/ext/remote_config.c b/ext/remote_config.c index 867281f432..a4291804bc 100644 --- a/ext/remote_config.c +++ b/ext/remote_config.c @@ -57,6 +57,13 @@ DDTRACE_PUBLIC void ddtrace_set_all_thread_vm_interrupt(void) { #endif } +DDTRACE_PUBLIC const char *ddtrace_remote_config_get_path() { + if (DDTRACE_G(remote_config_state)) { + return ddog_remote_config_get_path(DDTRACE_G(remote_config_state)); + } + return NULL; +} + #ifndef _WIN32 static void dd_sigvtalarm_handler(int signal, siginfo_t *siginfo, void *ctx) { UNUSED(signal, siginfo, ctx); diff --git a/ext/remote_config.h b/ext/remote_config.h index cac1c89d2c..9e27fc403f 100644 --- a/ext/remote_config.h +++ b/ext/remote_config.h @@ -9,5 +9,6 @@ void ddtrace_rinit_remote_config(void); void ddtrace_rshutdown_remote_config(void); DDTRACE_PUBLIC void ddtrace_set_all_thread_vm_interrupt(void); +DDTRACE_PUBLIC const char *ddtrace_remote_config_get_path(void); #endif diff --git a/ext/sidecar.c b/ext/sidecar.c index eed8124481..f3d5d6037e 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -98,7 +98,10 @@ ddog_SidecarTransport *dd_sidecar_connection_factory(void) { return sidecar_transport; } -static void maybe_enable_appsec() { +static void maybe_enable_appsec(bool *appsec_features, bool *appsec_config) { + *appsec_features = false; + *appsec_config = false; + // this must be done in ddtrace rather than ddappsec because // the sidecar is launched by ddtrace before ddappsec has a chance // to run its first rinit @@ -117,6 +120,14 @@ static void maybe_enable_appsec() { } void (*dd_appsec_maybe_enable_helper)(typeof(&ddog_sidecar_enable_appsec) enable_appsec) = handle; dd_appsec_maybe_enable_helper(ddog_sidecar_enable_appsec); + + typedef void (*dd_appsec_rc_conf_t)(bool *, bool *); + dd_appsec_rc_conf_t dd_appsec_rc_conf = dlsym(RTLD_DEFAULT, "dd_appsec_rc_conf"); + if (dd_appsec_rc_conf) { + dd_appsec_rc_conf(appsec_features, appsec_config); + } else { + LOG(WARN, "Could not resolve dd_appsec_rc_conf"); + } #endif } @@ -124,9 +135,11 @@ void ddtrace_sidecar_setup(void) { ddtrace_set_non_resettable_sidecar_globals(); ddtrace_set_resettable_sidecar_globals(); - maybe_enable_appsec(); + bool appsec_features; + bool appsec_config; + maybe_enable_appsec(&appsec_features, &appsec_config); - ddog_init_remote_config(get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()); + ddog_init_remote_config(get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED(), appsec_features, appsec_config); ddtrace_sidecar = dd_sidecar_connection_factory(); if (!ddtrace_sidecar && ddtrace_endpoint) { // Something went wrong diff --git a/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt b/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt index fe12ee4637..a5cc003137 100644 --- a/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt +++ b/tests/ext/autoload-php-files/error_get_last_is_unaffected.phpt @@ -4,6 +4,7 @@ Errors in ddtrace autoloader do not affect error_get_last() DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off DD_AUTOLOAD_NO_COMPILE=1 +DD_APPSEC_ENABLED=0 --INI-- error_reporting=E_ALL datadog.trace.sources_path="{PWD}/.." diff --git a/tests/ext/extension_no_static_tls.phpt b/tests/ext/extension_no_static_tls.phpt index d331e5acae..6ae89ca2fd 100644 --- a/tests/ext/extension_no_static_tls.phpt +++ b/tests/ext/extension_no_static_tls.phpt @@ -26,7 +26,7 @@ if (!file_exists('/proc/self/maps')) { die('skip: no /proc/self/maps'); } // 5. Determine in which compilation unit(s) this function is defined $maps = file_get_contents('/proc/self/maps'); -if (preg_match('@(?<=\\s)\\S*/ddtrace[^/]*\\.so$@m', $maps, $m) != 1) { +if (preg_match('@(?<=\\s)\\S*/ddtrace\\.so$@m', $maps, $m) != 1) { die('cannot find loaded ddtrace.so'); } diff --git a/tests/ext/live-debugger/exception-replay_001.phpt b/tests/ext/live-debugger/exception-replay_001.phpt index e8cdd472b7..604d8e376f 100644 --- a/tests/ext/live-debugger/exception-replay_001.phpt +++ b/tests/ext/live-debugger/exception-replay_001.phpt @@ -8,7 +8,7 @@ DD_AGENT_HOST=request-replayer DD_TRACE_AGENT_PORT=80 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_EXCEPTION_REPLAY_ENABLED=1 -DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 +DD_EXCEPTION_REPLAY_RATE_LIMIT_SECONDS=1 --INI-- datadog.trace.agent_test_session_token=live-debugger/exception-replay_001 --FILE-- diff --git a/tests/ext/live-debugger/exception-replay_002.phpt b/tests/ext/live-debugger/exception-replay_002.phpt index 1880a57a5b..ceec8ac97f 100644 --- a/tests/ext/live-debugger/exception-replay_002.phpt +++ b/tests/ext/live-debugger/exception-replay_002.phpt @@ -7,7 +7,7 @@ DD_AGENT_HOST=request-replayer DD_TRACE_AGENT_PORT=80 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_EXCEPTION_REPLAY_ENABLED=1 -DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 +DD_EXCEPTION_REPLAY_RATE_LIMIT_SECONDS=1 --INI-- datadog.trace.agent_test_session_token=live-debugger/exception-replay_002 --FILE-- diff --git a/tests/ext/sandbox-prehook/exceptions_and_errors_are_ignored_in_tracing_closure.phpt b/tests/ext/sandbox-prehook/exceptions_and_errors_are_ignored_in_tracing_closure.phpt index 61a3447334..75d2109bcb 100644 --- a/tests/ext/sandbox-prehook/exceptions_and_errors_are_ignored_in_tracing_closure.phpt +++ b/tests/ext/sandbox-prehook/exceptions_and_errors_are_ignored_in_tracing_closure.phpt @@ -3,6 +3,7 @@ --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_TRACED_INTERNAL_FUNCTIONS=mt_rand,mt_srand +DD_APPSEC_ENABLED=0 --INI-- error_reporting=E_ALL --FILE-- diff --git a/tests/ext/sandbox/exception_is_defined.phpt b/tests/ext/sandbox/exception_is_defined.phpt index 8909a92771..364354d7f0 100644 --- a/tests/ext/sandbox/exception_is_defined.phpt +++ b/tests/ext/sandbox/exception_is_defined.phpt @@ -2,6 +2,7 @@ Exceptions in the tracing closure callback are always defined --ENV-- DD_TRACE_TRACED_INTERNAL_FUNCTIONS=array_sum +DD_APPSEC_ENABLED=0 --FILE-- Date: Mon, 7 Oct 2024 21:50:09 +0200 Subject: [PATCH 099/103] Bump to 1.4.0 (#2877) Signed-off-by: Bob Weinand --- VERSION | 2 +- libdatadog | 2 +- package.xml | 72 +++++++++++++++++++++++++---------------------------- 3 files changed, 36 insertions(+), 40 deletions(-) diff --git a/VERSION b/VERSION index 589268e6fe..e21e727f96 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.0 \ No newline at end of file +1.4.0 \ No newline at end of file diff --git a/libdatadog b/libdatadog index 67d4f7a19a..f363618c74 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 67d4f7a19afd7f7301423e400008d42cc9284c04 +Subproject commit f363618c74f039e77fece732e68b9f9faddd9113 diff --git a/package.xml b/package.xml index e855a399cf..1558e260a2 100644 --- a/package.xml +++ b/package.xml @@ -28,12 +28,6 @@ bwoebi@php.net yes - - Joe Watkins - krakjoe - krakjoe@php.net - yes - Pierre Bonet pierotibou @@ -76,56 +70,58 @@ From dcc880fb51007b45eb2b5036e5a84edfc198e5c5 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Tue, 8 Oct 2024 15:24:22 +0100 Subject: [PATCH 100/103] Revert unintended change in extension_no_static_tls.phpt (#2879) * Revert unintended change in extension_no_static_tls.phpt * Revert more unintentional test changes --- tests/ext/extension_no_static_tls.phpt | 2 +- tests/ext/live-debugger/exception-replay_001.phpt | 2 +- tests/ext/live-debugger/exception-replay_002.phpt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ext/extension_no_static_tls.phpt b/tests/ext/extension_no_static_tls.phpt index 6ae89ca2fd..d331e5acae 100644 --- a/tests/ext/extension_no_static_tls.phpt +++ b/tests/ext/extension_no_static_tls.phpt @@ -26,7 +26,7 @@ if (!file_exists('/proc/self/maps')) { die('skip: no /proc/self/maps'); } // 5. Determine in which compilation unit(s) this function is defined $maps = file_get_contents('/proc/self/maps'); -if (preg_match('@(?<=\\s)\\S*/ddtrace\\.so$@m', $maps, $m) != 1) { +if (preg_match('@(?<=\\s)\\S*/ddtrace[^/]*\\.so$@m', $maps, $m) != 1) { die('cannot find loaded ddtrace.so'); } diff --git a/tests/ext/live-debugger/exception-replay_001.phpt b/tests/ext/live-debugger/exception-replay_001.phpt index 604d8e376f..e8cdd472b7 100644 --- a/tests/ext/live-debugger/exception-replay_001.phpt +++ b/tests/ext/live-debugger/exception-replay_001.phpt @@ -8,7 +8,7 @@ DD_AGENT_HOST=request-replayer DD_TRACE_AGENT_PORT=80 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_EXCEPTION_REPLAY_ENABLED=1 -DD_EXCEPTION_REPLAY_RATE_LIMIT_SECONDS=1 +DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 --INI-- datadog.trace.agent_test_session_token=live-debugger/exception-replay_001 --FILE-- diff --git a/tests/ext/live-debugger/exception-replay_002.phpt b/tests/ext/live-debugger/exception-replay_002.phpt index ceec8ac97f..1880a57a5b 100644 --- a/tests/ext/live-debugger/exception-replay_002.phpt +++ b/tests/ext/live-debugger/exception-replay_002.phpt @@ -7,7 +7,7 @@ DD_AGENT_HOST=request-replayer DD_TRACE_AGENT_PORT=80 DD_TRACE_GENERATE_ROOT_SPAN=0 DD_EXCEPTION_REPLAY_ENABLED=1 -DD_EXCEPTION_REPLAY_RATE_LIMIT_SECONDS=1 +DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS=1 --INI-- datadog.trace.agent_test_session_token=live-debugger/exception-replay_002 --FILE-- From df248349163ce159d18bd7cb10589da49f66eece Mon Sep 17 00:00:00 2001 From: Alexandre Choura <42672104+PROFeNoM@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:13:42 -0400 Subject: [PATCH 101/103] fix: MongoDBIntegration condition typo (#2884) --- src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php b/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php index 027a87bc1c..51dbd62d1b 100644 --- a/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php +++ b/src/DDTrace/Integrations/MongoDB/MongoDBIntegration.php @@ -26,7 +26,7 @@ public function commandStarted(\MongoDB\Driver\Monitoring\CommandStartedEvent $e if ($span) { if (is_null(self::$useDeprecatedMethods)) { // v1.20+: getServer() is deprecated in favor of getHost() and getPort() - self::$useDeprecatedMethods = !method_exists($event, 'getHost') || method_exists($event, 'getPort'); + self::$useDeprecatedMethods = !method_exists($event, 'getHost') || !method_exists($event, 'getPort'); } if (self::$useDeprecatedMethods) { From 0430ed8948569261c0a5868b787a284236ec1189 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 9 Oct 2024 21:40:38 +0200 Subject: [PATCH 102/103] Fix crash in profiler when span stack switch causes GC run (#2885) gc_collect_cycles may be intercepted by profiler and when happening precisely within ddtrace_switch_span_stack, the DDTRACE_G(active_span) points to just freed memory. Signed-off-by: Bob Weinand --- ext/span.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/span.c b/ext/span.c index 4af6dbf9a4..c2de1238ce 100644 --- a/ext/span.c +++ b/ext/span.c @@ -320,8 +320,9 @@ void ddtrace_switch_span_stack(ddtrace_span_stack *target_stack) { } GC_ADDREF(&target_stack->std); - OBJ_RELEASE(&DDTRACE_G(active_stack)->std); + ddtrace_span_stack *active_stack = DDTRACE_G(active_stack); DDTRACE_G(active_stack) = target_stack; + OBJ_RELEASE(&active_stack->std); } ddtrace_span_data *ddtrace_init_dummy_span(void) { From b23438e2473f607de9f1ca998cdaf679dff1e31c Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 10 Oct 2024 06:51:29 +0200 Subject: [PATCH 103/103] Fix profiler ZTS build on alpine (#2887) Circleci config was bad. Signed-off-by: Bob Weinand --- .circleci/continue_config.yml | 8 ++++++-- dockerfiles/ci/alpine_compile_extension/env-init | 13 +++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 39ac8aa2fc..0802359e3b 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -650,7 +650,9 @@ commands: steps: - run: name: Build Profiler NTS + shell: /bin/bash -ieo pipefail command: | + source "$BASH_ENV" if [ -d '/opt/rh/devtoolset-7' ] ; then set +eo pipefail source scl_source enable devtoolset-7 @@ -659,7 +661,7 @@ commands: set -u prefix="<< parameters.prefix >>" mkdir -vp "${prefix}" - command -v switch-php && switch-php "${PHP_VERSION}" + switch-php "${PHP_VERSION}" cd profiling echo "${CARGO_TARGET_DIR}" cargo build --release @@ -668,7 +670,9 @@ commands: objcopy --compress-debug-sections "${prefix}/datadog-profiling.so" - run: name: Build Profiler ZTS + shell: /bin/bash -ieo pipefail command: | + source "$BASH_ENV" if [ -d '/opt/rh/devtoolset-7' ] ; then set +eo pipefail source scl_source enable devtoolset-7 @@ -677,7 +681,7 @@ commands: set -u prefix="<< parameters.prefix >>" mkdir -vp "${prefix}" - command -v switch-php && switch-php "${PHP_VERSION}-zts" + switch-php "${PHP_VERSION}-zts" cd profiling echo "${CARGO_TARGET_DIR}" touch build.rs #make sure `build.rs` gets executed after `switch-php` call diff --git a/dockerfiles/ci/alpine_compile_extension/env-init b/dockerfiles/ci/alpine_compile_extension/env-init index 09d0fe509a..b6b2bde7bd 100755 --- a/dockerfiles/ci/alpine_compile_extension/env-init +++ b/dockerfiles/ci/alpine_compile_extension/env-init @@ -1,16 +1,17 @@ #!/bin/sh switch_php() { - phpType=$1 + phpType=${1#*-} - if [ ${phpType} != 'zts' ] && [ ${phpType} != 'nts' ]; then - echo "Invalid PHP version. Valid versions are: 'nts' and 'zts-" + if [ "${phpType}" != 'zts' ] && [ "${phpType}" != 'nts' ] && [ "${phpType#*.}" = "${phpType}" ]; then + echo "Invalid PHP version. Valid versions are: 'nts' and 'zts'" return 1 fi - if [ ${phpType} = 'zts' ]; then + if [ "${phpType}" = 'zts' ]; then export PATH="$(echo "${PATH}" | sed 's/\(\/usr\/local\/php-[^:]*\)\(-zts\|\)\/bin/\1-zts\/bin/')" else - export PATH="$(echo "${PATH}" | sed 's/\(\/usr\/local\/php-[^:]*\)\(-zts\|\)\/bin/\1\/bin/')" + export PATH="$(echo "${PATH}" | sed 's/\(\/usr\/local\/php-[^:-]*\)\(-zts\|\)\/bin/\1\/bin/')" fi -} \ No newline at end of file +} +alias switch-php=switch_php

    ^Bp;`&}UqD|d>`UJr8Hp7R_9{y+ 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 z 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�BM4+tjV>6bTB^4UxmADGy>Nao-XV9^SaeU1Cr3 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 GRrF_67c^ma)>X3@ zs?b7~8-eMqYoDqcFm*i-g@$ro8ExX4ob8W7eviw{w!Z

    @Uz%Y?ih*m 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 z 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