Bearsampp 2025.8.29
Loading...
Searching...
No Matches
class.bin.postgresql.php
Go to the documentation of this file.
1<?php
2/*
3 * Copyright (c) 2021-2024 Bearsampp
4 * License: GNU General Public License version 3 or later; see LICENSE.txt
5 * Author: Bear
6 * Website: https://bearsampp.com
7 * Github: https://github.com/Bearsampp
8 */
9
16class BinPostgresql extends Module
17{
18 const SERVICE_NAME = 'bearsampppostgresql';
19
20 const ROOT_CFG_ENABLE = 'postgresqlEnable';
21 const ROOT_CFG_VERSION = 'postgresqlVersion';
22
23 const LOCAL_CFG_CTL_EXE = 'postgresqlCtlExe';
24 const LOCAL_CFG_CLI_EXE = 'postgresqlCliExe';
25 const LOCAL_CFG_DUMP_EXE = 'postgresqlDumpExe';
26 const LOCAL_CFG_DUMP_ALL_EXE = 'postgresqlDumpAllExe';
27 const LOCAL_CFG_CONF = 'postgresqlConf';
28 const LOCAL_CFG_HBA_CONF = 'postgresqlUserConf';
29 const LOCAL_CFG_ALT_CONF = 'postgresqlAltConf';
30 const LOCAL_CFG_ALT_HBA_CONF = 'postgresqlAltUserConf';
31 const LOCAL_CFG_PORT = 'postgresqlPort';
32 const LOCAL_CFG_ROOT_USER = 'postgresqlRootUser';
33 const LOCAL_CFG_ROOT_PWD = 'postgresqlRootPwd';
34
35 const CMD_VERSION = '--version';
36
37 private $service;
38 private $errorLog;
39 private $ctlExe;
40 private $cliExe;
41 private $dumpExe;
42 private $dumpAllExe;
43 private $conf;
44 private $hbaConf;
45 private $altConf;
46 private $altHbaConf;
47 private $port;
48 private $rootUser;
49 private $rootPwd;
50
57 public function __construct($id, $type)
58 {
59 Util::logInitClass( $this );
60 $this->reload( $id, $type );
61 }
62
69 public function reload($id = null, $type = null)
70 {
72 Util::logReloadClass( $this );
73
74 $this->name = $bearsamppLang->getValue( Lang::POSTGRESQL );
75 $this->version = $bearsamppConfig->getRaw( self::ROOT_CFG_VERSION );
76 parent::reload( $id, $type );
77
78 $this->enable = $this->enable && $bearsamppConfig->getRaw( self::ROOT_CFG_ENABLE );
79 $this->service = new Win32Service( self::SERVICE_NAME );
80 $this->errorLog = $bearsamppRoot->getLogsPath() . '/postgresql.log';
81
82 if ( $this->bearsamppConfRaw !== false ) {
83 $this->ctlExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_CTL_EXE];
84 $this->cliExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_CLI_EXE];
85 $this->dumpExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_DUMP_EXE];
86 $this->dumpAllExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_DUMP_ALL_EXE];
87 $this->conf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_CONF];
88 $this->hbaConf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_HBA_CONF];
89 $this->altConf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_ALT_CONF];
90 $this->altHbaConf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_ALT_HBA_CONF];
91 $this->port = $this->bearsamppConfRaw[self::LOCAL_CFG_PORT];
92 $this->rootUser = isset( $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_USER] ) ? $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_USER] : 'postgres';
93 $this->rootPwd = isset( $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_PWD] ) ? $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_PWD] : '';
94 }
95
96 if ( !$this->enable ) {
97 Util::logInfo( $this->name . ' is not enabled!' );
98
99 return;
100 }
101 if ( !is_dir( $this->currentPath ) ) {
102 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->currentPath ) );
103
104 return;
105 }
106 if ( !is_dir( $this->symlinkPath ) ) {
107 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->symlinkPath ) );
108
109 return;
110 }
111 if ( !is_file( $this->bearsamppConf ) ) {
112 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->bearsamppConf ) );
113
114 return;
115 }
116 if ( !file_exists( $this->conf ) ) {
117 $this->conf = $this->altConf;
118 }
119 if ( !file_exists( $this->hbaConf ) ) {
120 $this->hbaConf = $this->altHbaConf;
121 }
122
123 if ( !is_file( $this->ctlExe ) ) {
124 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->ctlExe ) );
125
126 return;
127 }
128 if ( !is_file( $this->cliExe ) ) {
129 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->cliExe ) );
130
131 return;
132 }
133 if ( !is_file( $this->dumpExe ) ) {
134 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->dumpExe ) );
135
136 return;
137 }
138 if ( !is_file( $this->dumpAllExe ) ) {
139 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->dumpAllExe ) );
140
141 return;
142 }
143 if ( !is_file( $this->conf ) ) {
144 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->conf ) );
145
146 return;
147 }
148 if ( !is_file( $this->hbaConf ) ) {
149 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->hbaConf ) );
150
151 return;
152 }
153 if ( !is_numeric( $this->port ) || $this->port <= 0 ) {
154 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_PORT, $this->port ) );
155
156 return;
157 }
158 if ( empty( $this->rootUser ) ) {
159 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_ROOT_USER, $this->rootUser ) );
160
161 return;
162 }
163
164 $this->service->setDisplayName( APP_TITLE . ' ' . $this->getName() );
165 $this->service->setBinPath( $this->ctlExe );
166 $this->service->setStartType( Win32Service::SERVICE_DEMAND_START );
167 $this->service->setErrorControl( Win32Service::SERVER_ERROR_NORMAL );
168 }
169
175 protected function replaceAll($params)
176 {
177 $content = file_get_contents( $this->bearsamppConf );
178
179 foreach ( $params as $key => $value ) {
180 $content = preg_replace( '|' . $key . ' = .*|', $key . ' = ' . '"' . $value . '"', $content );
181 $this->bearsamppConfRaw[$key] = $value;
182 switch ( $key ) {
183 case self::LOCAL_CFG_PORT:
184 $this->port = $value;
185 break;
186 case self::LOCAL_CFG_ROOT_USER:
187 $this->rootUser = $value;
188 break;
189 case self::LOCAL_CFG_ROOT_PWD:
190 $this->rootPwd = $value;
191 break;
192 }
193 }
194
195 file_put_contents( $this->bearsamppConf, $content );
196 }
197
207 public function changePort($port, $checkUsed = false, $wbProgressBar = null)
208 {
209 global $bearsamppWinbinder;
210
211 if ( !Util::isValidPort( $port ) ) {
212 Util::logError( $this->getName() . ' port not valid: ' . $port );
213
214 return false;
215 }
216
217 $port = intval( $port );
218 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
219
220 $isPortInUse = Util::isPortInUse( $port );
221 if ( !$checkUsed || $isPortInUse === false ) {
222 // bearsampp.conf
223 $this->setPort( $port );
224 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
225
226 // conf
227 $this->update();
228 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
229
230 return true;
231 }
232
233 Util::logDebug( $this->getName() . ' port in used: ' . $port . ' - ' . $isPortInUse );
234
235 return $isPortInUse;
236 }
237
244 public function checkPort($port, $showWindow = false)
245 {
246 global $bearsamppLang, $bearsamppWinbinder;
247 $boxTitle = sprintf($bearsamppLang->getValue(Lang::CHECK_PORT_TITLE), $this->getName(), $port);
248
249 if (!Util::isValidPort($port)) {
250 Util::logError($this->getName() . ' port not valid: ' . $port);
251 return false;
252 }
253
254 $dbLink = @pg_connect("host=127.0.0.1 port=$port user={$this->rootUser} password={$this->rootPwd}");
255 if (!$dbLink) {
256 Util::logDebug($this->getName() . ' connection failed: ' . pg_last_error());
257 return $this->handleNonPostgresUsage($port, $showWindow, $boxTitle);
258 }
259
260 // Verify active PostgreSQL connection
261 $connectionStatus = pg_connection_status($dbLink);
262 $isPostgres = $connectionStatus === PGSQL_CONNECTION_OK;
263 pg_close($dbLink);
264
265 if ($isPostgres) {
266 Util::logDebug($this->getName() . " port $port is used by PostgreSQL");
267 if ($showWindow) {
268 $bearsamppWinbinder->messageBoxInfo(
269 sprintf($bearsamppLang->getValue(Lang::PORT_USED_BY), $port, 'PostgreSQL'),
270 $boxTitle
271 );
272 }
273 return true;
274 }
275
276 return $this->handleNonPostgresUsage($port, $showWindow, $boxTitle);
277 }
278
282 private function handleNonPostgresUsage($port, $showWindow, $boxTitle)
283 {
284 global $bearsamppLang, $bearsamppWinbinder;
285
287 Util::logDebug($this->getName() . " port $port used by non-PostgreSQL service");
288 if ($showWindow) {
289 $bearsamppWinbinder->messageBoxWarning(
291 $boxTitle
292 );
293 }
294 return false;
295 }
296
297 Util::logDebug($this->getName() . " port $port not in use");
298 if ($showWindow) {
299 $bearsamppWinbinder->messageBoxError(
300 sprintf($bearsamppLang->getValue(Lang::PORT_NOT_USED), $port),
301 $boxTitle
302 );
303 }
304 return false;
305 }
306
315 public function changeRootPassword($currentPwd, $newPwd, $wbProgressBar = null)
316 {
317 global $bearsamppWinbinder;
318 $error = null;
319
320 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
321 $dbLink = pg_connect( 'host=127.0.0.1 port=' . $this->port . ' user=' . $this->rootUser . ' password=' . $currentPwd );
322
323 if ( !$dbLink ) {
324 $error = pg_last_error( $dbLink );
325 }
326
327 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
328 $pgr = pg_query_params( $dbLink, 'SELECT quote_ident($1)', array(pg_escape_string( $this->rootUser )) );
329 list( $quoted_user ) = pg_fetch_array( $pgr );
330 $password = pg_escape_string( $newPwd );
331 $result = pg_query( $dbLink, "ALTER USER $quoted_user WITH PASSWORD '$password'" );
332 if ( empty( $error ) && !$result ) {
333 $error = pg_last_error( $dbLink );
334 }
335
336 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
337 if ( $dbLink ) {
338 pg_close( $dbLink );
339 }
340
341 if ( !empty( $error ) ) {
342 return $error;
343 }
344
345 // bearsampp.conf
346 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
347 $this->setRootPwd( $newPwd );
348
349 // conf
350 $this->update();
351 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
352
353 return true;
354 }
355
364 public function checkRootPassword($currentPwd = null, $wbProgressBar = null)
365 {
366 global $bearsamppWinbinder;
367 $currentPwd = $currentPwd == null ? $this->rootPwd : $currentPwd;
368 $error = null;
369
370 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
371 $dbLink = pg_connect( 'host=127.0.0.1 port=' . $this->port . ' user=' . $this->rootUser . ' password=' . $currentPwd );
372 if ( !$dbLink ) {
373 $error = pg_last_error( $dbLink );
374 }
375
376 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
377 if ( $dbLink ) {
378 pg_close( $dbLink );
379 }
380
381 if ( !empty( $error ) ) {
382 return $error;
383 }
384
385 return true;
386 }
387
396 public function switchVersion($version, $showWindow = false)
397 {
398 Util::logDebug( 'Switch ' . $this->name . ' version to ' . $version );
399
400 return $this->updateConfig( $version, 0, $showWindow );
401 }
402
412 protected function updateConfig($version = null, $sub = 0, $showWindow = false)
413 {
414 global $bearsamppLang, $bearsamppApps, $bearsamppWinbinder;
415
416 if ( !$this->enable ) {
417 return true;
418 }
419
420 $version = $version == null ? $this->version : $version;
421 Util::logDebug( ($sub > 0 ? str_repeat( ' ', 2 * $sub ) : '') . 'Update ' . $this->name . ' ' . $version . ' config' );
422
423 $boxTitle = sprintf( $bearsamppLang->getValue( Lang::SWITCH_VERSION_TITLE ), $this->getName(), $version );
424
425 $currentPath = str_replace( 'postgresql' . $this->getVersion(), 'postgresql' . $version, $this->getCurrentPath() );
426 $conf = str_replace( 'postgresql' . $this->getVersion(), 'postgresql' . $version, $this->getConf() );
427 $bearsamppConf = str_replace( 'postgresql' . $this->getVersion(), 'postgresql' . $version, $this->bearsamppConf );
428
429 if ( $this->version != $version ) {
430 $this->initData( $currentPath );
431 }
432
433 if ( !file_exists( $conf ) || !file_exists( $bearsamppConf ) ) {
434 Util::logError( 'bearsampp config files not found for ' . $this->getName() . ' ' . $version );
435 if ( $showWindow ) {
436 $bearsamppWinbinder->messageBoxError(
437 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_NOT_FOUND_ERROR ), $this->getName() . ' ' . $version ),
438 $boxTitle
439 );
440 }
441
442 return false;
443 }
444
445 $bearsamppConfRaw = parse_ini_file( $bearsamppConf );
446 if ( $bearsamppConfRaw === false || !isset( $bearsamppConfRaw[self::ROOT_CFG_VERSION] ) || $bearsamppConfRaw[self::ROOT_CFG_VERSION] != $version ) {
447 Util::logError( 'bearsampp config file malformed for ' . $this->getName() . ' ' . $version );
448 if ( $showWindow ) {
449 $bearsamppWinbinder->messageBoxError(
450 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_MALFORMED_ERROR ), $this->getName() . ' ' . $version ),
451 $boxTitle
452 );
453 }
454
455 return false;
456 }
457
458 // bearsampp.conf
459 $this->setVersion( $version );
460
461 // conf
462 Util::replaceInFile( $this->getConf(), array(
463 '/^port(.*?)=(.*?)(\d+)/' => 'port = ' . $this->port
464 ) );
465
466 // phppgadmin
467 $bearsamppApps->getPhppgadmin()->update( $sub + 1 );
468
469 return true;
470 }
471
477 public function initData($path = null)
478 {
479 $path = $path != null ? $path : $this->getCurrentPath();
480
481 if ( file_exists( $path . '/data' ) ) {
482 return;
483 }
484
486 }
487
491 public function rebuildConf()
492 {
493 Util::replaceInFile( $this->conf, array(
494 '/^port(.*?)=(.*?)(\d+)/' => 'port = ' . $this->port
495 ) );
496 Util::replaceInFile( $this->altConf, array(
497 '/^port(.*?)=(.*?)(\d+)/' => 'port = ' . $this->port
498 ) );
499 }
500
508 public function getCmdLineOutput($cmd)
509 {
510 $result = null;
511
512 $bin = $this->getCliExe();
513 if ( file_exists( $bin ) ) {
514 $tmpResult = Batch::exec( 'postgresqlGetCmdLineOutput', '"' . $bin . '" ' . $cmd );
515 if ( $tmpResult !== false && is_array( $tmpResult ) ) {
516 $result = trim( str_replace( $bin, '', implode( PHP_EOL, $tmpResult ) ) );
517 }
518 }
519
520 return $result;
521 }
522
528 public function setVersion($version)
529 {
530 global $bearsamppConfig;
531 $this->version = $version;
532 $bearsamppConfig->replace( self::ROOT_CFG_VERSION, $version );
533 $this->reload();
534 }
535
541 public function getService()
542 {
543 return $this->service;
544 }
545
552 public function setEnable($enabled, $showWindow = false)
553 {
554 global $bearsamppConfig, $bearsamppLang, $bearsamppWinbinder;
555
556 if ( $enabled == Config::ENABLED && !is_dir( $this->currentPath ) ) {
557 Util::logDebug( $this->getName() . ' cannot be enabled because bundle ' . $this->getVersion() . ' does not exist in ' . $this->currentPath );
558 if ( $showWindow ) {
559 $bearsamppWinbinder->messageBoxError(
560 sprintf( $bearsamppLang->getValue( Lang::ENABLE_BUNDLE_NOT_EXIST ), $this->getName(), $this->getVersion(), $this->currentPath ),
561 sprintf( $bearsamppLang->getValue( Lang::ENABLE_TITLE ), $this->getName() )
562 );
563 }
564 $enabled = Config::DISABLED;
565 }
566
567 Util::logInfo( $this->getName() . ' switched to ' . ($enabled == Config::ENABLED ? 'enabled' : 'disabled') );
568 $this->enable = $enabled == Config::ENABLED;
569 $bearsamppConfig->replace( self::ROOT_CFG_ENABLE, $enabled );
570
571 $this->reload();
572 if ( $this->enable ) {
573 Util::installService( $this, $this->port, null, $showWindow );
574 }
575 else {
576 Util::removeService( $this->service, $this->name );
577 }
578 }
579
585 public function getErrorLog()
586 {
587 return $this->errorLog;
588 }
589
595 public function getCtlExe()
596 {
597 return $this->ctlExe;
598 }
599
605 public function getCliExe()
606 {
607 return $this->cliExe;
608 }
609
615 public function getDumpExe()
616 {
617 return $this->dumpExe;
618 }
619
625 public function getDumpAllExe()
626 {
627 return $this->dumpAllExe;
628 }
629
635 public function getConf()
636 {
637 return $this->conf;
638 }
639
645 public function getHbaConf()
646 {
647 return $this->hbaConf;
648 }
649
655 public function getPort()
656 {
657 return $this->port;
658 }
659
665 public function setPort($port)
666 {
667 $this->replace( self::LOCAL_CFG_PORT, $port );
668 }
669
675 public function getRootUser()
676 {
677 return $this->rootUser;
678 }
679
685 public function setRootUser($rootUser)
686 {
687 $this->replace( self::LOCAL_CFG_ROOT_USER, $rootUser );
688 }
689
695 public function getRootPwd()
696 {
697 return $this->rootPwd;
698 }
699
705 public function setRootPwd($rootPwd)
706 {
707 $this->replace( self::LOCAL_CFG_ROOT_PWD, $rootPwd );
708 }
709}
$result
global $bearsamppLang
global $bearsamppRoot
static initializePostgresql($path)
static exec($basename, $content, $timeout=true, $catchOutput=true, $standalone=false, $silent=true, $rebuild=true)
handleNonPostgresUsage($port, $showWindow, $boxTitle)
updateConfig($version=null, $sub=0, $showWindow=false)
changeRootPassword($currentPwd, $newPwd, $wbProgressBar=null)
switchVersion($version, $showWindow=false)
checkPort($port, $showWindow=false)
changePort($port, $checkUsed=false, $wbProgressBar=null)
setEnable($enabled, $showWindow=false)
reload($id=null, $type=null)
checkRootPassword($currentPwd=null, $wbProgressBar=null)
const DISABLED
const ENABLED
const ENABLE_BUNDLE_NOT_EXIST
const BEARSAMPP_CONF_MALFORMED_ERROR
const ERROR_EXE_NOT_FOUND
const ERROR_CONF_NOT_FOUND
const POSTGRESQL
const BEARSAMPP_CONF_NOT_FOUND_ERROR
const PORT_NOT_USED
const ERROR_INVALID_PARAMETER
const ENABLE_TITLE
const SWITCH_VERSION_TITLE
const CHECK_PORT_TITLE
const PORT_USED_BY
const PORT_USED_BY_ANOTHER_DBMS
const ERROR_FILE_NOT_FOUND
update($sub=0, $showWindow=false)
replace($key, $value)
static logError($data, $file=null)
static installService($bin, $port, $syntaxCheckCmd, $showWindow=false)
static logInitClass($classInstance)
static removeService($service, $name)
static isValidPort($port)
static logInfo($data, $file=null)
static logDebug($data, $file=null)
static logReloadClass($classInstance)
static isPortInUse($port)
static replaceInFile($path, $replaceList)
global $bearsamppConfig
Definition homepage.php:27
const APP_TITLE
Definition root.php:13