Brad writes:
I recently changed ljlib.pl's header from: require "$ENV{'LJHOME'}/cgi-bin/ljconfig.pl"; require "$ENV{'LJHOME'}/cgi-bin/ljdefaults.pl"; to: # determine how we're going to send mail BEGIN { do "$ENV{'LJHOME'}/cgi-bin/ljconfig.pl"; do "$ENV{'LJHOME'}/cgi-bin/ljdefaults.pl"; $LJ::OPTMOD_NETSMTP = eval "use Net::SMTP (); 1;"; if ($LJ::SMTP_SERVER) { die "Net::SMTP not installed\n" unless $LJ::OPTMOD_NETSMTP; MIME::Lite->send('smtp', $LJ::SMTP_SERVER, Timeout => 10); } else { MIME::Lite->send('sendmail', $LJ::SENDMAIL); } } but now, hooks are loaded in a web context (from BML, etc), but not from command line programs which just source ljlib.pl, like synsuck, so synsuck is segfaulting on synsuck, just like deadjournal. do I not understand perl's BEGIN blocks and do statements? what's invalidating %LJ::HOOKS? :-/
Avva replies:
I toyed around with this a bit... seems LJ::HOOKS is never filled in. When you "do ljconfig.pl" in BEGIN, eventually it gets to ljcom.pl and its code. ljcom.pl gets compiled fine, but its execution (all its initialisation statements) fails, so it never gets to define the hooks. The reason they fail is that ljlib's BEGIN is executed before the rest of ljlib.pl is even parsed, much less compiled. Inside that BEGIN, all LJ:: functions haven't been defined yet. The rest of the stuff works because ljconfig.pl and ljdefaults.pl are just a bunch of assignments, they don't use LJ:: functions. Why this works in web context? More complicated. The difference is that in web context, everything begins with ljconfig.pl (require'd in modperl.pl). Then it goes ljconfig.pl->ljconfig-local.pl->ljcom.pl->paylib.pl->ljlib.pl , and the whole of ljlib.pl is compiled (while its invokation of ljconfig.pl in BEGIN doesn't get to ljcom.pl the second time because require's are forced by Perl to be non-reentrant, and while ljlib.pl doesn't require ljconfig.pl, it do'es it, ljconfig.pl require's ljconfig-local.pl ); after that execution continues in ljcom.pl and everything goes through because ljlib.pl has been compiled. So it only works by accident, because of this incidental ljcom.pl->paylib.pl->ljlib.pl connection. It's pretty silly that on ljcom installations, require'ing ljconfig.pl pulls in ljlib.pl, but on livejournal installations, that's not the case. This probably bits people on their asses every now and then.
And then he replies again later....
Really, when you think about it, ljconfig-local.pl is not the right place to pull in ljcom.pl and other site-specific libraries. Rather, this should be done in a new ljlib-local.pl (and we don't need ljconfig-local.pl at all then, since ljconfig.pl is supposed to be customised directly anyway). Here's how it should be working, I think: a) no ljconfig-local.pl . This means that require'ing ljconfig.pl doesn't execute anything heavy (as it does now), but only defines lots of constants from ljconfig.pl and ljdefaults.pl . b) ljlib-local.pl comes from ljcom CVS, require's ljcom.pl . Perhaps it should also require paylib.pl separately, rather than from ljcom.pl, not sure if it matters. c) Now you can "do" ljconfig.pl,ljdefaults.pl in ljlib.pl in a BEGIN block without problems. d) in ljlib.pl at the beginning, you require ljlib-local.pl if there is one; or if you prefer, you "do" it in a BEGIN block, but put it at the end of ljlib.pl, that'll work too because LJ:: functions have been compiled by then and ljcom's initialisation will work. Thoughts?