diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index e3d7b4a6..910114d7 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -11,7 +11,7 @@ jobs:
with:
submodules: true
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
path: |
data/country_osm_grid.sql.gz
@@ -27,7 +27,7 @@ jobs:
mv nominatim-src.tar.bz2 Nominatim
- name: 'Upload Artifact'
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: full-source
path: nominatim-src.tar.bz2
@@ -43,40 +43,28 @@ jobs:
ubuntu: 20
postgresql: '9.6'
postgis: '2.5'
- php: '7.3'
lua: '5.1'
- flavour: ubuntu-20
ubuntu: 20
postgresql: 13
postgis: 3
- php: '7.4'
lua: '5.3'
- flavour: ubuntu-22
ubuntu: 22
postgresql: 15
postgis: 3
- php: '8.1'
lua: '5.3'
runs-on: ubuntu-${{ matrix.ubuntu }}.04
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
with:
name: full-source
- name: Unpack Nominatim
run: tar xf nominatim-src.tar.bz2
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ matrix.php }}
- tools: phpunit:9, phpcs, composer
- ini-values: opcache.jit=disable
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- uses: actions/setup-python@v4
with:
python-version: 3.7
@@ -119,20 +107,11 @@ jobs:
run: pip3 install -U pylint
if: matrix.flavour != 'oldstuff'
- - name: PHP linting
- run: phpcs --report-width=120 .
- working-directory: Nominatim
- if: matrix.flavour != 'oldstuff'
-
- name: Python linting
run: python3 -m pylint nominatim
working-directory: Nominatim
if: matrix.flavour != 'oldstuff'
- - name: PHP unit tests
- run: phpunit ./
- working-directory: Nominatim/test/php
-
- name: Python unit tests
run: python3 -m pytest test/python
working-directory: Nominatim
@@ -156,7 +135,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
with:
name: full-source
@@ -185,16 +164,16 @@ jobs:
- name: BDD tests (legacy tokenizer)
run: |
- python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DTOKENIZER=legacy --format=progress3
+ python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DAPI_ENGINE=php -DTOKENIZER=legacy --format=progress3
working-directory: Nominatim/test/bdd
- python-api-test:
+ php-test:
needs: create-archive
runs-on: ubuntu-22.04
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
with:
name: full-source
@@ -206,6 +185,23 @@ jobs:
postgresql-version: 15
postgis-version: 3
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.1
+ tools: phpunit:9, phpcs, composer
+ ini-values: opcache.jit=disable
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: PHP linting
+ run: phpcs --report-width=120 .
+ working-directory: Nominatim
+
+ - name: PHP unit tests
+ run: phpunit ./
+ working-directory: Nominatim/test/php
+
- uses: ./Nominatim/.github/actions/build-nominatim
with:
flavour: 'ubuntu-22'
@@ -213,12 +209,9 @@ jobs:
- name: Install test prerequsites
run: sudo apt-get install -y -qq python3-behave
- - name: Install Python webservers
- run: pip3 install starlette asgi_lifespan httpx
-
- - name: BDD tests (starlette)
+ - name: BDD tests (php)
run: |
- python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DAPI_ENGINE=starlette --format=progress3
+ python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DAPI_ENGINE=php --format=progress3
working-directory: Nominatim/test/bdd
@@ -268,7 +261,7 @@ jobs:
OS: ${{ matrix.name }}
INSTALL_MODE: ${{ matrix.install_mode }}
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
with:
name: full-source
path: /home/nominatim
@@ -355,7 +348,7 @@ jobs:
needs: create-archive
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
with:
name: full-source
@@ -392,5 +385,4 @@ jobs:
NOMINATIM_DATABASE_DSN="pgsql:host=127.0.0.1;dbname=nominatim;user=osm-import;password=osm-import" nominatim import --continue import-from-file --osm-file test.pbf
- name: Check full import
- run: |
- nominatim admin --check-database
+ run: nominatim admin --check-database
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 536b21bc..6bd99967 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -82,13 +82,14 @@ endif()
# Setting PHP binary variable as to command line (prevailing) or auto detect
-if (BUILD_API OR BUILD_IMPORTER)
+if (BUILD_API)
if (NOT PHP_BIN)
find_program (PHP_BIN php)
endif()
# sanity check if PHP binary exists
if (NOT EXISTS ${PHP_BIN})
- message(FATAL_ERROR "PHP binary not found. Install php or provide location with -DPHP_BIN=/path/php ")
+ message(WARNING "PHP binary not found. Only Python frontend can be used.")
+ set(PHP_BIN "")
else()
message (STATUS "Using PHP binary " ${PHP_BIN})
endif()
@@ -226,7 +227,11 @@ if (BUILD_IMPORTER)
PATTERN "paths.py" EXCLUDE
PATTERN __pycache__ EXCLUDE)
- configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py.tmpl paths-py.installed)
+ if (EXISTS ${PHP_BIN})
+ configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py.tmpl paths-py.installed)
+ else()
+ configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py-no-php.tmpl paths-py.installed)
+ endif()
install(FILES ${PROJECT_BINARY_DIR}/paths-py.installed
DESTINATION ${NOMINATIM_LIBDIR}/lib-python/nominatim
RENAME paths.py)
@@ -254,7 +259,7 @@ if (BUILD_MODULE)
DESTINATION ${NOMINATIM_LIBDIR}/module)
endif()
-if (BUILD_API)
+if (BUILD_API AND EXISTS ${PHP_BIN})
install(DIRECTORY lib-php DESTINATION ${NOMINATIM_LIBDIR})
endif()
diff --git a/Vagrantfile b/Vagrantfile
index 57e64c2c..7f5f2459 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -38,7 +38,7 @@ Vagrant.configure("2") do |config|
lv.memory = 2048
lv.nested = true
if ENV['CHECKOUT'] != 'y' then
- override.vm.synced_folder ".", "/home/vagrant/Nominatim", type: 'nfs'
+ override.vm.synced_folder ".", "/home/vagrant/Nominatim", type: 'nfs', nfs_udp: false
end
end
diff --git a/cmake/paths-py-no-php.tmpl b/cmake/paths-py-no-php.tmpl
new file mode 100644
index 00000000..36856bf3
--- /dev/null
+++ b/cmake/paths-py-no-php.tmpl
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# This file is part of Nominatim. (https://nominatim.org)
+#
+# Copyright (C) 2022 by the Nominatim developer community.
+# For a full list of authors see the git log.
+"""
+Path settings for extra data used by Nominatim (installed version).
+"""
+from pathlib import Path
+
+PHPLIB_DIR = None
+SQLLIB_DIR = (Path('@NOMINATIM_LIBDIR@') / 'lib-sql').resolve()
+DATA_DIR = Path('@NOMINATIM_DATADIR@').resolve()
+CONFIG_DIR = Path('@NOMINATIM_CONFIGDIR@').resolve()
diff --git a/docs/admin/Import.md b/docs/admin/Import.md
index 0fd5ec29..b31066d3 100644
--- a/docs/admin/Import.md
+++ b/docs/admin/Import.md
@@ -268,27 +268,10 @@ nominatim reverse --lat 51 --lon 45
```
If you want to run Nominatim as a service, you need to make a choice between
-running the traditional PHP frontend or the new experimental Python frontend.
+running the modern Python frontend and the legacy PHP frontend.
Make sure you have installed the right packages as per
[Installation](Installation.md#software).
-#### Testing the PHP frontend
-
-You can run a small test server with the PHP frontend like this:
-
-```sh
-nominatim serve
-```
-
-Go to `http://localhost:8088/status.php` and you should see the message `OK`.
-You can also run a search query, e.g. `http://localhost:8088/search.php?q=Berlin`
-or, for reverse-only installations a reverse query,
-e.g. `http://localhost:8088/reverse.php?lat=27.1750090510034&lon=78.04209025`.
-
-Do not use this test server in production.
-To run Nominatim via webservers like Apache or nginx, please continue reading
-[Deploy the PHP frontend](Deployment-PHP.md).
-
#### Testing the Python frontend
To run the test server against the Python frontend, you must choose a
@@ -296,10 +279,10 @@ web framework to use, either starlette or falcon. Make sure the appropriate
packages are installed. Then run
``` sh
-nominatim serve --engine falcon
+nominatim serve
```
-or
+or, if you prefer to use Starlette instead of Falcon as webserver,
``` sh
nominatim serve --engine starlette
@@ -314,6 +297,24 @@ Do not use this test server in production.
To run Nominatim via webservers like Apache or nginx, please continue reading
[Deploy the Python frontend](Deployment-Python.md).
+#### Testing the PHP frontend
+
+You can run a small test server with the PHP frontend like this:
+
+```sh
+nominatim serve --engine php
+```
+
+Go to `http://localhost:8088/status.php` and you should see the message `OK`.
+You can also run a search query, e.g. `http://localhost:8088/search.php?q=Berlin`
+or, for reverse-only installations a reverse query,
+e.g. `http://localhost:8088/reverse.php?lat=27.1750090510034&lon=78.04209025`.
+
+Do not use this test server in production.
+To run Nominatim via webservers like Apache or nginx, please continue reading
+[Deploy the PHP frontend](Deployment-PHP.md).
+
+
## Enabling search by category phrases
diff --git a/docs/admin/Installation.md b/docs/admin/Installation.md
index 89e56c6e..ef6bd081 100644
--- a/docs/admin/Installation.md
+++ b/docs/admin/Installation.md
@@ -55,23 +55,24 @@ For running Nominatim:
* [PyYaml](https://pyyaml.org/) (5.1+)
* [datrie](https://github.com/pytries/datrie)
-When running the PHP frontend:
-
- * [PHP](https://php.net) (7.3+)
- * PHP-pgsql
- * PHP-intl (bundled with PHP)
-
For running continuous updates:
* [pyosmium](https://osmcode.org/pyosmium/)
-For running the experimental Python frontend:
+For running the Python frontend:
* one of the following web frameworks:
* [falcon](https://falconframework.org/) (3.0+)
* [starlette](https://www.starlette.io/)
* [uvicorn](https://www.uvicorn.org/)
+For running the legacy PHP frontend:
+
+ * [PHP](https://php.net) (7.3+)
+ * PHP-pgsql
+ * PHP-intl (bundled with PHP)
+
+
For dependencies for running tests and building documentation, see
the [Development section](../develop/Development-Environment.md).
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index f332640f..74465d1a 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -22,8 +22,8 @@ nav:
- 'Basic Installation': 'admin/Installation.md'
- 'Import' : 'admin/Import.md'
- 'Update' : 'admin/Update.md'
- - 'Deploy (PHP frontend)' : 'admin/Deployment-PHP.md'
- 'Deploy (Python frontend)' : 'admin/Deployment-Python.md'
+ - 'Deploy (PHP frontend)' : 'admin/Deployment-PHP.md'
- 'Nominatim UI' : 'admin/Setup-Nominatim-UI.md'
- 'Advanced Installations' : 'admin/Advanced-Installations.md'
- 'Maintenance' : 'admin/Maintenance.md'
diff --git a/nominatim/api/logging.py b/nominatim/api/logging.py
index e16e0bd2..2d9a487a 100644
--- a/nominatim/api/logging.py
+++ b/nominatim/api/logging.py
@@ -13,6 +13,7 @@ import datetime as dt
import textwrap
import io
import re
+import html
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import AsyncConnection
@@ -227,7 +228,7 @@ class HTMLLogger(BaseLogger):
HtmlFormatter(nowrap=True, lineseparator='
'))
self._write(f'
{sqlstr}{sqlstr}')
+ self._write(f'{html.escape(sqlstr)}')
def _python_var(self, var: Any) -> str:
@@ -235,7 +236,7 @@ class HTMLLogger(BaseLogger):
fmt = highlight(str(var), PythonLexer(), HtmlFormatter(nowrap=True))
return f'{fmt}{str(var)}'
+ return f'{html.escape(str(var))}'
def _write(self, text: str) -> None:
diff --git a/nominatim/cli.py b/nominatim/cli.py
index 88a60782..720a8ece 100644
--- a/nominatim/cli.py
+++ b/nominatim/cli.py
@@ -159,13 +159,15 @@ class AdminServe:
group = parser.add_argument_group('Server arguments')
group.add_argument('--server', default='127.0.0.1:8088',
help='The address the server will listen to.')
- group.add_argument('--engine', default='php',
+ group.add_argument('--engine', default='falcon',
choices=('php', 'falcon', 'starlette'),
- help='Webserver framework to run. (default: php)')
+ help='Webserver framework to run. (default: falcon)')
def run(self, args: NominatimArgs) -> int:
if args.engine == 'php':
+ if args.config.lib_dir.php is None:
+ raise UsageError("PHP frontend not configured.")
run_php_server(args.server, args.project_dir / 'website')
else:
import uvicorn # pylint: disable=import-outside-toplevel
diff --git a/nominatim/tokenizer/icu_tokenizer.py b/nominatim/tokenizer/icu_tokenizer.py
index 5a90edf5..84b4b924 100644
--- a/nominatim/tokenizer/icu_tokenizer.py
+++ b/nominatim/tokenizer/icu_tokenizer.py
@@ -214,19 +214,20 @@ class ICUTokenizer(AbstractTokenizer):
return list(s[0].split('@')[0] for s in cur)
- def _install_php(self, phpdir: Path, overwrite: bool = True) -> None:
+ def _install_php(self, phpdir: Optional[Path], overwrite: bool = True) -> None:
""" Install the php script for the tokenizer.
"""
- assert self.loader is not None
- php_file = self.data_dir / "tokenizer.php"
+ if phpdir is not None:
+ assert self.loader is not None
+ php_file = self.data_dir / "tokenizer.php"
- if not php_file.exists() or overwrite:
- php_file.write_text(dedent(f"""\
- None:
diff --git a/nominatim/tokenizer/legacy_tokenizer.py b/nominatim/tokenizer/legacy_tokenizer.py
index 2d28a8b2..f3a00839 100644
--- a/nominatim/tokenizer/legacy_tokenizer.py
+++ b/nominatim/tokenizer/legacy_tokenizer.py
@@ -269,15 +269,16 @@ class LegacyTokenizer(AbstractTokenizer):
def _install_php(self, config: Configuration, overwrite: bool = True) -> None:
""" Install the php script for the tokenizer.
"""
- php_file = self.data_dir / "tokenizer.php"
+ if config.lib_dir.php is not None:
+ php_file = self.data_dir / "tokenizer.php"
- if not php_file.exists() or overwrite:
- php_file.write_text(dedent(f"""\
- None:
diff --git a/nominatim/tools/refresh.py b/nominatim/tools/refresh.py
index 43e5b1eb..008fc714 100644
--- a/nominatim/tools/refresh.py
+++ b/nominatim/tools/refresh.py
@@ -213,6 +213,10 @@ def _quote_php_variable(var_type: Type[Any], config: Configuration,
def setup_website(basedir: Path, config: Configuration, conn: Connection) -> None:
""" Create the website script stubs.
"""
+ if config.lib_dir.php is None:
+ LOG.info("Python frontend does not require website setup. Skipping.")
+ return
+
if not basedir.exists():
LOG.info('Creating website directory.')
basedir.mkdir()
diff --git a/test/bdd/environment.py b/test/bdd/environment.py
index 664b5ac7..460f3569 100644
--- a/test/bdd/environment.py
+++ b/test/bdd/environment.py
@@ -28,7 +28,7 @@ userconfig = {
'SERVER_MODULE_PATH' : None,
'TOKENIZER' : None, # Test with a custom tokenizer
'STYLE' : 'extratags',
- 'API_ENGINE': 'php',
+ 'API_ENGINE': 'falcon',
'PHPCOV' : False, # set to output directory to enable code coverage
}
diff --git a/test/bdd/steps/steps_api_queries.py b/test/bdd/steps/steps_api_queries.py
index 3d3b16c7..aa1b43b8 100644
--- a/test/bdd/steps/steps_api_queries.py
+++ b/test/bdd/steps/steps_api_queries.py
@@ -243,7 +243,7 @@ def step_impl(context, fmt):
try:
tree = ET.fromstring(context.response.page)
except Exception as ex:
- assert False, f"Could not parse page:\n{context.response.page}"
+ assert False, f"Could not parse page: {ex}\n{context.response.page}"
assert tree.tag == 'html'
body = tree.find('./body')
diff --git a/test/python/cli/test_cli.py b/test/python/cli/test_cli.py
index 93e86108..d455f35e 100644
--- a/test/python/cli/test_cli.py
+++ b/test/python/cli/test_cli.py
@@ -63,7 +63,7 @@ def test_cli_add_data_tiger_data(cli_call, cli_tokenizer_mock, mock_func_factory
def test_cli_serve_php(cli_call, mock_func_factory):
func = mock_func_factory(nominatim.cli, 'run_php_server')
- cli_call('serve') == 0
+ cli_call('serve', '--engine', 'php') == 0
assert func.called == 1
diff --git a/vagrant/Install-on-Ubuntu-20.sh b/vagrant/Install-on-Ubuntu-20.sh
index 720e80c8..57361ec7 100755
--- a/vagrant/Install-on-Ubuntu-20.sh
+++ b/vagrant/Install-on-Ubuntu-20.sh
@@ -16,16 +16,16 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
# Make sure all packages are up-to-date by running:
#
- sudo apt update -qq
+ sudo apt-get update -qq
# Now you can install all packages needed for Nominatim:
- sudo apt install -y build-essential cmake g++ libboost-dev libboost-system-dev \
+ sudo apt-get install -y build-essential cmake g++ libboost-dev libboost-system-dev \
libboost-filesystem-dev libexpat1-dev zlib1g-dev \
libbz2-dev libpq-dev liblua5.3-dev lua5.3 lua-dkjson \
nlohmann-json3-dev postgresql-12-postgis-3 \
postgresql-contrib-12 postgresql-12-postgis-3-scripts \
- php-cli php-pgsql php-intl libicu-dev python3-dotenv \
+ libicu-dev python3-dotenv \
python3-psycopg2 python3-psutil python3-jinja2 python3-pip \
python3-icu python3-datrie python3-yaml git
@@ -133,45 +133,107 @@ fi #DOCS:
# Nominatim is now ready to use. You can continue with
# [importing a database from OSM data](../admin/Import.md). If you want to set up
-# a webserver first, continue reading.
+# the API frontend first, continue reading.
#
+# Setting up the Python frontend
+# ==============================
+#
+# Some of the Python packages in Ubuntu are too old. Therefore run the
+# frontend from a Python virtualenv with current packages.
+#
+# To set up the virtualenv, run:
+
+#DOCS:```sh
+sudo apt-get install -y virtualenv
+virtualenv $USERHOME/nominatim-venv
+$USERHOME/nominatim-venv/bin/pip install SQLAlchemy PyICU psycopg[binary] \
+ psycopg2-binary python-dotenv PyYAML falcon uvicorn gunicorn
+#DOCS:```
+
+# Next you need to create a systemd job that runs Nominatim on gunicorn.
+# First create a systemd job that manages the socket file:
+
+#DOCS:```sh
+sudo tee /etc/systemd/system/nominatim.socket << EOFSOCKETSYSTEMD
+[Unit]
+Description=Gunicorn socket for Nominatim
+
+[Socket]
+ListenStream=/run/nominatim.sock
+SocketUser=www-data
+
+[Install]
+WantedBy=multi-user.target
+EOFSOCKETSYSTEMD
+#DOCS:```
+
+# Then create the service for Nominatim itself.
+
+#DOCS:```sh
+sudo tee /etc/systemd/system/nominatim.service << EOFNOMINATIMSYSTEMD
+[Unit]
+Description=Nominatim running as a gunicorn application
+After=network.target
+Requires=nominatim.socket
+
+[Service]
+Type=simple
+Environment="PYTHONPATH=/usr/local/lib/nominatim/lib-python/"
+User=www-data
+Group=www-data
+WorkingDirectory=$USERHOME/nominatim-project
+ExecStart=$USERHOME/nominatim-venv/bin/gunicorn -b unix:/run/nominatim.sock -w 4 -k uvicorn.workers.UvicornWorker nominatim.server.falcon.server:run_wsgi
+ExecReload=/bin/kill -s HUP \$MAINPID
+StandardOutput=append:/var/log/gunicorn-nominatim.log
+StandardError=inherit
+PrivateTmp=true
+TimeoutStopSec=5
+KillMode=mixed
+
+[Install]
+WantedBy=multi-user.target
+EOFNOMINATIMSYSTEMD
+#DOCS:```
+
+# Activate the services:
+
+if [ "x$NOSYSTEMD" != "xyes" ]; then #DOCS:
+ sudo systemctl daemon-reload
+ sudo systemctl enable nominatim.socket
+ sudo systemctl start nominatim.socket
+ sudo systemctl enable nominatim.service
+fi #DOCS:
+
+
# Setting up a webserver
# ======================
#
-# The webserver should serve the php scripts from the website directory of your
-# [project directory](../admin/Import.md#creating-the-project-directory).
-# This directory needs to exist when being configured.
-# Therefore set up a project directory and create a website directory:
+# The webserver is only needed as a proxy between the public interface
+# and the gunicorn service.
+#
+# The frontend will need configuration information from the project
+# directory, which will be populated later
+# [during the import process](../admin/Import.md#creating-the-project-directory)
+# Already create the project directory itself now:
+
mkdir $USERHOME/nominatim-project
- mkdir $USERHOME/nominatim-project/website
-# The import process will populate the directory later.
-#
# Option 1: Using Apache
# ----------------------
#
if [ "x$2" == "xinstall-apache" ]; then #DOCS:
-#
-# Apache has a PHP module that can be used to serve Nominatim. To install them
-# run:
+# First install apache itself and enable the proxy module:
- sudo apt install -y apache2 libapache2-mod-php
+ sudo apt-get install -y apache2
+ sudo a2enmod proxy_http
-# You need to create an alias to the website directory in your apache
-# configuration. Add a separate nominatim configuration to your webserver:
+# To set up proxying for Apache add the following configuration:
#DOCS:```sh
sudo tee /etc/apache2/conf-available/nominatim.conf << EOFAPACHECONF
-