afiqiqmal/malaysiaholiday
A Laravel package for retrieving Malaysian public holidays. Look up holiday dates by state and year, with a simple API for querying and integrating holiday data into your PHP applications.
Architecture fit. This is a data-provisioning package for Malaysian public and school holidays — most likely a service provider plus facade over a bundled/derived dataset with caching. It fits any Laravel application with calendar-sensitive business logic: payroll and statutory holiday pay, leave and attendance systems, SLA/response-time clocks, logistics cutoffs, billing-cycle shifting, and scheduled-job gating. It is not infrastructure-critical, but it is correctness-critical: a wrong answer silently corrupts downstream calculations rather than throwing.
Integration feasibility. Straightforward. Composer install, package discovery, one facade call. The 4.2.1 change is additive — a new source key plus a fallback data path — so existing consumers reading name/date are unaffected. The only integration hazard is consumers doing strict shape assertions (DTO constructors, assertSame on arrays, JSON-schema validation, snapshot tests) which will fail on the extra key. That is a mechanical fix, not a design problem.
Technical risk. This release meaningfully widens the risk surface, and that is the headline. Previously the package leaned on a single structured-ish upstream; 4.2.1 introduces a second upstream (calendarmalaysia.com) for 2025 onwards, which in practice means HTML parsing of a third-party site we do not control. Concretely: (1) the two sources will disagree at the 2024/2025 boundary, producing visible discontinuities; (2) the Malay state-note parsing (sahaja = "only", kecuali = "except") is semantically load-bearing — a misparse flips a state-restricted holiday into a national one or vice versa, which is exactly the failure mode that breaks payroll; (3) whether the fallback is a build-time fetch or a runtime HTTP call is the single most important unknown, since runtime egress to a scraped site is both a latency and an availability dependency.
Key questions.
source always populated, or nullable for older/bundled data? What are its enumerated values, and is it stable enough to key on?sahaja/kecuali notes covered by unit tests with fixtures per state, or is the parsing heuristic untested against real page variants?Stack fit. PHP 8.x, Laravel 9–11, standard Composer delivery. No infrastructure prerequisites. Fits cleanly alongside an existing CalendarService or scheduling layer.
Migration path. Greenfield — there is no prior assessment and no incumbent. Treat this as a new dependency and adopt it behind a seam rather than calling the facade directly from business logic:
HolidayCalendar interface (isHoliday(CarbonInterface $date, ?string $state): bool, plus a source-carrying DTO).holidays table with date, name, states, is_observed, source, fetched_at, package_version. Business logic reads our table; the package is a seeder.This decouples correctness-critical reads from a scraping dependency and gives us an audit trail when a number is questioned.
Compatibility. Pin ^4.2. Audit tests and API contracts for strict array-shape assumptions before bumping. Anything serialising the package's raw array to an external consumer now leaks source — decide deliberately whether to expose or strip it.
Sequencing.
sahaja/kecuali entries.source and fetched_at.Maintenance. Double the parsing surface. Two upstreams with different structures and different editorial conventions means a bug can live in either path, and the newer path is HTML-scraped — the most brittle class of integration. Budget a standing allowance rather than a one-off: realistically one to two breakage events per year, each needing a same-day patch because the alternative is silently wrong holiday data.
Support. The source key is the most operationally valuable thing in this release — it converts "the system says 3 May is a holiday" into "the system says 3 May is a holiday and here is which upstream told us so." Surface it in internal admin tooling and in any user-facing discrepancy workflow. Without it, every data dispute starts with a bisect.
Scaling. Read volume is trivially cacheable — this is a handful of rows per year. The real scaling concern is a cache stampede on cold start if the fallback triggers live HTTP: one expired cache plus a burst of requests becomes a burst of requests to a third-party site, which invites rate-limiting precisely when you can least afford it. Mitigate with a warm-up job, a lock around refresh, and negative-result caching.
Failure modes (ranked by blast radius).
sahaja/kecuali misread, promoting a state-only holiday to national. Plausible, hard to notice, and produces confidently wrong output.Ramp-up. Low for consumption — a developer can call it in an afternoon. Medium-to-high for ownership: whoever maintains this needs to read Malay, understand the distinction between federal and state observance, know why Kelantan and Terengganu differ on weekends, and be able to judge whether a source is wrong or merely unfamiliar. That is a single-person knowledge concentration. Document the state-note semantics and the 2025 source boundary in the repo, and name a secondary owner.
How can I help you explore Laravel packages today?