PB Interpreter » Use PB instead of PHP or Perl for websites!

Developed or developing a new product in PureBasic? Tell the world about it.
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

PB Interpreter » Use PB instead of PHP or Perl for websites!

Post by AND51 »

Download:

http://www.and51.de/pbinterpreter.zip


History:

Version 1.5
  • Added: Added long form parameters. You can now use --force or -f, --compress or -c, --warn or -w
  • Added: Added environment variables which are set to 1 if -f, -c, -p or -w are activated (PB_Interpreter_ParamCompress, PB_Interpreter_ParamWarn, PB_Interpreter_ParamParse, PB_Interpreter_ParamForce)
  • Added: MULTILINE SYNTAX now lets you write multiline code, e. g. for long Print() commands. They are similar to here-documents in Perl and they support interpolation: you can easily insert your variables or any other expression. This feature supports multiline with and without CRLF-replacement (CRLFs are optionally replaced by #CRLF$) and you can include external files as multiline code.
    Hint: for best performance, multiline is only supported when you set the -p or --param parameter. See page 4 of this thread for examples
  • Added: For convenience, get-parameters like 'test' and 'ok' of index.pb?test=1&ok=2 are provided as environment variables 'GET_test' and 'GET_ok'
  • Added: Environment variables PB_Interpreter_Version to determine the PB Interpreters version, PB_Interpreter_QueryStrings number of GET-params extracted from the URI
  • Repaired: Before 1.2 the last parameter was ignored, because of a trailing CRLF appended by Apache
Version 1.3
  • (internal release, not published)
Version 1.2
  • (internal release, not published)
Version 1.1
  • Added: (Re-)Compilation only when neccessary (if source code newer than executable). Executable will be kept in the system's temp-directory
  • Added: isParameter() and getParameterValue() in the interpreters source code (explanations see source code)
  • Added: error message when compiler cannot be started
  • Added: parameter -c (compress) to compress the executable with UPX (if installed). This is optional (see readme). Note: If compression failes, the process will not be aborted! Furthermore, it seems that not all executables are compressed (is there a minimum size?)
  • Added: parameter -w (warn) tries to output error messages to the browser. Without, error messages can only be read in the server's error_log
  • Added: parameter -f (force) enforces recompilation (useful, if you got problems or not enough space to keep all the executables)
  • Added: many new comments, which inform you about what's going on. Please, read them
  • Added: stderr will now be read. So if you use ConsoleError(), this message will be catched and redirected to Apache. Apache writes this message into the error_log of the server
  • Changed: passing over POST-data. Upload not limited to 1 MB for security reasaons (this was a suggestion in 1.0, find the optimal way yourself)
  • Changed: timout for compilation from 20 sec to 5 sec (this is only a suggestion! find your own optimal value)
  • Changed: if possible, the whole error message from the compiler is being returned, not only the first line
  • Changed: Renamed PB_Interpreter_CompilationDuration to PB_Interpreter_CreationTime. Measures the whole progress, not only the compilation duration
  • Changed: new file pattern (unique, but static), so that the interpreter can find a previous compiled exe. Only if you enforce a recompilation with -f, the filename is made up randomly
  • Changed: Be careful with ProgramRunning() (lines 140 and 243). Seems to return 1 always on my linux server
  • Removed: PATH does not need to be set, as the compiler seems to work without it
  • Repaired: program parameters in the shebang-line were passed as one parameter to the PB Interpreter and thus to the executable. Now the interpreter disassembles the program parameter with StringField()
  • Optimized: to read the output of an executable the buffer is not reallocated everytime in the loop any more.
Version 1.0
  • Initial release, source replaced by subsequent versionsin forum




Hello!

Recently, I finished my PB Interpreter. It allows you to store *.pb-files on your webspace, which are compiled everytime a browser calls them. This allows you to create your websites with PureBasic!
Since a PureBasic-installation is required, you must also upload PB to your webspace, so that the interpreter has access to the compiler. Usually, you need a virtual server (vServer) or better. Please, don't store the PB-installation in the same directory as your webspace, but e. g. in your /home-directory, so nobody can download it! You can also use the demoversion, if you are unsure.


In this post, I'll introduce you into version 1.0 of my interpreter. I already finished version 1.1 which comes with much more features, but since I am very new to Linux, I only want to publish 1.1, if there are enough people being interested in this project. It was very hard for me to gather all that required knowledge to get this far, so it should be worth it for me.

The interpreter makes use of CGI (Common Gateway Interface) to interact as a bridge between Apache and your PureBasic-scripts:
  1. You call the website, e. g. my test website http://and51.de/cgi-bin/purebasic/index.cgi
  2. Apache calls the PB Interpreter
  3. The PB Interpreter takes your *.pb-file, compiles it, then runs it
  4. The PB Interpreter reads the output (stdout) of that program and redirects it back to Apache
  5. Apache gives this output to your brwoser
As you can see, it is the same way that a PHP-script would take to be interpreted, but this works with pure PureBasic.


Get Linux-PB if xour server runs Linux/Unix, otherwise Windows-PB, if you've got a windows-server. Extract it and load it 'as it is' to your server, e. g. to /pb/ (this is the path I took).
To get this version 1.0 working, compile the following code and upload it into the uploaded PureBasic-installation. Applay chmod rights 777 or better 755. If it won't work later on, compare the usernames. I uploaded PB under the root-account, but the PB Interpreter will run under "www" or "wwwrun". Don't forget to assign 755 rights to every *.pb-files later on!

You do NOT need to follow the INSTALL file on Linux. The PB Interpreter will set the right environment variables automatically! If you store the interpreter within the /pb-directory (not the /pb/compilers-directory), then it will find the right path, because it uses ProgramFilename().


The code is fully commented. It should be easy to unterstand. You can also try my website written in PB (link above).
I wan to thank these people for their support: DarkDragon (german), stbi (german), walker (english).

Code: Select all

; Copyright: AND51, 2009
; Contact: AND51 at purebasic.fr/english
; E-Mail: purebasic@and51.de
; 		You may use this app, there is no special licence.
; 		Just make me happy by putting me into your credits
; 	Note: We must circumvent a bug in ProgramRunning() (see lines 142 and 233) (only on Linux without debugger).

; inform child processes, such as the PB-scripts, about this programs version
SetEnvironmentVariable("PB_Interpreter_Version", "150")

; to show that we have good manners :-)
EnableExplicit

; for later usage, measures the time needed for whole compilation
Define creation_time=ElapsedMilliseconds()

; open console for apache (this is our stdout)
If Not OpenConsole()
	End
EndIf

; collect program parameters to pass them to the purebasic-executable later on.
; you can define program parameters in the shebang-line. example:
; #!/purebasic/interpreter -param1 -w /fast
; these params are passed to this interpreter and we forward them to the pb-executable.
; some parameters are also relevant for the behaviour of this interpreter.
; on my linux server, all parameters are passed as one parameter, so we only catch param number one!
Define commandline$=RemoveString(RemoveString(ProgramParameter(0), #LF$), #CR$), i, n
NewList parameters.s()
If commandline$
	n=CountString(commandline$, " ")+1
	For i=1 To n
		AddElement(parameters())
			parameters()=StringField(commandline$, i, " ")
	Next
EndIf

Procedure isParameter(parameter$)
	; returns 0 unless the specified parameter was passed to this interpreter
	Shared parameters()
	ForEach parameters()
		If parameters() = parameter$
			ProcedureReturn 1
		EndIf
	Next
EndProcedure
Procedure.s getParameterValue(parameter$)
	; returns the value of a specified parameter. so if there os a param like /number=51,
	; you weill get 51, if you pass "/number" here (colons (:) and equals (=) are valid delimiters)
	Shared parameters()
	Protected length=Len(parameter$)
	ForEach parameters()
		If Left(parameters(), length) = parameter$
			Protected delimiter=FindString(parameters(), ":", 1)
			If Not delimiter
				delimiter=FindString(parameters(), "=", 1)
			EndIf
			If delimiter
				ProcedureReturn Mid(parameters(), delimiter+1)
			EndIf
		EndIf
	Next
EndProcedure
Procedure error(ErrorDescription$, stdout=0)
	; outputs userdefined error to stderr and exits
	; if 'stdout' is true or parameter -w was passed, then write it to stdout, too
	; (including prepending http-header)
	ConsoleError(ErrorDescription$)
	If stdout Or isparameter("-w") Or isParameter("--warn")
		PrintN("Content-Type: text/html"+#LF$)
		PrintN(ErrorDescription$)
	EndIf
	End
EndProcedure
Procedure FindLastString(String$, StringToFind$, CaseInSensitive=0)
	Protected length=Len(StringToFind$), *position=@String$+(Len(String$)-length)<<#PB_Compiler_Unicode
	If StringToFind$
		While *position >= @String$
			If CompareMemoryString(*position, @StringToFind$, CaseInSensitive, length) = #PB_String_Equal
				ProcedureReturn (*position-@String$)>>#PB_Compiler_Unicode+1
			EndIf
			*position-SizeOf(Character)
		Wend
	EndIf
EndProcedure

; apache sets an environment variable, named SCRIPT_FILENAME. this is the pb-file we
; have to compile. if this var is missing, our job is invalid and we can
Define script_filename$=GetEnvironmentVariable("SCRIPT_FILENAME")
If FileSize(script_filename$) = -1
	error("The environmentvariable SCRIPT_FILENAME contains an invalid or no filename: "+script_filename$)
EndIf

; set neccessary environment-variables for the compiler, so you do not need 'export'.
; yes, this interpreter is self-configuring ;-)
; to get this working properly, place this interpreter in the root directory of purebasic (not in the
; 'compilers' directory!)
SetEnvironmentVariable("PUREBASIC_HOME", GetPathPart(ProgramFilename()))
;SetEnvironmentVariable("PATH", GetEnvironmentVariable("PUREBASIC_HOME")+"compilers:"+GetEnvironmentVariable("PATH"))

; generate unique filename for the executable source code in this format:
; %TempDir%/pb-executbale_PathAndFilenameOfSourceWithoutSlash
Define source$, exe$=GetTemporaryDirectory()+"pb-executable_"+RemoveString(script_filename$, "/")

; new compilation system: filenames are not random any more, but still unique (see line 88-90).
; we only have to compile again, if the source code has changed since the last compilation.
; if the executable already exists, we only have to run it. this saves time and disk space: one
; executable can be run by different users. the exe will remain in the temporary directory, however.
; to delete the exe nevertheless after usage, use the -f (f=force recompile) parameter. in this case the
; exe-filename is appended some random numbers to get a unique filename (to avoid conflicts when
; two users create the same exe with the same filename, whereas the user finishing firstly wants
; to delete the executable used by a second user)
If isParameter("-f") Or isParameter("--force")
	exe$+FormatDate("_%yyyy-%mm-%dd_%hh-%ii-%ss_CanBeDeleted_", Date())+Str(Random(9999999))+".tmp"
	SetEnvironmentVariable("PB_Interpreter_ParamForce", "1")
EndIf

; test, if (re-)compilation is neccessary or was enforced
If GetFileDate(script_filename$, #PB_Date_Modified) > GetFileDate(exe$, #PB_Date_Created)
	; generate a filename for the copy of the source code. it mus be unicq also, but will be deleted afterwards
	source$=exe$+".pb"
	
	; in this section we copy the source file to leave the original source untouched. we edit the copy and send it
	; to the compiler. first, we parse the file (if the specified parameters are set), then we comment the shebang-line.
	; important: we need to filter the shebang-line, otherwise this would cause a syntax error.
	; fastest method: copy it and comment the first line. (so the first line is always considered to be non-PB code!)
	; if you know a faster method, please tell me (AND51 @ PB-forums)
	If isParameter("-p") Or isParameter("--parse") ; copying and parsing (if params are set)
		Define script=ReadFile(#PB_Any, script_filename$)
		If script
			Define tempfile=CreateFile(#PB_Any, source$)
			If tempfile
				Define parsing_mode$, n, line$, bom=ReadStringFormat(script)
				WriteStringFormat(tempfile, bom)
				WriteStringN(tempfile, ";"+ReadString(script, bom), bom)
				Define here_start=CreateRegularExpression(#PB_Any, "(?i)<<(|here|include|file|file|multi(|line)|(cr|)lf)$", #PB_RegularExpression_AnyNewLine|#PB_RegularExpression_MultiLine)
				Define here_end=CreateRegularExpression(#PB_Any, "^\s*<<", #PB_RegularExpression_AnyNewLine|#PB_RegularExpression_MultiLine)
				While Not Eof(script)
					line$=ReadString(script, bom)
					If MatchRegularExpression(here_start, line$)
						n=FindLastString(line$, "<<")
						WriteString(tempfile, Left(line$, n-1), bom) ; user should care about #DQUOTE$ by himself
						parsing_mode$=LCase(Mid(line$, n+2))
						Print(">>"+parsing_mode$)
						Select parsing_mode$
							Case "include", "file" ; include an external file and treat it as one string, i. e. replacing (cr)lf's
								While Not Eof(script)
									line$=ReadString(script, bom)
									If MatchRegularExpression(here_end, line$)
										Break
									Else
										Print(">>"+GetPathPart(script_filename$)+Trim(line$))
										Define include=ReadFile(#PB_Any, GetPathPart(script_filename$)+Trim(line$)), temp$
										Print(">>"+Str(include))
										If include
											Define include_bom=ReadStringFormat(include)
											While Not Eof(include)
												WriteString(tempfile, ReadString(include, include_bom), bom)
												If Not Eof(include)
													CompilerIf #PB_Compiler_OS = #PB_OS_Windows
														WriteString(tempfile, #DQUOTE$+"+#CRLF$+"+#DQUOTE$, bom)
													CompilerElse
														WriteString(tempfile, #DQUOTE$+"+#LF$+"+#DQUOTE$, bom)
													CompilerEndIf
												EndIf
											Wend
											CloseFile(include)
										Else
											error("Could not open '"+line$+"' as include file.")
										EndIf
									EndIf
								Wend
							Case "crlf", "lf", "multiline", "multi", "here" ; normal HERE-document (like in perl) with crlf-replacement
								While Not Eof(script)
									line$=ReadString(script, bom)
									If MatchRegularExpression(here_end, line$)
										Break
									Else
										Select parsing_mode$
											Case "crlf"
												WriteString(tempfile, #DQUOTE$+"+#CRLF$+"+#DQUOTE$, bom)
											Case "lf"
												WriteString(tempfile, #DQUOTE$+"+#LF$+"+#DQUOTE$, bom)
											Default
												CompilerIf #PB_Compiler_OS = #PB_OS_Windows
													WriteString(tempfile, #DQUOTE$+"+#CRLF$+"+#DQUOTE$, bom)
												CompilerElse
													WriteString(tempfile, #DQUOTE$+"+#LF$+"+#DQUOTE$, bom)
												CompilerEndIf
										EndSelect
									EndIf
									WriteString(tempfile, line$, bom)
								Wend
							Default
								While Not Eof(script)
									line$=ReadString(script, bom)
									If MatchRegularExpression(here_end, line$)
										Break
									EndIf
									WriteString(tempfile, line$, bom)
								Wend
						EndSelect
						WriteStringN(tempfile, Mid(line$, FindString(line$, "<<", 1)+2), bom) ; #DQUOTE$ user should care for dquotes by himself
					Else
						WriteStringN(tempfile, line$, bom)
					EndIf
				Wend
				CloseFile(script)
				CloseFile(tempfile)
			Else
				error("The source file cannot be created.")
			EndIf
		Else
			error("The source file cannot be read for parsing.")
		EndIf
	Else ; copying only (editing only first line)
		If CopyFile(script_filename$, source$)
			Define tempfile=OpenFile(#PB_Any, source$)
			If tempfile
				WriteString(tempfile, ";", ReadStringFormat(tempfile))
				CloseFile(tempfile)
			Else
				error("Temporary source file cannot be edited.")
			EndIf
		Else
			error("Source file cannot be copied.")
		EndIf
	EndIf
	
	; timeout granted for compilation: 5 seconds (suggestion only!)
	Define compilation_time=ElapsedMilliseconds()
	
	; compile the requested executable to the temp-folder, not to the same directory as the source code.
	; --quiet disables all unneccessary text output; so there will only be text ouput, if a compiler error
	; occured. so if AvailableProgramOutput() is zero, compilation was successful
	Define compiler=RunProgram(GetPathPart(ProgramFilename())+"compilers/pbcompiler", "--quiet --executable "+#DQUOTE$+exe$+#DQUOTE$+" "+#DQUOTE$+source$+#DQUOTE$, "", #PB_Program_Open|#PB_Program_Read)
	If Not compiler
		error("Compiler could not be started.")
	EndIf
	
	; waiting if neccessary and catch the compilers output; if output > 0, then there is an error.
	; we assume this, because of the --quiet flag (see line 125-128).
	; kill the compiler and abort the whole procress, if timeout reached
	WaitProgram(compiler, 5000)
	Define compilerOutput=AvailableProgramOutput(compiler)
	
	If compilerOutput Or FileSize(exe$) = -1 Or ElapsedMilliseconds() > compilation_time+5000 ;Or ProgramRunning(compiler) 
		Define compiler_error$, *compiler=AllocateMemory(compilerOutput)
		If *compiler
			ReadProgramData(compiler, *compiler, compilerOutput)
			compiler_error$=PeekS(*compiler)
		Else
			compiler_error$=ReadProgramString(compiler)+" (this was probably not the whole compiler output due to technical reasons)"
		EndIf
		CloseProgram(compiler)
		KillProgram(compiler)
		error("Compilation process did not finish correctly. Error: "+#DQUOTE$+compiler_error$+#DQUOTE$+".")
	EndIf
	
	; new: compress the executable with UPX when you specify the parameter -c. as this step is optional, interpreter
	; will continue in any case, even if the executable was not compressed (for whatever reason). furthermore, files
	; seem to not be compressed, when they are too small anyway. we will use UPX's standard settings, as they
	; offer a good balance between speed and compression ratio
	If isParameter("-c") Or isParameter("--compress")
		If Not RunProgram(GetPathPart(ProgramFilename())+"upx", "-qqq "+#DQUOTE$+exe$+#DQUOTE$, "", #PB_Program_Wait)
			ConsoleError("Something went wrong when compressing executable with UPX. Maybe the executable remains uncompressed. Resuming progress!")
		EndIf
	EndIf
EndIf

; for convenience, we provide all QUERY_STRING-parameters as environment variables for the program:
; the URI "www.and51.de/index.pb?site=downloads&sort=asc" would create the environment variables
; "GET_site" with the value "downloads" and "GET_sort" with "asc". then you can easily catch them with the
; GetEnvironmentVariable() command. caution: you must decode them manually if neccessary with URLDecoder()
If GetEnvironmentVariable("QUERY_STRING") And GetEnvironmentVariable("REQUEST_URI") ; check first param, but extract informations from second param
	Define query_exp=CreateRegularExpression(#PB_Any, "(?U)(?<=&).+(?==)")
	Dim querystrings.s(0)
	Define i, n=ExtractRegularExpression(query_exp, "&"+GetEnvironmentVariable("QUERY_STRING"), querystrings())
	For i=1 To n
		SetEnvironmentVariable("GET_"+querystrings(i-1), GetURLPart(GetEnvironmentVariable("REQUEST_URI"), querystrings(i-1)))
	Next
	SetEnvironmentVariable("PB_Interpreter_QueryStrings", Str(n))
EndIf

; set some additional evironment variables to inform our later purebasic-executable about...
SetEnvironmentVariable("PB_Interpreter_ProgramFilename", ProgramFilename())
SetEnvironmentVariable("PB_Interpreter_Date", Str(Date()))
SetEnvironmentVariable("PB_Interpreter_PBVersion", Str(#PB_Compiler_Version))
If isParameter("-c") Or isParameter("--compress")
	SetEnvironmentVariable("PB_Interpreter_ParamCompress", "1")
EndIf
If isParameter("-w") Or isParameter("--warn")
	SetEnvironmentVariable("PB_Interpreter_ParamWarn", "1")
EndIf
If isParameter("-p") Or isParameter("--parse")
	SetEnvironmentVariable("PB_Interpreter_ParamParse", "1")
EndIf
SetEnvironmentVariable("PB_Interpreter_CreationTime", Str(ElapsedMilliseconds()-creation_time))

; now run our purebasic-executable. it will have all the environmentvariables that this process has.
; if we have POST-data, we will write it to the executable's input (stdin). therefore, the executable
; must open a console with OpenConsole() and read it with ReadConsoleData(). the executable
; can check, if there is POST-data: just call GetEnvironmentVariable("CONTENT_LENGTH"). this is
; the size of POST-data in bytes. if this env-var is non-existent or null, there is no data and the
; executable does not need to read anything; remember, a reading action with Input() or ReadConsoleData()
; blocks your program until an EOF has been written to the stdin. so avoid reading, if you can.
; we will also pass the ProgramParameter()'s, as described in line 19-30
; note 1: we will make use of WorkingDirectory$, so that the executable thinks, it would run in the same
; directory as the source code. so you get the feeling that your website only consists of .pb-files :-)
; Remember, the purebasic executable can read and set the directory with GetCurrentDirectory() and
; SetCurrentDirectory()
; note 2: the executable can also read its real directory with ProgramFilename()
; note 3: the executable is responsible for correct interaction between itself and the browser! therefore
; it needs to create a correct HTTP-header before generating any output. if you don't know what
; you must do, place these two lines at the top of your program:
; 		PrintN("Content-Type: text/html")
; 		PrintN("")
; note 4: all the executables output for the browser can be done with PrintN() and WriteConsoleData().
; note 5: remember all the env-vars, that we set for the executable. use them, if you need their informations
; note 6: the recommended way to create a "purebasic-made website" is this order:
;     1) OpenConsole()
;     2) read POST-Data, if neccessary
;     3) follow note 3
;     4) generate your output like in note 4
; note 7: try to avoid long breaks. my experience is, that when you have too long output-pauses,
; apache will simply dismiss your executable
; note 8: this interpreter now catches stderr, too. that means, you can write to stderr by using ConsoleError().
; text written to stderr should appear in the server's error_log!
Define program=RunProgram(exe$, commandline$, GetPathPart(script_filename$), #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error)
If program
	; pass over POST-data, if existent
	Define content_length=Val(GetEnvironmentVariable("CONTENT_LENGTH"))
	If content_length
		Define *buffer=AllocateMemory(content_length)
		If *buffer
			ReadConsoleData(*buffer, content_length)
			WriteProgramData(program, *buffer, content_length)
		Else
			ConsoleError("Warning: POST-data cannot be passed to executable's stdin due to insufficient memory allocation.")
		EndIf
		WriteProgramData(program, #PB_Program_Eof, 0) ; not neccessary when using CloseProgram(), but then reading doesnt work either
	EndIf
	
	Define available, size, *output=AllocateMemory(1000), stderr$
	If Not *output
		CloseProgram(program)
		KillProgram(program)
		error("Cannot handle executable's output due to insufficient memory allocation.")
	EndIf
	While ProgramRunning(program)
		; handle exe's stderr (stderr might appaer in the webserver's error_log!)
		stderr$=ReadProgramError(program)
		If stderr$
			ConsoleError(stderr$)
		EndIf
		
		; handle exe's stdout
		available=AvailableProgramOutput(program)
		If available
			If available > 1000 ; ensure reading in steps of 1000 bytes only, so we don't need a large buffer
				available=1000
			EndIf
			size=ReadProgramData(program, *output, available)
			; redirecting programs output directly to apache by writing it to OUR console (our stdout)
			WriteConsoleData(*output, size)
			Continue ; speeds up the data redirection by skipping the Delay() below, if there is a continuous data flow
		EndIf
		Delay(5) ; do not consume the whole cpu time. find the optimal value yourself and let me know, please
		
		; circumvent a bug in ProgramRunning() (this command always returns 1),
		; see http://www.purebasic.fr/english/viewtopic.php?t=37273
		If ProgramExitCode(program) <> -1 : Break : EndIf
	Wend
Else
	error("The compiled program cannot be started: "+exe$)
EndIf

; the program execution has finished. now we delete the exe$ and the temp source file if enforced
; (see line 91-98)
If isParameter("-f") Or isParameter("--force")
	DeleteFile(exe$)
EndIf
If source$
	DeleteFile(source$)
EndIf

; keep in mind that this interpreter can do any stuff here, e. g. append advertisement, increase any counters, etc.

; we do not need to tidy up the rest (free buffers and other things), because PB does this
; automatically on program exit. the console will also be closed automatically
End
Don't forget to compile without debugger!


This is a simple Hello-World-program stored as *.pb-file:

Code: Select all

#!/pb/interpreter

OpenConsole()
PrintN("Content-Type: text/html")
PrintN("")
PrintN("Hello from Purebasic 4.30 on your HTTP WebServer!")
The first line is the Shebang-line hat makes Apache calling the PB Interpreter, but the rest is 100% PureBasic Syntax!



———————————————————————————————————————


I will continue working on this project following my private interests. But if enough people are interested in this, I will publish new versions, so that you can use this, too!

I would like to tell you about version 1.1 now, that I've already written, because it has got significant updates compared to 1.0.

A list of features of 1.0 and 1.1 (in the following, I call a compiled executable 'script'):
  • write dynamic contents in PureBasic instead of PHP or Perl, e. g. guestbooks, forums, counter, upload/download-services, browsergames, etc.
  • define your own program parameters, read environment variables
  • your script can also write into the servers error_log (1.1 only)
  • compilation only, if the sourcecode is newer than the previous compiled executbale (1.1 only) (the most important issue, read more later on)
  • you can enforce a re-compilation with -f (force) prameter (1.1 only)
  • with parameter -w (warn) errors will be also displayed in your browser (1.1) and not only in the server's error_log (1.0), so you can trace errors more easily
  • with -c (compress) you can compress the executable with UPX (if installed)
  • the interpreter provides your executable the following information via environment variables (extendable): current date (same as if you use Date()), duration of the compilation precess in milliseconds, all environment variables of the webserver (QUERY_STRING, REMOTE_ADDR, etc.), POST-uploads (data uploaded via POST-methods, e. g. formulars)
  • PB Interpreter sets the current directory the same as your script, so if you call CreateFile(), this file will be created in the same directory as your script is in (the second essential idea of this project)
  • the interpreter deletes its 2 temp files automatically and is self-configurable: you do not need to use "export" as described in the install-file of Linux-PB, in order to set the env vars PATH and PUREBASIC_HOME
  • no external dependancies (like DLLs or INIs) neccessary
  • also works with the demo-version of PureBasic
  • with small interventions in the httpd.conf, it is also possible, to let Apache recognize the extention .pb. By default, it only recognizes .cgi-extension
  • the PB Interpreter should give you the feeling, that your whole website only consists of .pb-files. You can admnistrate and edit your source files with every editor, no need to fumble around like this or this (@ linked threads: I mean no harm)
Going through step 1 to 5 as written above takes about 150 - 280 milliseconds, because a *.pb-file will be compiled every time a browser calls it. That's what I changed in version 1.1. The PB Interpreter will keep every executable in the temp-directory of the system. Hence, recompiling is only neccessary, if the source file was modified since the last compilation. This decreases the generation time significantly. Now it only takes 0 - 1 (one) millisecond on my server, to answer a browser request. This fact makes my PB Interpreter able to compete with PHP and Perl. I would even suggest, that PB is faster than PHP or Perl.
The reason is that a PHP script must be prepared by the PHP interpreter every time: include includes, check syntax, interprete the script and evaluate it. With PB, the syntax and the includes have to be prepared only once. Once, the *.pb-file was compiled, the executable only needs to be executed everytime a browser requests it.



Well, so far I hope that I could give you a good overview about my project, PB Interpreter. You may use it freely on your own webserver. It should be very easy to use it also on Windows, because you only need to replace frontslashes / by backslashes \. Please, tell me, if you are interested in this project. If yes, I will release my version 1.1. :D
Last edited by AND51 on Mon Jul 27, 2009 5:27 am, edited 6 times in total.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

You shouldn't call it "interpreter". This is misleading.
quidquid Latine dictum sit altum videtur
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Yeah, I was told about it in the german forum already.
Sorry.
In fact, it is not a interpreter, you're right. But didn't find a better name. I don't want to call it "PB Precompiler for Apache", "PB Fake Interpreter" or something like that. I hope, you understand. Excuse me, I forgot to mention this in my first post.

If someone can tell me a much better name, I will consider renaming it. :)
PB 4.30

Code: Select all

onErrorGoto(?Fred)
User avatar
J. Baker
Addict
Addict
Posts: 2181
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Post by J. Baker »

Wow, you mentioned browser games. What's the limits on this and can you post an example? Nice work by the way! ;)
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef


Even the vine knows it surroundings but the man with eyes does not.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

Thanks for sharing this (whatever the name :)

Not knowing much about perl or php, this could be very nice :wink:

cheers
Last edited by rsts on Mon May 11, 2009 4:49 am, edited 1 time in total.
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

you mentioned browser games
Well with PureBasic you can do the same things like with PHP or Perl. If you're lucky, you can even use the Database-commands, so that you can access MySQL or SQL databases on your server.

I never worked with databases, so I cannot say if it works. Just try it. If something doesn't work, e. g. the compilation fails and you don't see any error (even if you use -w parameter in version 1.1), then read your error_log; there will be listed every error.
What's the limits on this and can you post an example?
Well, you cannot use every command. For example, you cannot use Sprites, Screens and maybe Sounds. This is, because a virtual server does not have a (good) graphics or sound card. Furthemore, you should ask your provider, if there are limits, e. g. how much RAM do you may use, etc.
When I just made this test Repeat : Forever, the app was killed automatically. :shock: My provider told me that applications consuming too much ressources are killed, because I only own a virtual server. That means I share it with 2-4 other customers and I should not steal all of their ressources. So try to avoid long pauses (long Delay()'s) and consuming too much CPU or RAM).
If you've got a dedicated server or using your own PC, you can use as much ressources as you want.

I just coded this example:

Code: Select all

#!/pb/interpreter -w

OpenConsole()
PrintN("Content-Type: text/html"+#LF$)

If OpenFile(0, "hitcounter.txt")
	hits=ReadLong(0)+1
	FileSeek(0, 0)
	WriteLong(0, hits)
Else
	Print("hitcounter.txt cannot be opened")
EndIf

Print("This page was accessed "+Str(hits)+" times.")
You can see it here:
http://www.and51.de/hits.pb

This is just an example of a HTML-page with dynamic content. And if you think further, you can also create counters -> guestbooks -> searches -> forums -> browsergames. :)
Note knowing much about perl or php, this could be very nice
Did you mean "not"? So if you don't have knowledge about PHP or Perl, who cares? From now on, you can also create dynamic sites using a very familiar programming language... PureBasic! :D


So are you interested in this? If you want to test this, I can publish version 1.1 inclusive source code, tomorrow.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
User avatar
J. Baker
Addict
Addict
Posts: 2181
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Post by J. Baker »

Cool, thanks a lot AND51! And thanks for posting another example. ;)
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef


Even the vine knows it surroundings but the man with eyes does not.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

AND51 wrote:
Note knowing much about perl or php, this could be very nice
Did you mean "not"? So if you don't have knowledge about PHP or Perl, who cares? From now on, you can also create dynamic sites using a very familiar programming language... PureBasic! :D


So are you interested in this? If you want to test this, I can publish version 1.1 inclusive source code, tomorrow.
Yes, I meant "not" :oops: and yes I'm interested in learning more, although I will have to text it on my PC server since my web-hosting is a shared service plan on which I can not run this service :(

Thanks again for the code and examples.

cheers
Seymour Clufley
Addict
Addict
Posts: 1264
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

This is very, very interesting.

Unfortunately I can't test it because I don't have a server just now, but please post v1.1.
walker
Enthusiast
Enthusiast
Posts: 634
Joined: Wed May 05, 2004 4:04 pm
Location: Germany

Post by walker »

.. I definitely will use it .. :D at least for testing on my local apache server.... to find out the capabilities of this (nameless :wink: ) very cool program
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Yes, damn interesting!

I shall contact my hosting service to see if I can upload the PB compiler (Windows host) and if the necessary permissions are given to execute .exe's on the server etc.

Must admit that I am a little confused about the installation instructions?

I compile your code to "interpreter.exe" and place it in my /pb installation folder - is that correct?

Thanks for this; much appreciated.
I may look like a mule, but I'm not a complete ass.
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post by djes »

It could be very *hot* stuff. A really good compiled language as PB for sites dev, isn't it pretty? However, have to be rock solid for security reasons, checking all input variables, and so on.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

This has inspired me to write my first cgi program with PB - works great! I'm not using AND51's tool as yet as I am still unsure whether I can upload the PB installation safely?

Actually I didn't think my hosting service would allow exe's to run in my cgi-bin folder; quite a surprise when it worked! I thought that was just for Perl scripts!

This opens up some nice possibilities! :) Though I know others have been doing this for years!

@AND51 : have you checked with Fred that our PB license allows the compiler to be used in this way? I ask because, well, uploading the whole installation to a server does seem a bit, well, above and beyond the stipulations of a 'regular' license! :)
I may look like a mule, but I'm not a complete ass.
milan1612
Addict
Addict
Posts: 894
Joined: Thu Apr 05, 2007 12:15 am
Location: Nuremberg, Germany
Contact:

Post by milan1612 »

srod wrote:Actually I didn't think my hosting service would allow exe's to run in my cgi-bin folder; quite a surprise when it worked! I thought that was just for Perl scripts!
The problem I see is that most of the hosting service providers use Linux on there servers,
I can't imagine you can do this with Linux binaries. Does anyone know if it's possible?
Windows 7 & PureBasic 4.4
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Post by DoubleDutch »

What happens if multiple people access the site at the same time? I mean while the compiler is busy compiling - will it not mess the system up?

If there are no problems, why should you keep compiling the source - why not just execute the compiled version?
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
Post Reply