Ticket #2440: php.py

File php.py, 3.6 KB (added by django@…, 18 years ago)

custom php template loader. Add to TEMPLATE_LOADERS

Line 
1from django.conf import settings
2from django.template import TemplateDoesNotExist
3from django.template.loader import find_template_source
4from django.core.cache import cache
5from subprocess import Popen, PIPE
6import sys, os, threading
7
8def _getdefault(name, default=None):
9 try:
10 default = getattr(settings, name)
11 except: pass
12 return default
13
14## these should be set in the settings.py file
15PHP_BIN = _getdefault('PHP_BIN', '/usr/bin/php')
16PHP_ARGS = _getdefault('PHP_ARGS', ['-q',])
17PHP_IN_SHELL = _getdefault('PHP_IN_SHELL', True)
18PHP_THREAD_IO = _getdefault('PHP_THREAD_IO', sys.platform == 'win32')
19PHP_BUFFER_SIZE = _getdefault('PHP_BUFFER_SIZE',
20 sys.platform == 'win32' and -1 or 4096)
21PHP_CACHE_SECONDS = _getdefault('PHP_CACHE_SECONDS', None)
22
23class PipeThread(threading.Thread):
24 """This is needed for Win32 where the buffer can lock if you
25 pass the windows shell buffer size and do not read the output pending
26 from the running app"""
27 def __init__(self, fin, mode='read'):
28 self.fin = fin
29 self.sout = ""
30 self.mode = mode
31 threading.Thread.__init__(self)
32 def run(self):
33 if self.mode == 'read':
34 self.sout = self.fin.read()
35 else:
36 self.fin.write(self.sout)
37 def read(self):
38 return self.sout
39 def write(self, data):
40 self.sout = data
41 def close(self):
42 ## WARNING! This can cause a crash on windows depending on the FD
43 ## If its an stdout or stderr FD then this will crash
44 if self.mode != 'read':
45 self.fin.close()
46
47def runphp(source):
48 php = Popen([PHP_BIN,] + PHP_ARGS, shell=PHP_IN_SHELL,
49 bufsize=1<<12, universal_newlines=True, ## restriction of templates
50 stdin=PIPE, stdout=PIPE, #stderr=PIPE,
51 close_fds= sys.platform != 'win32' and True or False)
52
53 ## RED_FLAG: check for returncode after reading stdout and if its an error
54 ## (and we are TEMPLATE_DEBUG), then raise a special error with
55 ## the contents of stderr... wee...
56 if not PHP_THREAD_IO:
57 php.stdin.write(source)
58 php.stdin.close() # or we block on stdout.read()
59 page = php.stdout.read()
60 php.stdout.close() # just in case we are windows
61 else:
62 phpin = PipeThread(php.stdin, 'write')
63 phpin.write(source)
64 phpin.start()
65 phpout = PipeThread(php.stdout)
66 phpout.start()
67 phpin.close()
68 retcode = php.wait()
69 phpout.join(1)
70 page = phpout.read()
71 return page
72
73def load_template_source(template_name, template_dirs=None):
74 """all php template requests must start with 'php:' to keep namespaces
75 distinct and as an added security measure
76 (and it would be recursive otherwise)"""
77 if len(template_name) <= 4 or template_name[:4] != 'php:':
78 raise TemplateDoesNotExist, (
79 "Not a PHP template request: %s" % template_name)
80 if PHP_CACHE_SECONDS:
81 page_and_origin_name = cache.get(template_name)
82 if page_and_origin_name: return page_and_origin_name
83 try:
84 source, origin = find_template_source(template_name[4:], template_dirs)
85 except TemplateDoesNotExist:
86 raise TemplateDoesNotExist, template_name
87
88 page = runphp(source)
89
90 page_and_origin_name = (page, origin and origin.name or template_name)
91 if PHP_CACHE_SECONDS:
92 cache.set(template_name, page_and_origin_name, PHP_CACHE_SECONDS)
93 return page_and_origin_name
94load_template_source.is_usable=True
Back to Top